LCOV - code coverage report
Current view: top level - feature/hs - hs_metrics.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 49 52 94.2 %
Date: 2021-11-24 03:28:48 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.14