]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
report: drop MetricsFamilyContext, CGroupContext, CGroupInfo
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Mon, 11 May 2026 18:51:47 +0000 (20:51 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 12 May 2026 20:56:04 +0000 (22:56 +0200)
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.

src/core/varlink-metrics.c
src/network/networkd-varlink-metrics.c
src/report/report-basic.c
src/report/report-cgroup-server.c
src/report/report-cgroup.c
src/report/report-cgroup.h
src/shared/metrics.c
src/shared/metrics.h

index f1ac0791bc914d8399fb1d68f5edcb0d5e2b85d9..a470341c2b182c6150e74fe708e54cd1d1bcd644 100644 (file)
 #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);
index 50aacebbf8077f057d0d86b1768f9ac8c8f8407b..ee5645314cdd2a8048b3cb3523e8449ab1089da0 100644 (file)
@@ -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);
index 50a4dfaaf130190fe55e43f8bb8663500431d490..11ecb7b45c678067bc09058376d2e2bc4bd63d3a 100644 (file)
 #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,
         },
         {}
index 9f5ac9370694a8249e4daa286c9b659401b06e60..279edbb8d13ed923f2a555f1443e2e9585d7cc8f 100644 (file)
@@ -2,7 +2,6 @@
 
 #include "sd-varlink.h"
 
-#include "alloc-util.h"
 #include "build.h"
 #include "format-table.h"
 #include "help-util.h"
 
 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");
 
index 9a52c03d1774140208fa6d47aeed9ccb64ee6421..48fcbb5a647beff365febc0998de75ec6ae69c49 100644 (file)
 #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", &current);
-                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", &current);
+        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);
 }
index dae8411df58b06f03078416574189d89f8baba25..ce1e55b280bd3724d8c80923b692dbc7714acfef 100644 (file)
@@ -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);
index 6c1490cbab8c1c92a5a34dc65cb98340dbcba1f7..7c50f8ab34ee6a16d8bd1205debb7e8f61fabd8f 100644 (file)
@@ -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);
 }
index ffc28cb0a9d8cafce623074863ef4075f63fd303..7c4afb5de77db83419701cf9f2ba03a529c1ba6f 100644 (file)
@@ -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);