diff --git a/src/core/varlink-metrics.c b/src/core/varlink-metrics.c index f1ac0791bc..a470341c2b 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 50aacebbf8..ee5645314c 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 50a4dfaaf1..772a5d9f74 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -9,137 +9,240 @@ #include "alloc-util.h" #include "architecture.h" #include "hostname-setup.h" +#include "log.h" #include "metrics.h" +#include "os-util.h" #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; +enum { + FIELD_PRETTY_NAME, + FIELD_NAME, + FIELD_ID, + FIELD_CPE_NAME, + FIELD_VARIANT_ID, + FIELD_VERSION_ID, + FIELD_BUILD_ID, + FIELD_IMAGE_VERSION, + FIELD_IMAGE_ID, + FIELD_SUPPORT_END, + FIELD_EXPERIMENT, + FIELD_SYSEXT_LEVEL, + FIELD_CONFEXT_LEVEL, + _FIELD_MAX, +}; - assert(context); +static int os_release_generate(const MetricFamily mf[static _FIELD_MAX - 1], sd_varlink *link, void *userdata) { + char* values[_FIELD_MAX] = {}; + CLEANUP_ELEMENTS(values, free_many_charp); + int r; - v = detect_virtualization(); + assert(mf && mf->name); + assert(link); + + r = parse_os_release(NULL, + "PRETTY_NAME", &values[FIELD_PRETTY_NAME], + "NAME", &values[FIELD_NAME], + "ID", &values[FIELD_ID], + "CPE_NAME", &values[FIELD_CPE_NAME], + "VARIANT_ID", &values[FIELD_VARIANT_ID], + "VERSION_ID", &values[FIELD_VERSION_ID], + "BUILD_ID", &values[FIELD_BUILD_ID], + "IMAGE_VERSION", &values[FIELD_IMAGE_VERSION], + "IMAGE_ID", &values[FIELD_IMAGE_ID], + "SUPPORT_END", &values[FIELD_SUPPORT_END], + "EXPERIMENT", &values[FIELD_EXPERIMENT], + "SYSEXT_LEVEL", &values[FIELD_SYSEXT_LEVEL], + "CONFEXT_LEVEL", &values[FIELD_CONFEXT_LEVEL]); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); + return 0; + } + + for (size_t i = 1; i < _FIELD_MAX; i++) { + const char *v = values[i]; + if (i == FIELD_NAME && values[FIELD_PRETTY_NAME]) + v = values[FIELD_PRETTY_NAME]; /* Prefer PRETTY_NAME to NAME */ + + if (v) { + r = metric_build_send_string( + mf + i - 1, + link, + /* object= */ NULL, + v, + /* fields= */ NULL); + if (r < 0) + return r; + } + } + + return 0; +} + +static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + assert(mf && mf->name); + assert(link); + + 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); } +#define OS_RELEASE_STANDARD_FIELD(name) \ + { \ + METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease." name, \ + "Operating system identification (" name "= field from os-release)", \ + METRIC_FAMILY_TYPE_STRING, \ + .generate = NULL, \ + } + 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 "OSRelease.NAME", + "Operating system human-readable name (PRETTY_NAME= or NAME= field from os-release)", + METRIC_FAMILY_TYPE_STRING, + .generate = os_release_generate, + }, + OS_RELEASE_STANDARD_FIELD("ID"), + OS_RELEASE_STANDARD_FIELD("CPE_NAME"), + OS_RELEASE_STANDARD_FIELD("VARIANT_ID"), + OS_RELEASE_STANDARD_FIELD("VERSION_ID"), + OS_RELEASE_STANDARD_FIELD("BUILD_ID"), + OS_RELEASE_STANDARD_FIELD("IMAGE_VERSION"), + OS_RELEASE_STANDARD_FIELD("IMAGE_ID"), + OS_RELEASE_STANDARD_FIELD("SUPPORT_END"), + OS_RELEASE_STANDARD_FIELD("EXPERIMENT"), + OS_RELEASE_STANDARD_FIELD("SYSEXT_LEVEL"), + OS_RELEASE_STANDARD_FIELD("CONFEXT_LEVEL"), + /* Keep those ↑ in sync with os_release_generate(). */ + { + 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 9f5ac93706..279edbb8d1 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 9a52c03d17..240f09f9f2 100644 --- a/src/report/report-cgroup.c +++ b/src/report/report-cgroup.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "cgroup-util.h" +#include "errno-util.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" @@ -16,349 +17,67 @@ #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); + strings); 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); + for (unsigned i = 0; i < 3; i++) { + r = safe_atou64(strings[i], &values[i]); + if (r < 0) + return r; + } - free_many_charp(values, ELEMENTSOF(values)); - 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; + for (unsigned i = 0; i < 3; i++) + ret[i] = values[i] * NSEC_PER_USEC; return 0; } -static int ensure_cpu_stat_cached(CGroupInfo *info) { +static int cpu_usage_send( + const MetricFamily *mf, + sd_varlink *link, + const char *path, + const char *unit) { + + static const char* const types[] = { "total", "user", "system" }; + uint64_t values[3]; int r; - assert(info); + assert(mf && mf->name); + assert(link); + assert(path); + assert(unit); - 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); + r = cpu_stat_parse(path, values); 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; - } - - info->cpu_stat_cached = 1; - return 0; -} - -static int cpu_usage_send_one( - MetricFamilyContext *context, - const char *unit, - uint64_t value_nsec, - const char *type) { - - _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; - int r; - - assert(context); - 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_usage_send_one(context, (*c)->unit, (*c)->cpu_total_nsec, "total"); - if (r < 0) - return r; - - r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_user_nsec, "user"); - if (r < 0) - return r; - - r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_system_nsec, "system"); - if (r < 0) - return r; - } - - 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) + log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "cpu.stat"); return 0; + } - FOREACH_ARRAY(c, cgroups, n_cgroups) { - uint64_t current = 0, limit = UINT64_MAX; + for (unsigned i = 0; i < 3; i++) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL; - 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(); + r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", types[i])); + if (r < 0) + return r; - 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; - } + r = metric_build_send_unsigned(mf, link, unit, values[i], fields); + if (r < 0) + return r; } return 0; @@ -366,22 +85,20 @@ static int memory_usage_build_json(MetricFamilyContext *context, void *userdata) /* 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; + return errno_or_else(EIO); for (;;) { _cleanup_free_ char *line = NULL; @@ -421,54 +138,34 @@ 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) { +static int io_read_send( + const MetricFamily mf[static 2], + sd_varlink *link, + const char *path, + const char *unit) { + + uint64_t values[2]; int r; - assert(info); + assert(mf && mf[0].name && mf[1].name); + assert(link); + assert(path); + assert(unit); - if (info->io_stat_cached > 0) - return 0; - if (info->io_stat_cached < 0) - return info->io_stat_cached; - - 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; } - 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; - int r; - - assert(context); - - r = walk_cgroups(ctx, &cgroups, &n_cgroups); - if (r < 0) - return 0; - - FOREACH_ARRAY(c, cgroups, n_cgroups) { - if (ensure_io_stat_cached(*c) < 0) - continue; - - r = metric_build_send_unsigned( - context, - (*c)->unit, - (*c)->io_rbytes, - /* fields= */ NULL); + 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; } @@ -476,27 +173,76 @@ 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 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"); - FOREACH_ARRAY(c, cgroups, n_cgroups) { - if (ensure_io_stat_cached(*c) < 0) + 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; + + /* 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_rios, - /* 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; } @@ -504,30 +250,95 @@ static int io_read_operations_build_json(MetricFamilyContext *context, void *use return 0; } -static int tasks_current_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); + 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; + } + + return metric_build_send_unsigned(mf, link, unit, val, /* 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; + + r = io_read_send(mf + 1, link, path, unit); + if (r < 0) + return r; + + r = memory_usage_send(mf + 3, link, path, unit); + if (r < 0) + return r; + + r = tasks_current_send(mf + 4, link, path, unit); + if (r < 0) + return r; + + 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 +346,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 +398,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 dae8411df5..ce1e55b280 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 6c1490cbab..7c50f8ab34 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 ffc28cb0a9..7c4afb5de7 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); diff --git a/test/units/TEST-74-AUX-UTILS.report.sh b/test/units/TEST-74-AUX-UTILS.report.sh index 456132c04d..d89a06cd9a 100755 --- a/test/units/TEST-74-AUX-UTILS.report.sh +++ b/test/units/TEST-74-AUX-UTILS.report.sh @@ -58,6 +58,10 @@ varlinkctl list-methods /run/systemd/report/io.systemd.Basic varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {} varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.Describe {} +id1="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {} | jq --seq -r 'select(.name == "io.systemd.Basic.OSRelease.ID") | .value')" +id2="$(. /etc/os-release; echo "$ID")" +[ "$id1" = "$id2" ] + # Test HTTP upload (plain http) FAKE_SERVER=/usr/lib/systemd/tests/integration-tests/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/fake-report-server.py CERTDIR=$(mktemp -d)