hs: Fix multiple port label on single metric
[tor.git] / src / feature / hs / hs_metrics.c
blobe80d98c2ddcd58b967b935ff249f3e161130fa56
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 /** Initialize a metrics store for the given service.
34 * Essentially, this goes over the base_metrics array and adds them all to the
35 * store set with their label(s) if any. */
36 static void
37 init_store(hs_service_t *service)
39 metrics_store_t *store;
41 tor_assert(service);
43 store = service->metrics.store;
45 for (size_t i = 0; i < base_metrics_size; ++i) {
46 /* Add entries with port as label. We need one metric line per port. */
47 if (base_metrics[i].port_as_label && service->config.ports) {
48 SMARTLIST_FOREACH_BEGIN(service->config.ports,
49 const hs_port_config_t *, p) {
50 metrics_store_entry_t *entry =
51 metrics_store_add(store, base_metrics[i].type, base_metrics[i].name,
52 base_metrics[i].help);
54 /* Add labels to the entry. */
55 metrics_store_entry_add_label(entry,
56 metrics_format_label("onion", service->onion_address));
57 metrics_store_entry_add_label(entry,
58 metrics_format_label("port", port_to_str(p->virtual_port)));
59 } SMARTLIST_FOREACH_END(p);
60 } else {
61 metrics_store_entry_t *entry =
62 metrics_store_add(store, base_metrics[i].type, base_metrics[i].name,
63 base_metrics[i].help);
64 metrics_store_entry_add_label(entry,
65 metrics_format_label("onion", service->onion_address));
70 /** Update the metrics key entry in the store in the given service. The port,
71 * if non 0, is used to find the correct metrics entry. The value n is the
72 * value used to update the entry. */
73 void
74 hs_metrics_update_by_service(const hs_metrics_key_t key,
75 hs_service_t *service, const uint16_t port,
76 int64_t n)
78 tor_assert(service);
80 /* Get the metrics entry in the store. */
81 smartlist_t *entries = metrics_store_get_all(service->metrics.store,
82 base_metrics[key].name);
83 if (BUG(!entries)) {
84 return;
87 /* We need to find the right metrics entry by finding the port label if any.
89 * XXX: This is not the most optimal due to the string format. Maybe at some
90 * point turn this into a kvline and a map in a metric entry? */
91 SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) {
92 if (port == 0 ||
93 metrics_store_entry_has_label(entry,
94 metrics_format_label("port", port_to_str(port)))) {
95 metrics_store_entry_update(entry, n);
96 break;
98 } SMARTLIST_FOREACH_END(entry);
101 /** Update the metrics key entry in the store of a service identified by the
102 * given identity public key. The port, if non 0, is used to find the correct
103 * metrics entry. The value n is the value used to update the entry.
105 * This is used by callsite that have access to the key but not the service
106 * object so an extra lookup is done to find the service. */
107 void
108 hs_metrics_update_by_ident(const hs_metrics_key_t key,
109 const ed25519_public_key_t *ident_pk,
110 const uint16_t port, int64_t n)
112 hs_service_t *service;
114 tor_assert(ident_pk);
116 service = hs_service_find(ident_pk);
117 if (!service) {
118 /* This is possible because an onion service client can end up here due to
119 * having an identity key onto a connection _to_ an onion service. We
120 * can't differentiate that from an actual onion service initiated by a
121 * service and thus the only way to know is to lookup the service. */
122 return;
124 hs_metrics_update_by_service(key, service, port, n);
127 /** Return a list of all the onion service metrics stores. This is the
128 * function attached to the .get_metrics() member of the subsys_t. */
129 const smartlist_t *
130 hs_metrics_get_stores(void)
132 /* We can't have the caller to free the returned list so keep it static,
133 * simply update it. */
134 static smartlist_t *stores_list = NULL;
136 smartlist_free(stores_list);
137 stores_list = hs_service_get_metrics_stores();
138 return stores_list;
141 /** Initialize the metrics store in the given service. */
142 void
143 hs_metrics_service_init(hs_service_t *service)
145 tor_assert(service);
147 /* This function is called when we register a service and so it could either
148 * be a new service or a service that was just reloaded through a HUP signal
149 * for instance. Thus, it is possible that the service has already an
150 * initialized store. If so, just return. */
151 if (service->metrics.store) {
152 return;
155 service->metrics.store = metrics_store_new();
156 init_store(service);
159 /** Free the metrics store in the given service. */
160 void
161 hs_metrics_service_free(hs_service_t *service)
163 tor_assert(service);
165 metrics_store_free(service->metrics.store);