Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / feature / hs / hs_metrics.c
blobe023eab90c1dae337230adba4647f2c41aac85aa
1 /* Copyright (c) 2020-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * @file hs_metrics.c
6 * @brief Onion service metrics exposed through the MetricsPort
7 **/
9 #define HS_METRICS_ENTRY_PRIVATE
11 #include "orconfig.h"
13 #include "lib/malloc/malloc.h"
14 #include "lib/container/smartlist.h"
15 #include "lib/metrics/metrics_store.h"
17 #include "feature/hs/hs_metrics.h"
18 #include "feature/hs/hs_metrics_entry.h"
19 #include "feature/hs/hs_service.h"
21 /** Return a static buffer pointer that contains the port as a string.
23 * Subsequent call to this function invalidates the previous buffer. */
24 static const char *
25 port_to_str(const uint16_t port)
27 static char buf[8];
28 tor_snprintf(buf, sizeof(buf), "%u", port);
29 return buf;
32 /** Return a static buffer pointer that contains a formatted label on the form
33 * of key=value.
35 * Subsequent call to this function invalidates the previous buffer. */
36 static const char *
37 format_label(const char *key, const char *value)
39 static char buf[128];
40 tor_snprintf(buf, sizeof(buf), "%s=%s", key, value);
41 return buf;
44 /** Initialize a metrics store for the given service.
46 * Essentially, this goes over the base_metrics array and adds them all to the
47 * store set with their label(s) if any. */
48 static void
49 init_store(hs_service_t *service)
51 metrics_store_t *store;
53 tor_assert(service);
55 store = service->metrics.store;
57 for (size_t i = 0; i < base_metrics_size; ++i) {
58 metrics_store_entry_t *entry =
59 metrics_store_add(store, base_metrics[i].type, base_metrics[i].name,
60 base_metrics[i].help);
62 /* Add labels to the entry. */
63 metrics_store_entry_add_label(entry,
64 format_label("onion", service->onion_address));
65 if (base_metrics[i].port_as_label && service->config.ports) {
66 SMARTLIST_FOREACH_BEGIN(service->config.ports,
67 const hs_port_config_t *, p) {
68 metrics_store_entry_add_label(entry,
69 format_label("port", port_to_str(p->virtual_port)));
70 } SMARTLIST_FOREACH_END(p);
75 /** Update the metrics key entry in the store in the given service. The port,
76 * if non 0, is used to find the correct metrics entry. The value n is the
77 * value used to update the entry. */
78 void
79 hs_metrics_update_by_service(const hs_metrics_key_t key,
80 hs_service_t *service, const uint16_t port,
81 int64_t n)
83 tor_assert(service);
85 /* Get the metrics entry in the store. */
86 smartlist_t *entries = metrics_store_get_all(service->metrics.store,
87 base_metrics[key].name);
88 if (BUG(!entries)) {
89 return;
92 /* We need to find the right metrics entry by finding the port label if any.
94 * XXX: This is not the most optimal due to the string format. Maybe at some
95 * point turn this into a kvline and a map in a metric entry? */
96 SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) {
97 if (port == 0 ||
98 metrics_store_entry_has_label(entry,
99 format_label("port", port_to_str(port)))) {
100 metrics_store_entry_update(entry, n);
101 break;
103 } SMARTLIST_FOREACH_END(entry);
106 /** Update the metrics key entry in the store of a service identified by the
107 * given identity public key. The port, if non 0, is used to find the correct
108 * metrics entry. The value n is the value used to update the entry.
110 * This is used by callsite that have access to the key but not the service
111 * object so an extra lookup is done to find the service. */
112 void
113 hs_metrics_update_by_ident(const hs_metrics_key_t key,
114 const ed25519_public_key_t *ident_pk,
115 const uint16_t port, int64_t n)
117 hs_service_t *service;
119 tor_assert(ident_pk);
121 service = hs_service_find(ident_pk);
122 if (!service) {
123 /* This is possible because an onion service client can end up here due to
124 * having an identity key onto a connection _to_ an onion service. We
125 * can't differentiate that from an actual onion service initiated by a
126 * service and thus the only way to know is to lookup the service. */
127 return;
129 hs_metrics_update_by_service(key, service, port, n);
132 /** Return a list of all the onion service metrics stores. This is the
133 * function attached to the .get_metrics() member of the subsys_t. */
134 const smartlist_t *
135 hs_metrics_get_stores(void)
137 /* We can't have the caller to free the returned list so keep it static,
138 * simply update it. */
139 static smartlist_t *stores_list = NULL;
141 smartlist_free(stores_list);
142 stores_list = hs_service_get_metrics_stores();
143 return stores_list;
146 /** Initialize the metrics store in the given service. */
147 void
148 hs_metrics_service_init(hs_service_t *service)
150 tor_assert(service);
152 /* This function is called when we register a service and so it could either
153 * be a new service or a service that was just reloaded through a HUP signal
154 * for instance. Thus, it is possible that the service has already an
155 * initialized store. If so, just return. */
156 if (service->metrics.store) {
157 return;
160 service->metrics.store = metrics_store_new();
161 init_store(service);
164 /** Free the metrics store in the given service. */
165 void
166 hs_metrics_service_free(hs_service_t *service)
168 tor_assert(service);
170 metrics_store_free(service->metrics.store);