1 /* Copyright (c) 2020-2021, 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 /** 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. */
37 init_store(hs_service_t
*service
)
39 metrics_store_t
*store
;
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
);
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. */
74 hs_metrics_update_by_service(const hs_metrics_key_t key
,
75 hs_service_t
*service
, const uint16_t port
,
80 /* Get the metrics entry in the store. */
81 smartlist_t
*entries
= metrics_store_get_all(service
->metrics
.store
,
82 base_metrics
[key
].name
);
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
) {
93 metrics_store_entry_has_label(entry
,
94 metrics_format_label("port", port_to_str(port
)))) {
95 metrics_store_entry_update(entry
, n
);
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. */
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
);
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. */
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. */
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();
141 /** Initialize the metrics store in the given service. */
143 hs_metrics_service_init(hs_service_t
*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
) {
155 service
->metrics
.store
= metrics_store_new();
159 /** Free the metrics store in the given service. */
161 hs_metrics_service_free(hs_service_t
*service
)
165 metrics_store_free(service
->metrics
.store
);