From: Zbigniew Jędrzejewski-Szmek Date: Mon, 11 May 2026 18:51:47 +0000 (+0200) Subject: report: drop MetricsFamilyContext, CGroupContext, CGroupInfo X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5cba9704ad4c1ee372fa6ab3f62f85670162151b;p=thirdparty%2Fsystemd.git report: drop MetricsFamilyContext, CGroupContext, CGroupInfo Previously, we passed around information about the MetricFamily'ies and the varlink connection in a helper structure. Having a hybrid of const static and runtime stuff is iffy. Let's simplify things by passing two separate parameters. Also, in report-cgroup.c we built a cache of parsed values. This requires additional storage requirements and introduces complexity when dealing with population of the cache at the appropriate time. This cache is not useful: for each cgroup, we generate a list of metrics, and we have all the information at hand. The only reason why we'd create the cache and not generate all the relevant replies at once was that the helper functions called the .generate function for each MetricFamily separately. The MetricFamily interface is changed, so that metrics can be defined without a .generate function. This is understood to mean that the preceding metric family's .generate function will also genarate this family. This allows us to define related metrics nicely in a table: { METRIC_IO_SYSTEMD_CGROUP_PREFIX "CpuUsage", generate_func }, { METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadBytes", NULL }, { METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadOperations", NULL }, { METRIC_IO_SYSTEMD_CGROUP_PREFIX "SomethingElse", generate_func2 }, ... When implementing .Describe, we list all the families. When implementing .List, we only call those with .generate, and we get the same results as before. This allows the .generate functions to be simplified: instead of keeping state, they just spit out all the metrics for a given object in a tight loop. --- diff --git a/src/core/varlink-metrics.c b/src/core/varlink-metrics.c index f1ac0791bc9..a470341c2b1 100644 --- a/src/core/varlink-metrics.c +++ b/src/core/varlink-metrics.c @@ -11,13 +11,14 @@ #include "unit.h" #include "varlink-metrics.h" -static int active_timestamp_build_json(MetricFamilyContext *context, void *userdata) { +static int active_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); _cleanup_(sd_json_variant_unrefp) sd_json_variant *enter_fields = NULL; r = sd_json_buildo(&enter_fields, SD_JSON_BUILD_PAIR_STRING("event", "enter")); @@ -35,7 +36,8 @@ static int active_timestamp_build_json(MetricFamilyContext *context, void *userd continue; r = metric_build_send_unsigned( - context, + mf, + vl, unit->id, unit->active_enter_timestamp.realtime, enter_fields); @@ -43,7 +45,8 @@ static int active_timestamp_build_json(MetricFamilyContext *context, void *userd return r; r = metric_build_send_unsigned( - context, + mf, + vl, unit->id, unit->active_exit_timestamp.realtime, exit_fields); @@ -54,13 +57,14 @@ static int active_timestamp_build_json(MetricFamilyContext *context, void *userd return 0; } -static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void *userdata) { +static int inactive_exit_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH_KEY(unit, key, manager->units) { /* ignore aliases */ @@ -68,7 +72,8 @@ static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void continue; r = metric_build_send_unsigned( - context, + mf, + vl, unit->id, unit->inactive_exit_timestamp.realtime, /* fields= */ NULL); @@ -79,13 +84,14 @@ static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void return 0; } -static int state_change_timestamp_build_json(MetricFamilyContext *context, void *userdata) { +static int state_change_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH_KEY(unit, key, manager->units) { /* ignore aliases */ @@ -93,7 +99,8 @@ static int state_change_timestamp_build_json(MetricFamilyContext *context, void continue; r = metric_build_send_unsigned( - context, + mf, + vl, unit->id, unit->state_change_timestamp.realtime, /* fields= */ NULL); @@ -104,15 +111,17 @@ static int state_change_timestamp_build_json(MetricFamilyContext *context, void return 0; } -static int status_errno_build_json(MetricFamilyContext *context, void *userdata) { +static int status_errno_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); int r; - assert(context); + assert(mf && mf->name); + assert(vl); LIST_FOREACH(units_by_type, unit, manager->units_by_type[UNIT_SERVICE]) { r = metric_build_send_unsigned( - context, + mf, + vl, unit->id, (uint64_t) SERVICE(unit)->status_errno, /* fields= */ NULL); @@ -123,13 +132,14 @@ static int status_errno_build_json(MetricFamilyContext *context, void *userdata) return 0; } -static int unit_active_state_build_json(MetricFamilyContext *context, void *userdata) { +static int unit_active_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH_KEY(unit, key, manager->units) { /* ignore aliases */ @@ -137,7 +147,8 @@ static int unit_active_state_build_json(MetricFamilyContext *context, void *user continue; r = metric_build_send_string( - context, + mf, + vl, unit->id, unit_active_state_to_string(unit_active_state(unit)), /* fields= */ NULL); @@ -148,13 +159,14 @@ static int unit_active_state_build_json(MetricFamilyContext *context, void *user return 0; } -static int unit_load_state_build_json(MetricFamilyContext *context, void *userdata) { +static int unit_load_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH_KEY(unit, key, manager->units) { /* ignore aliases */ @@ -162,7 +174,8 @@ static int unit_load_state_build_json(MetricFamilyContext *context, void *userda continue; r = metric_build_send_string( - context, + mf, + vl, unit->id, unit_load_state_to_string(unit->load_state), /* fields= */ NULL); @@ -173,15 +186,20 @@ static int unit_load_state_build_json(MetricFamilyContext *context, void *userda return 0; } -static int nrestarts_build_json(MetricFamilyContext *context, void *userdata) { +static int nrestarts_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); int r; - assert(context); + assert(mf && mf->name); + assert(vl); LIST_FOREACH(units_by_type, unit, manager->units_by_type[UNIT_SERVICE]) { r = metric_build_send_unsigned( - context, unit->id, SERVICE(unit)->n_restarts, /* fields= */ NULL); + mf, + vl, + unit->id, + SERVICE(unit)->n_restarts, + /* fields= */ NULL); if (r < 0) return r; } @@ -189,23 +207,26 @@ static int nrestarts_build_json(MetricFamilyContext *context, void *userdata) { return 0; } -static int reload_count_build_json(MetricFamilyContext *context, void *userdata) { +static int reload_count_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); - assert(context); + assert(mf && mf->name); + assert(vl); return metric_build_send_unsigned( - context, + mf, + vl, /* object= */ NULL, manager->reload_count, /* fields= */ NULL); } -static int units_by_type_total_build_json(MetricFamilyContext *context, void *userdata) { +static int units_by_type_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); int r; - assert(context); + assert(mf && mf->name); + assert(vl); for (UnitType type = 0; type < _UNIT_TYPE_MAX; type++) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; @@ -219,7 +240,8 @@ static int units_by_type_total_build_json(MetricFamilyContext *context, void *us return r; r = metric_build_send_unsigned( - context, + mf, + vl, /* object= */ NULL, counter, fields); @@ -230,14 +252,15 @@ static int units_by_type_total_build_json(MetricFamilyContext *context, void *us return 0; } -static int units_by_state_total_build_json(MetricFamilyContext *context, void *userdata) { +static int units_by_state_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); uint64_t counters[_UNIT_ACTIVE_STATE_MAX] = {}; Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); /* TODO need a rework probably with state counter */ HASHMAP_FOREACH_KEY(unit, key, manager->units) { @@ -256,7 +279,8 @@ static int units_by_state_total_build_json(MetricFamilyContext *context, void *u return r; r = metric_build_send_unsigned( - context, + mf, + vl, /* object= */ NULL, counters[state], fields); @@ -267,38 +291,43 @@ static int units_by_state_total_build_json(MetricFamilyContext *context, void *u return 0; } -static int jobs_queued_build_json(MetricFamilyContext *context, void *userdata) { +static int jobs_queued_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); - assert(context); + assert(mf && mf->name); + assert(vl); return metric_build_send_unsigned( - context, + mf, + vl, /* object= */ NULL, hashmap_size(manager->jobs), /* fields= */ NULL); } -static int system_state_build_json(MetricFamilyContext *context, void *userdata) { +static int system_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); - assert(context); + assert(mf && mf->name); + assert(vl); return metric_build_send_string( - context, + mf, + vl, /* object= */ NULL, manager_state_to_string(manager_state(manager)), /* fields= */ NULL); } -static int units_by_load_state_total_build_json(MetricFamilyContext *context, void *userdata) { +static int units_by_load_state_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); uint64_t counters[_UNIT_LOAD_STATE_MAX] = {}; Unit *unit; char *key; int r; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH_KEY(unit, key, manager->units) { /* ignore aliases */ @@ -316,7 +345,8 @@ static int units_by_load_state_total_build_json(MetricFamilyContext *context, vo return r; r = metric_build_send_unsigned( - context, + mf, + vl, /* object= */ NULL, counters[state], fields); @@ -327,13 +357,14 @@ static int units_by_load_state_total_build_json(MetricFamilyContext *context, vo return 0; } -static int units_total_build_json(MetricFamilyContext *context, void *userdata) { +static int units_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); uint64_t count = 0; Unit *unit; char *key; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH_KEY(unit, key, manager->units) { /* ignore aliases */ @@ -344,7 +375,8 @@ static int units_total_build_json(MetricFamilyContext *context, void *userdata) } return metric_build_send_unsigned( - context, + mf, + vl, /* object= */ NULL, count, /* fields= */ NULL); diff --git a/src/network/networkd-varlink-metrics.c b/src/network/networkd-varlink-metrics.c index 50aacebbf80..ee5645314cd 100644 --- a/src/network/networkd-varlink-metrics.c +++ b/src/network/networkd-varlink-metrics.c @@ -19,7 +19,8 @@ typedef const char* (*link_metric_extractor_t)(const Link *link); static int link_metric_build_json( - MetricFamilyContext *context, + const MetricFamily *mf, + sd_varlink *vl, link_metric_extractor_t extractor, void *userdata) { @@ -27,11 +28,12 @@ static int link_metric_build_json( Link *link; int r; - assert(context); + assert(mf && mf->name); + assert(vl); assert(extractor); HASHMAP_FOREACH(link, manager->links_by_index) { - r = metric_build_send_string(context, link->ifname, extractor(link), /* fields= */ NULL); + r = metric_build_send_string(mf, vl, link->ifname, extractor(link), /* fields= */ NULL); if (r < 0) return r; } @@ -63,50 +65,52 @@ static const char* link_get_oper_state(const Link *l) { return link_operstate_to_string(ASSERT_PTR(l)->operstate); } -static int link_address_state_build_json(MetricFamilyContext *ctx, void *userdata) { - return link_metric_build_json(ctx, link_get_address_state, userdata); +static int link_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + return link_metric_build_json(mf, vl, link_get_address_state, userdata); } -static int link_admin_state_build_json(MetricFamilyContext *ctx, void *userdata) { - return link_metric_build_json(ctx, link_get_admin_state, userdata); +static int link_admin_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + return link_metric_build_json(mf, vl, link_get_admin_state, userdata); } -static int link_carrier_state_build_json(MetricFamilyContext *ctx, void *userdata) { - return link_metric_build_json(ctx, link_get_carrier_state, userdata); +static int link_carrier_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + return link_metric_build_json(mf, vl, link_get_carrier_state, userdata); } -static int link_ipv4_address_state_build_json(MetricFamilyContext *ctx, void *userdata) { - return link_metric_build_json(ctx, link_get_ipv4_address_state, userdata); +static int link_ipv4_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + return link_metric_build_json(mf, vl, link_get_ipv4_address_state, userdata); } -static int link_ipv6_address_state_build_json(MetricFamilyContext *ctx, void *userdata) { - return link_metric_build_json(ctx, link_get_ipv6_address_state, userdata); +static int link_ipv6_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + return link_metric_build_json(mf, vl, link_get_ipv6_address_state, userdata); } -static int link_oper_state_build_json(MetricFamilyContext *ctx, void *userdata) { - return link_metric_build_json(ctx, link_get_oper_state, userdata); +static int link_oper_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + return link_metric_build_json(mf, vl, link_get_oper_state, userdata); } -static int managed_interfaces_build_json(MetricFamilyContext *context, void *userdata) { +static int managed_interfaces_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Link *link; uint64_t count = 0; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH(link, manager->links_by_index) if (link->network) count++; - return metric_build_send_unsigned(context, /* object= */ NULL, count, /* fields= */ NULL); + return metric_build_send_unsigned(mf, vl, /* object= */ NULL, count, /* fields= */ NULL); } -static int required_for_online_build_json(MetricFamilyContext *context, void *userdata) { +static int required_for_online_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Link *link; int r; - assert(context); + assert(mf && mf->name); + assert(vl); HASHMAP_FOREACH(link, manager->links_by_index) { if (!link->network) @@ -114,7 +118,8 @@ static int required_for_online_build_json(MetricFamilyContext *context, void *us if (link->network->required_for_online == 0) { r = metric_build_send_string( - context, + mf, + vl, link->ifname, "no", /* fields= */ NULL); @@ -127,7 +132,8 @@ static int required_for_online_build_json(MetricFamilyContext *context, void *us if (range.min == range.max) r = metric_build_send_string( - context, + mf, + vl, link->ifname, min_str, /* fields= */ NULL); @@ -137,7 +143,8 @@ static int required_for_online_build_json(MetricFamilyContext *context, void *us return -ENOMEM; r = metric_build_send_string( - context, + mf, + vl, link->ifname, value, /* fields= */ NULL); diff --git a/src/report/report-basic.c b/src/report/report-basic.c index 50a4dfaaf13..11ecb7b45c6 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -13,92 +13,102 @@ #include "report-basic.h" #include "virt.h" -static int architecture_generate(MetricFamilyContext *context, void *userdata) { - assert(context); +static int architecture_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + assert(mf && mf->name); + assert(link); return metric_build_send_string( - context, + mf, + link, /* object= */ NULL, architecture_to_string(uname_architecture()), /* fields= */ NULL); } -static int boot_id_generate(MetricFamilyContext *context, void *userdata) { +static int boot_id_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { sd_id128_t id; int r; - assert(context); + assert(mf && mf->name); + assert(link); r = sd_id128_get_boot(&id); if (r < 0) return r; return metric_build_send_string( - context, + mf, + link, /* object= */ NULL, SD_ID128_TO_STRING(id), /* fields= */ NULL); } -static int hostname_generate(MetricFamilyContext *context, void *userdata) { +static int hostname_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { _cleanup_free_ char *hostname = NULL; int r; - assert(context); + assert(mf && mf->name); + assert(link); r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &hostname); if (r < 0) return r; return metric_build_send_string( - context, + mf, + link, /* object= */ NULL, hostname, /* fields= */ NULL); } -static int kernel_version_generate(MetricFamilyContext *context, void *userdata) { +static int kernel_version_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { struct utsname u; - assert(context); + assert(mf && mf->name); + assert(link); assert_se(uname(&u) >= 0); return metric_build_send_string( - context, + mf, + link, /* object= */ NULL, u.release, /* fields= */ NULL); } -static int machine_id_generate(MetricFamilyContext *context, void *userdata) { +static int machine_id_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { sd_id128_t id; int r; - assert(context); + assert(mf && mf->name); + assert(link); r = sd_id128_get_machine(&id); if (r < 0) return r; return metric_build_send_string( - context, + mf, + link, /* object= */ NULL, SD_ID128_TO_STRING(id), /* fields= */ NULL); } -static int virtualization_generate(MetricFamilyContext *context, void *userdata) { - Virtualization v; +static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + assert(mf && mf->name); + assert(link); - assert(context); - - v = detect_virtualization(); + Virtualization v = detect_virtualization(); if (v < 0) return v; return metric_build_send_string( - context, + mf, + link, /* object= */ NULL, virtualization_to_string(v), /* fields= */ NULL); @@ -107,39 +117,39 @@ static int virtualization_generate(MetricFamilyContext *context, void *userdata) static const MetricFamily metric_family_table[] = { /* Keep entries ordered alphabetically */ { - .name = METRIC_IO_SYSTEMD_BASIC_PREFIX "Architecture", - .description = "CPU architecture", - .type = METRIC_FAMILY_TYPE_STRING, + METRIC_IO_SYSTEMD_BASIC_PREFIX "Architecture", + "CPU architecture", + METRIC_FAMILY_TYPE_STRING, .generate = architecture_generate, }, { - .name = METRIC_IO_SYSTEMD_BASIC_PREFIX "BootID", - .description = "Current boot ID", - .type = METRIC_FAMILY_TYPE_STRING, + METRIC_IO_SYSTEMD_BASIC_PREFIX "BootID", + "Current boot ID", + METRIC_FAMILY_TYPE_STRING, .generate = boot_id_generate, }, { - .name = METRIC_IO_SYSTEMD_BASIC_PREFIX "Hostname", - .description = "System hostname", - .type = METRIC_FAMILY_TYPE_STRING, + METRIC_IO_SYSTEMD_BASIC_PREFIX "Hostname", + "System hostname", + METRIC_FAMILY_TYPE_STRING, .generate = hostname_generate, }, { - .name = METRIC_IO_SYSTEMD_BASIC_PREFIX "KernelVersion", - .description = "Kernel version", - .type = METRIC_FAMILY_TYPE_STRING, + METRIC_IO_SYSTEMD_BASIC_PREFIX "KernelVersion", + "Kernel version", + METRIC_FAMILY_TYPE_STRING, .generate = kernel_version_generate, }, { - .name = METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineID", - .description = "Machine ID", - .type = METRIC_FAMILY_TYPE_STRING, + METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineID", + "Machine ID", + METRIC_FAMILY_TYPE_STRING, .generate = machine_id_generate, }, { - .name = METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization", - .description = "Virtualization type", - .type = METRIC_FAMILY_TYPE_STRING, + METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization", + "Virtualization type", + METRIC_FAMILY_TYPE_STRING, .generate = virtualization_generate, }, {} diff --git a/src/report/report-cgroup-server.c b/src/report/report-cgroup-server.c index 9f5ac937069..279edbb8d13 100644 --- a/src/report/report-cgroup-server.c +++ b/src/report/report-cgroup-server.c @@ -2,7 +2,6 @@ #include "sd-varlink.h" -#include "alloc-util.h" #include "build.h" #include "format-table.h" #include "help-util.h" @@ -15,14 +14,9 @@ static int vl_server(void) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *vs = NULL; - _cleanup_(cgroup_context_freep) CGroupContext *ctx = NULL; int r; - ctx = new0(CGroupContext, 1); - if (!ctx) - return log_oom(); - - r = varlink_server_new(&vs, SD_VARLINK_SERVER_INHERIT_USERDATA, ctx); + r = varlink_server_new(&vs, SD_VARLINK_SERVER_INHERIT_USERDATA, /* userdata= */ NULL); if (r < 0) return log_error_errno(r, "Failed to allocate Varlink server: %m"); diff --git a/src/report/report-cgroup.c b/src/report/report-cgroup.c index 9a52c03d177..48fcbb5a647 100644 --- a/src/report/report-cgroup.c +++ b/src/report/report-cgroup.c @@ -16,256 +16,65 @@ #include "string-util.h" #include "time-util.h" -typedef struct CGroupInfo { - char *unit; - char *path; - uint64_t io_rbytes; - uint64_t io_rios; - int io_stat_cached; /* 0 = not attempted, > 0 = cached, < 0 = -errno */ - uint64_t cpu_total_nsec; - uint64_t cpu_user_nsec; - uint64_t cpu_system_nsec; - int cpu_stat_cached; /* 0 = not attempted, > 0 = cached, < 0 = -errno */ -} CGroupInfo; - -static CGroupInfo *cgroup_info_free(CGroupInfo *info) { - if (!info) - return NULL; - free(info->unit); - free(info->path); - return mfree(info); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupInfo*, cgroup_info_free); - -static void cgroup_info_array_free(CGroupInfo **infos, size_t n) { - FOREACH_ARRAY(i, infos, n) - cgroup_info_free(*i); - free(infos); -} - -static void cgroup_context_flush(CGroupContext *ctx) { - assert(ctx); - cgroup_info_array_free(ctx->cgroups, ctx->n_cgroups); - ctx->cgroups = NULL; - ctx->n_cgroups = 0; - ctx->cache_populated = false; -} - -CGroupContext *cgroup_context_free(CGroupContext *ctx) { - if (!ctx) - return NULL; - cgroup_context_flush(ctx); - return mfree(ctx); -} - -static int walk_cgroups_recursive(const char *path, CGroupInfo ***infos, size_t *n_infos) { - _cleanup_closedir_ DIR *d = NULL; - int r; - - assert(path); - assert(infos); - assert(n_infos); - - /* Collect any unit cgroup we encounter */ - _cleanup_free_ char *name = NULL; - r = cg_path_get_unit(path, &name); - if (r >= 0) { - _cleanup_(cgroup_info_freep) CGroupInfo *info = new(CGroupInfo, 1); - if (!info) - return log_oom(); - - *info = (CGroupInfo) { - .unit = TAKE_PTR(name), - .path = strdup(path), - }; - if (!info->path) - return log_oom(); - - if (!GREEDY_REALLOC(*infos, *n_infos + 1)) - return log_oom(); - - (*infos)[(*n_infos)++] = TAKE_PTR(info); - return 0; /* Unit cgroups are leaf nodes for our purposes */ - } - - /* Stop at delegation boundaries — don't descend into delegated subtrees */ - r = cg_is_delegated(path); - if (r == -ENOENT) - return 0; - if (r < 0) - return log_debug_errno(r, "Failed to check delegation for '%s': %m", path); - if (r > 0) - return 0; - - r = cg_enumerate_subgroups(path, &d); - if (r == -ENOENT) - return 0; - if (r < 0) - return log_debug_errno(r, "Failed to enumerate cgroup '%s': %m", path); - - for (;;) { - _cleanup_free_ char *fn = NULL, *child = NULL; - - r = cg_read_subgroup(d, &fn); - if (r < 0) - return log_debug_errno(r, "Failed to read subgroup from '%s': %m", path); - if (r == 0) - break; - - child = path_join(empty_to_root(path), fn); - if (!child) - return log_oom(); - - path_simplify(child); - - r = walk_cgroups_recursive(child, infos, n_infos); - if (r < 0) - return r; - } - - return 0; -} - -static int walk_cgroups(CGroupContext *ctx, CGroupInfo ***ret, size_t *ret_n) { - int r; - - assert(ctx); - assert(ret); - assert(ret_n); - - /* Return cached result if available */ - if (ctx->cache_populated) { - *ret = ctx->cgroups; - *ret_n = ctx->n_cgroups; - return 0; - } - - CGroupInfo **infos = NULL; - size_t n_infos = 0; - CLEANUP_ARRAY(infos, n_infos, cgroup_info_array_free); - - r = walk_cgroups_recursive("", &infos, &n_infos); - if (r < 0) - return r; - - ctx->cgroups = TAKE_PTR(infos); - ctx->n_cgroups = TAKE_GENERIC(n_infos, size_t, 0); - ctx->cache_populated = true; - - *ret = ctx->cgroups; - *ret_n = ctx->n_cgroups; - return 0; -} - /* Parse cpu.stat for a cgroup once, extracting usage_usec, user_usec and system_usec * in a single read so each scrape only opens the file once per cgroup. */ -static int cpu_stat_parse( - const char *cgroup_path, - uint64_t *ret_total_nsec, - uint64_t *ret_user_nsec, - uint64_t *ret_system_nsec) { - - char *values[3] = {}; - uint64_t total_us, user_us, system_us; +static int cpu_stat_parse(const char *cgroup_path, uint64_t ret[static 3]) { + char* strings[3] = {}; + CLEANUP_ELEMENTS(strings, free_many_charp); + uint64_t values[3]; int r; assert(cgroup_path); - assert(ret_total_nsec); - assert(ret_user_nsec); - assert(ret_system_nsec); r = cg_get_keyed_attribute( cgroup_path, "cpu.stat", STRV_MAKE("usage_usec", "user_usec", "system_usec"), - values); - if (r < 0) - return r; - - r = safe_atou64(values[0], &total_us); - if (r >= 0) - r = safe_atou64(values[1], &user_us); - if (r >= 0) - r = safe_atou64(values[2], &system_us); - - free_many_charp(values, ELEMENTSOF(values)); + strings); if (r < 0) return r; - *ret_total_nsec = total_us * NSEC_PER_USEC; - *ret_user_nsec = user_us * NSEC_PER_USEC; - *ret_system_nsec = system_us * NSEC_PER_USEC; - return 0; -} - -static int ensure_cpu_stat_cached(CGroupInfo *info) { - int r; - - assert(info); - - if (info->cpu_stat_cached > 0) - return 0; - if (info->cpu_stat_cached < 0) - return info->cpu_stat_cached; - - r = cpu_stat_parse(info->path, &info->cpu_total_nsec, &info->cpu_user_nsec, &info->cpu_system_nsec); - if (r < 0) { - if (r != -ENOENT) - log_debug_errno(r, "Failed to parse cpu.stat for '%s': %m", info->path); - info->cpu_stat_cached = r; - return r; + for (unsigned i = 0; i < 3; i++) { + r = safe_atou64(strings[i], &values[i]); + if (r < 0) + return r; } - info->cpu_stat_cached = 1; + for (unsigned i = 0; i < 3; i++) + ret[i] = values[i] * NSEC_PER_USEC; return 0; } -static int cpu_usage_send_one( - MetricFamilyContext *context, - const char *unit, - uint64_t value_nsec, - const char *type) { +static int cpu_usage_send( + const MetricFamily *mf, + sd_varlink *link, + const char *path, + const char *unit) { - _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; + static const char* const types[] = { "total", "user", "system" }; + uint64_t values[3]; int r; - assert(context); + assert(mf && mf->name); + assert(link); + assert(path); assert(unit); - assert(type); - r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", type)); - if (r < 0) - return r; - - return metric_build_send_unsigned(context, unit, value_nsec, fields); -} - -static int cpu_usage_build_json(MetricFamilyContext *context, void *userdata) { - CGroupContext *ctx = ASSERT_PTR(userdata); - CGroupInfo **cgroups; - size_t n_cgroups; - int r; - - assert(context); - - r = walk_cgroups(ctx, &cgroups, &n_cgroups); - if (r < 0) - return 0; /* Skip metric on failure */ - - FOREACH_ARRAY(c, cgroups, n_cgroups) { - if (ensure_cpu_stat_cached(*c) < 0) - continue; + r = cpu_stat_parse(path, values); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "cpu.stat"); + return 0; + } - r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_total_nsec, "total"); - if (r < 0) - return r; + for (unsigned i = 0; i < 3; i++) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; - r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_user_nsec, "user"); + r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", types[i])); if (r < 0) return r; - r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_system_nsec, "system"); + r = metric_build_send_unsigned(mf, link, unit, values[i], fields); if (r < 0) return r; } @@ -273,113 +82,20 @@ static int cpu_usage_build_json(MetricFamilyContext *context, void *userdata) { return 0; } -static int memory_usage_build_json(MetricFamilyContext *context, void *userdata) { - CGroupContext *ctx = ASSERT_PTR(userdata); - CGroupInfo **cgroups; - size_t n_cgroups; - int r; - - assert(context); - - r = walk_cgroups(ctx, &cgroups, &n_cgroups); - if (r < 0) - return 0; - - FOREACH_ARRAY(c, cgroups, n_cgroups) { - uint64_t current = 0, limit = UINT64_MAX; - - r = cg_get_attribute_as_uint64((*c)->path, "memory.current", ¤t); - if (r >= 0) { - /* Walk up the cgroup tree to find the tightest memory limit */ - _cleanup_free_ char *path_buf = strdup((*c)->path); - if (!path_buf) - return log_oom(); - - for (char *p = path_buf;;) { - uint64_t high, max; - - r = cg_get_attribute_as_uint64(p, "memory.max", &max); - if (r >= 0 && max < limit) - limit = max; - - r = cg_get_attribute_as_uint64(p, "memory.high", &high); - if (r >= 0 && high < limit) - limit = high; - - /* Move to parent */ - const char *e; - r = path_find_last_component(p, /* accept_dot_dot= */ false, &e, NULL); - if (r <= 0) - break; - p[e - p] = '\0'; - } - - if (limit != UINT64_MAX && limit > current) { - _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; - r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "available")); - if (r < 0) - return r; - - r = metric_build_send_unsigned( - context, - (*c)->unit, - limit - current, - fields); - if (r < 0) - return r; - } - - _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; - r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "current")); - if (r < 0) - return r; - - r = metric_build_send_unsigned( - context, - (*c)->unit, - current, - fields); - if (r < 0) - return r; - } - - uint64_t val; - r = cg_get_attribute_as_uint64((*c)->path, "memory.peak", &val); - if (r >= 0) { - _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; - r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "peak")); - if (r < 0) - return r; - - r = metric_build_send_unsigned( - context, - (*c)->unit, - val, - fields); - if (r < 0) - return r; - } - } - - return 0; -} - /* Parse io.stat for a cgroup once, summing both rbytes= and rios= fields in a * single pass to avoid reading the file twice. */ -static int io_stat_parse(const char *cgroup_path, uint64_t *ret_rbytes, uint64_t *ret_rios) { +static int io_stat_parse(const char *cgroup_path, uint64_t ret[static 2]) { _cleanup_free_ char *path = NULL; - _cleanup_fclose_ FILE *f = NULL; uint64_t rbytes = 0, rios = 0; int r; - assert(ret_rbytes); - assert(ret_rios); + assert(ret); r = cg_get_path(cgroup_path, "io.stat", &path); if (r < 0) return r; - f = fopen(path, "re"); + _cleanup_fclose_ FILE *f = fopen(path, "re"); if (!f) return -errno; @@ -421,54 +137,111 @@ static int io_stat_parse(const char *cgroup_path, uint64_t *ret_rbytes, uint64_t } } - *ret_rbytes = rbytes; - *ret_rios = rios; + ret[0] = rbytes; + ret[1] = rios; return 0; } -static int ensure_io_stat_cached(CGroupInfo *info) { - int r; +static int io_read_send( + const MetricFamily mf[static 2], + sd_varlink *link, + const char *path, + const char *unit) { - assert(info); + uint64_t values[2]; + int r; - if (info->io_stat_cached > 0) - return 0; - if (info->io_stat_cached < 0) - return info->io_stat_cached; + assert(mf && mf[0].name && mf[1].name); + assert(link); + assert(path); + assert(unit); - r = io_stat_parse(info->path, &info->io_rbytes, &info->io_rios); + r = io_stat_parse(path, values); if (r < 0) { if (r != -ENOENT) - log_debug_errno(r, "Failed to parse IO stats for '%s': %m", info->path); - info->io_stat_cached = r; - return r; + log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "io.stat"); + return 0; + } + + for (unsigned i = 0; i < 2; i++) { + r = metric_build_send_unsigned(mf + i, link, unit, values[i], /* fields= */ NULL); + if (r < 0) + return r; } - info->io_stat_cached = 1; return 0; } -static int io_read_bytes_build_json(MetricFamilyContext *context, void *userdata) { - CGroupContext *ctx = ASSERT_PTR(userdata); - CGroupInfo **cgroups; - size_t n_cgroups; +static int memory_usage_send( + const MetricFamily *mf, + sd_varlink *link, + const char *path, + const char *unit) { + + static const char* const types[] = { "current", "available", "peak" }; + bool bad[ELEMENTSOF(types)] = {}; + uint64_t current = 0, limit = UINT64_MAX, peak = 0; int r; - assert(context); + assert(mf && mf->name); + assert(link); + assert(path); + assert(unit); - r = walk_cgroups(ctx, &cgroups, &n_cgroups); - if (r < 0) - return 0; + r = cg_get_attribute_as_uint64(path, "memory.current", ¤t); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "memory.current"); + + bad[0] = bad[1] = true; + + } else { + /* Walk up the cgroup tree to find the tightest memory limit */ + _cleanup_free_ char *path_buf = strdup(path); + if (!path_buf) + return log_oom(); + + for (char *p = path_buf;;) { + uint64_t high, max; + + r = cg_get_attribute_as_uint64(p, "memory.max", &max); + if (r >= 0 && max < limit) + limit = max; + + r = cg_get_attribute_as_uint64(p, "memory.high", &high); + if (r >= 0 && high < limit) + limit = high; - FOREACH_ARRAY(c, cgroups, n_cgroups) { - if (ensure_io_stat_cached(*c) < 0) + /* Move to parent */ + const char *e; + r = path_find_last_component(p, /* accept_dot_dot= */ false, &e, NULL); + if (r <= 0) + break; + p[e - p] = '\0'; + } + + if (limit == UINT64_MAX || limit <= current) + bad[1] = true; + } + + r = cg_get_attribute_as_uint64(path, "memory.peak", &peak); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "memory.peak"); + bad[2] = true; + } + + uint64_t values[] = { current, limit - current, peak }; + for (unsigned i = 0; i < ELEMENTSOF(values); i++) { + if (bad[i]) continue; - r = metric_build_send_unsigned( - context, - (*c)->unit, - (*c)->io_rbytes, - /* fields= */ NULL); + _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; + r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", types[i])); + if (r < 0) + return r; + + r = metric_build_send_unsigned(mf, link, unit, values[i], fields); if (r < 0) return r; } @@ -476,58 +249,95 @@ static int io_read_bytes_build_json(MetricFamilyContext *context, void *userdata return 0; } -static int io_read_operations_build_json(MetricFamilyContext *context, void *userdata) { - CGroupContext *ctx = ASSERT_PTR(userdata); - CGroupInfo **cgroups; - size_t n_cgroups; +static int tasks_current_send( + const MetricFamily *mf, + sd_varlink *link, + const char *path, + const char *unit) { + + uint64_t val; int r; - assert(context); + assert(mf && mf->name); + assert(link); + assert(path); + assert(unit); - r = walk_cgroups(ctx, &cgroups, &n_cgroups); - if (r < 0) + r = cg_get_attribute_as_uint64(path, "pids.current", &val); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "pids.current"); return 0; + } - FOREACH_ARRAY(c, cgroups, n_cgroups) { - if (ensure_io_stat_cached(*c) < 0) - continue; + return metric_build_send_unsigned(mf, link, unit, val, /* fields= */ NULL); +} - r = metric_build_send_unsigned( - context, - (*c)->unit, - (*c)->io_rios, - /* fields= */ NULL); +static int walk_cgroups( + const MetricFamily mf[static 5], + sd_varlink *link, + const char *path) { + + int r; + + assert(mf && mf[0].name && mf[1].name && mf[2].name && mf[3].name && mf[4].name); + assert(mf[0].generate && !mf[1].generate && !mf[2].generate && !mf[3].generate && !mf[4].generate); + assert(path); + + _cleanup_free_ char *unit = NULL; + r = cg_path_get_unit(path, &unit); + if (r >= 0) { + r = cpu_usage_send(mf + 0, link, path, unit); if (r < 0) return r; - } - return 0; -} + r = io_read_send(mf + 1, link, path, unit); + if (r < 0) + return r; -static int tasks_current_build_json(MetricFamilyContext *context, void *userdata) { - CGroupContext *ctx = ASSERT_PTR(userdata); - CGroupInfo **cgroups; - size_t n_cgroups; - int r; + r = memory_usage_send(mf + 3, link, path, unit); + if (r < 0) + return r; - assert(context); + r = tasks_current_send(mf + 4, link, path, unit); + if (r < 0) + return r; - r = walk_cgroups(ctx, &cgroups, &n_cgroups); + return 0; /* Unit cgroups are leaf nodes for our purposes */ + } + + /* Stop at delegation boundaries — don't descend into delegated subtrees */ + r = cg_is_delegated(path); + if (r == -ENOENT) + return 0; if (r < 0) + return log_debug_errno(r, "Failed to check delegation for '%s': %m", path); + if (r > 0) return 0; - FOREACH_ARRAY(c, cgroups, n_cgroups) { - uint64_t val; + _cleanup_closedir_ DIR *d = NULL; + r = cg_enumerate_subgroups(path, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_debug_errno(r, "Failed to enumerate cgroup '%s': %m", path); - r = cg_get_attribute_as_uint64((*c)->path, "pids.current", &val); + for (;;) { + _cleanup_free_ char *fn = NULL, *child = NULL; + + r = cg_read_subgroup(d, &fn); if (r < 0) - continue; + return log_debug_errno(r, "Failed to read subgroup from '%s': %m", path); + if (r == 0) + break; - r = metric_build_send_unsigned( - context, - (*c)->unit, - val, - /* fields= */ NULL); + child = path_join(empty_to_root(path), fn); + if (!child) + return log_oom(); + + path_simplify(child); + + r = walk_cgroups(mf, link, child); if (r < 0) return r; } @@ -535,37 +345,49 @@ static int tasks_current_build_json(MetricFamilyContext *context, void *userdata return 0; } +static int cgroup_stats_send( + const MetricFamily mf[static 5], + sd_varlink *link, + void *userdata) { + + assert(mf); + assert(link); + assert(!userdata); + + return walk_cgroups(mf, link, ""); +} + static const MetricFamily cgroup_metric_family_table[] = { /* Keep metrics ordered alphabetically */ { - .name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "CpuUsage", - .description = "Per unit metric: CPU usage in nanoseconds (type=total|user|system)", - .type = METRIC_FAMILY_TYPE_COUNTER, - .generate = cpu_usage_build_json, + METRIC_IO_SYSTEMD_CGROUP_PREFIX "CpuUsage", + "Per unit metric: CPU usage in nanoseconds (type=total|user|system)", + METRIC_FAMILY_TYPE_COUNTER, + .generate = cgroup_stats_send, }, { - .name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadBytes", - .description = "Per unit metric: IO bytes read", - .type = METRIC_FAMILY_TYPE_COUNTER, - .generate = io_read_bytes_build_json, + METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadBytes", + "Per unit metric: IO bytes read", + METRIC_FAMILY_TYPE_COUNTER, + .generate = NULL, }, { - .name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadOperations", - .description = "Per unit metric: IO read operations", - .type = METRIC_FAMILY_TYPE_COUNTER, - .generate = io_read_operations_build_json, + METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadOperations", + "Per unit metric: IO read operations", + METRIC_FAMILY_TYPE_COUNTER, + .generate = NULL, }, { - .name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "MemoryUsage", - .description = "Per unit metric: memory usage in bytes", - .type = METRIC_FAMILY_TYPE_GAUGE, - .generate = memory_usage_build_json, + METRIC_IO_SYSTEMD_CGROUP_PREFIX "MemoryUsage", + "Per unit metric: memory usage in bytes", + METRIC_FAMILY_TYPE_GAUGE, + .generate = NULL, }, { - .name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "TasksCurrent", - .description = "Per unit metric: current number of tasks", - .type = METRIC_FAMILY_TYPE_GAUGE, - .generate = tasks_current_build_json, + METRIC_IO_SYSTEMD_CGROUP_PREFIX "TasksCurrent", + "Per unit metric: current number of tasks", + METRIC_FAMILY_TYPE_GAUGE, + .generate = NULL, }, {} }; @@ -575,12 +397,5 @@ int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd } int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { - CGroupContext *ctx = ASSERT_PTR(userdata); - int r; - - r = metrics_method_list(cgroup_metric_family_table, link, parameters, flags, userdata); - - cgroup_context_flush(ctx); - - return r; + return metrics_method_list(cgroup_metric_family_table, link, parameters, flags, userdata); } diff --git a/src/report/report-cgroup.h b/src/report/report-cgroup.h index dae8411df58..ce1e55b280b 100644 --- a/src/report/report-cgroup.h +++ b/src/report/report-cgroup.h @@ -5,16 +5,5 @@ #define METRIC_IO_SYSTEMD_CGROUP_PREFIX "io.systemd.CGroup." -typedef struct CGroupInfo CGroupInfo; - -typedef struct CGroupContext { - CGroupInfo **cgroups; - size_t n_cgroups; - bool cache_populated; -} CGroupContext; - -CGroupContext *cgroup_context_free(CGroupContext *ctx); -DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupContext*, cgroup_context_free); - int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/shared/metrics.c b/src/shared/metrics.c index 6c1490cbab8..7c50f8ab34e 100644 --- a/src/shared/metrics.c +++ b/src/shared/metrics.c @@ -73,15 +73,14 @@ static int metric_family_build_json(const MetricFamily *mf, sd_json_variant **re } int metrics_method_describe( - const MetricFamily metric_family_table[], + const MetricFamily mfs[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { - int r; - assert(metric_family_table); + assert(mfs); assert(link); assert(parameters); assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)); @@ -94,7 +93,7 @@ int metrics_method_describe( if (r < 0) return r; - for (const MetricFamily *mf = metric_family_table; mf && mf->name; mf++) { + for (const MetricFamily *mf = mfs; mf->name; mf++) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; r = metric_family_build_json(mf, &v); @@ -110,15 +109,14 @@ int metrics_method_describe( } int metrics_method_list( - const MetricFamily metric_family_table[], + const MetricFamily mfs[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { - int r; - assert(metric_family_table); + assert(mfs); assert(link); assert(parameters); assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)); @@ -131,25 +129,28 @@ int metrics_method_list( if (r < 0) return r; - MetricFamilyContext ctx = { .link = link }; - for (const MetricFamily *mf = metric_family_table; mf && mf->name; mf++) { - assert(mf->generate); - - ctx.metric_family = mf; - r = mf->generate(&ctx, userdata); - if (r < 0) - return log_debug_errno( - r, "Failed to list metrics for metric family '%s': %m", mf->name); - } + for (const MetricFamily *mf = mfs; mf->name; mf++) + /* Metrics without .generate are handled by the preceding entry with .generate. + * Call the generating functions that are defined. */ + if (mf->generate) { + r = mf->generate(mf, link, userdata); + if (r < 0) + return log_debug_errno(r, "Failed to list metrics for metric family '%s': %m", mf->name); + } return 0; } -static int metric_build_send(MetricFamilyContext *context, const char *object, sd_json_variant *value, sd_json_variant *fields) { - assert(context); +static int metric_build_send( + const MetricFamily *mf, + sd_varlink *link, + const char *object, + sd_json_variant *value, + sd_json_variant *fields) { + + assert(mf); + assert(link); assert(value); - assert(context->link); - assert(context->metric_family); if (fields) { assert(sd_json_variant_is_object(fields)); @@ -160,33 +161,51 @@ static int metric_build_send(MetricFamilyContext *context, const char *object, s assert(sd_json_variant_is_string(e)); } - return sd_varlink_replybo(context->link, - SD_JSON_BUILD_PAIR_STRING("name", context->metric_family->name), + return sd_varlink_replybo( + link, + SD_JSON_BUILD_PAIR_STRING("name", mf->name), JSON_BUILD_PAIR_STRING_NON_EMPTY("object", object), SD_JSON_BUILD_PAIR_VARIANT("value", value), JSON_BUILD_PAIR_VARIANT_NON_NULL("fields", fields)); } -int metric_build_send_string(MetricFamilyContext *context, const char *object, const char *value, sd_json_variant *fields) { +int metric_build_send_string( + const MetricFamily *mf, + sd_varlink *link, + const char *object, + const char *value, + sd_json_variant *fields) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; int r; + assert(mf); + assert(link); assert(value); r = sd_json_variant_new_string(&v, value); if (r < 0) return log_debug_errno(r, "Failed to allocate JSON string: %m"); - return metric_build_send(context, object, v, fields); + return metric_build_send(mf, link, object, v, fields); } -int metric_build_send_unsigned(MetricFamilyContext *context, const char *object, uint64_t value, sd_json_variant *fields) { +int metric_build_send_unsigned( + const MetricFamily *mf, + sd_varlink *link, + const char *object, + uint64_t value, + sd_json_variant *fields) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; int r; + assert(mf); + assert(link); + r = sd_json_variant_new_unsigned(&v, value); if (r < 0) return log_debug_errno(r, "Failed to allocate JSON unsigned: %m"); - return metric_build_send(context, object, v, fields); + return metric_build_send(mf, link, object, v, fields); } diff --git a/src/shared/metrics.h b/src/shared/metrics.h index ffc28cb0a9d..7c4afb5de77 100644 --- a/src/shared/metrics.h +++ b/src/shared/metrics.h @@ -13,12 +13,7 @@ typedef enum MetricFamilyType { typedef struct MetricFamily MetricFamily; -typedef struct MetricFamilyContext { - const MetricFamily* metric_family; - sd_varlink *link; -} MetricFamilyContext; - -typedef int (*metric_family_generate_func_t) (MetricFamilyContext *mfc, void *userdata); +typedef int (*metric_family_generate_func_t) (const MetricFamily *mf, sd_varlink *link, void *userdata); typedef struct MetricFamily { const char *name; @@ -38,8 +33,8 @@ int metrics_setup_varlink_server( DECLARE_STRING_TABLE_LOOKUP_TO_STRING(metric_family_type, MetricFamilyType); -int metrics_method_describe(const MetricFamily metric_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); -int metrics_method_list(const MetricFamily metric_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int metrics_method_describe(const MetricFamily mfs[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int metrics_method_list(const MetricFamily mfs[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); -int metric_build_send_string(MetricFamilyContext* context, const char *object, const char *value, sd_json_variant *fields); -int metric_build_send_unsigned(MetricFamilyContext* context, const char *object, uint64_t value, sd_json_variant *fields); +int metric_build_send_string(const MetricFamily* mf, sd_varlink *link, const char *object, const char *value, sd_json_variant *fields); +int metric_build_send_unsigned(const MetricFamily* mf, sd_varlink *link, const char *object, uint64_t value, sd_json_variant *fields);