1 /* Copyright (c) 2020, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * @brief Onion service metrics exposed through the MetricsPort
9 #define HS_METRICS_ENTRY_PRIVATE
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. */
25 port_to_str(const uint16_t port
)
28 tor_snprintf(buf
, sizeof(buf
), "%u", port
);
32 /** Return a static buffer pointer that contains a formatted label on the form
35 * Subsequent call to this function invalidates the previous buffer. */
37 format_label(const char *key
, const char *value
)
40 tor_snprintf(buf
, sizeof(buf
), "%s=%s", key
, value
);
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. */
49 init_store(hs_service_t
*service
)
51 metrics_store_t
*store
;
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 rend_service_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. */
79 hs_metrics_update_by_service(const hs_metrics_key_t key
,
80 hs_service_t
*service
, const uint16_t port
,
85 /* Get the metrics entry in the store. */
86 smartlist_t
*entries
= metrics_store_get_all(service
->metrics
.store
,
87 base_metrics
[key
].name
);
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
) {
98 metrics_store_entry_has_label(entry
,
99 format_label("port", port_to_str(port
)))) {
100 metrics_store_entry_update(entry
, n
);
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. */
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
);
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. */
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. */
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();
146 /** Initialize the metrics store in the given service. */
148 hs_metrics_service_init(hs_service_t
*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
) {
160 service
->metrics
.store
= metrics_store_new();
164 /** Free the metrics store in the given service. */
166 hs_metrics_service_free(hs_service_t
*service
)
170 metrics_store_free(service
->metrics
.store
);