]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: adding CGroup runtime for io.systemd.Unit.List 37646/head
authorIvan Kruglov <mail@ikruglov.com>
Mon, 2 Jun 2025 18:04:19 +0000 (20:04 +0200)
committerIvan Kruglov <mail@ikruglov.com>
Thu, 5 Jun 2025 11:05:05 +0000 (04:05 -0700)
src/core/varlink-cgroup.c
src/core/varlink-cgroup.h
src/core/varlink-unit.c
src/shared/varlink-io.systemd.Unit.c

index 5d9ed103147723828efdf4fbd9e250ffa3faee77..ec401054597885e477854f1beb28461d788f3125 100644 (file)
@@ -8,6 +8,7 @@
 #include "json-util.h"
 #include "in-addr-prefix-util.h"
 #include "ip-protocol-list.h"
+#include "path-util.h"
 #include "set.h"
 #include "unit.h"
 #include "varlink-cgroup.h"
@@ -351,3 +352,261 @@ int unit_cgroup_context_build_json(sd_json_variant **ret, const char *name, void
                         /* Others */
                         SD_JSON_BUILD_PAIR_BOOLEAN("CoredumpReceive", c->coredump_receive));
 }
+
+static int memory_accounting_metric_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        CGroupMemoryAccountingMetric metric;
+        uint64_t value;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        metric = cgroup_memory_accounting_metric_from_string(name);
+        assert(metric >= 0);
+
+        r = unit_get_memory_accounting(u, metric, &value);
+        if (r == -ENODATA)
+                goto empty;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get value for '%s': %m", name);
+
+        if (value == UINT64_MAX)
+                goto empty;
+
+        return sd_json_variant_new_unsigned(ret, value);
+
+empty:
+        *ret = NULL;
+        return 0;
+}
+
+static int memory_available_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        uint64_t value;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        r = unit_get_memory_available(u, &value);
+        if (r == -ENODATA)
+                goto empty;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get value of available memory: %m");
+
+        if (value == UINT64_MAX)
+                goto empty;
+
+        return sd_json_variant_new_unsigned(ret, value);
+
+empty:
+        *ret = NULL;
+        return 0;
+}
+
+static int effective_limit_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        CGroupLimitType type;
+        uint64_t value;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        type = cgroup_effective_limit_type_from_string(name);
+        assert(type >= 0);
+
+        r = unit_get_effective_limit(u, type, &value);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get value for '%s': %m", name);
+
+        if (value == UINT64_MAX) {
+                *ret = NULL;
+                return 0;
+        }
+
+        return sd_json_variant_new_unsigned(ret, value);
+}
+
+static int cpu_usage_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        nsec_t ns;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        r = unit_get_cpu_usage(u, &ns);
+        if (r == -ENODATA)
+                goto empty;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get cpu usage: %m");
+
+        if (ns == NSEC_INFINITY)
+                goto empty;
+
+        return sd_json_variant_new_unsigned(ret, ns);
+
+empty:
+        *ret = NULL;
+        return 0;
+}
+
+static int effective_cpuset_build_json(sd_json_variant **ret, const char *name, void *userdata, const char *cpuset_name) {
+        Unit *u = ASSERT_PTR(userdata);
+        _cleanup_(cpu_set_reset) CPUSet cpus = {};
+        int r;
+
+        assert(ret);
+        assert(name);
+        assert(cpuset_name);
+
+        r = unit_get_cpuset(u, &cpus, cpuset_name);
+        if (r == -ENODATA) {
+                *ret = NULL;
+                return 0;
+        }
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get cpu set '%s': %m", cpuset_name);
+
+        return cpu_set_build_json(ret, name, &cpus);
+}
+
+static inline int effective_cpus_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        return effective_cpuset_build_json(ret, name, userdata, "cpuset.cpus.effective");
+}
+
+static inline int effective_mems_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        return effective_cpuset_build_json(ret, name, userdata, "cpuset.mems.effective");
+}
+
+static int tasks_current_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        uint64_t cn;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        r = unit_get_tasks_current(u, &cn);
+        if (r == -ENODATA)
+                goto empty;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get count of current tasks: %m");
+
+        if (cn == UINT64_MAX)
+                goto empty;
+
+        return sd_json_variant_new_unsigned(ret, cn);
+
+empty:
+        *ret = NULL;
+        return 0;
+}
+
+static int get_ip_counter_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        CGroupIPAccountingMetric metric;
+        uint64_t value;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        metric = cgroup_ip_accounting_metric_from_string(name);
+        assert(metric >= 0);
+
+        r = unit_get_ip_accounting(u, metric, &value);
+        if (r == -ENODATA)
+                goto empty;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get value for '%s': %m", name);
+
+        if (value == UINT64_MAX)
+                goto empty;
+
+        return sd_json_variant_new_unsigned(ret, value);
+
+empty:
+        *ret = NULL;
+        return 0;
+}
+
+static int get_io_counter_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+        CGroupIOAccountingMetric metric;
+        uint64_t value;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        metric = cgroup_io_accounting_metric_from_string(name);
+        assert(metric >= 0);
+
+        r = unit_get_io_accounting(u, metric, &value);
+        if (r == -ENODATA)
+                goto empty;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get value for '%s': %m", name);
+
+        if (value == UINT64_MAX)
+                goto empty;
+
+        return sd_json_variant_new_unsigned(ret, value);
+
+empty:
+        *ret = NULL;
+        return 0;
+}
+
+int unit_cgroup_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        Unit *u = ASSERT_PTR(userdata);
+
+        assert(ret);
+        assert(name);
+
+        CGroupRuntime *crt = unit_get_cgroup_runtime(u);
+        if (!crt) {
+                *ret = NULL;
+                return 0;
+        }
+
+        return sd_json_buildo(
+                        ret,
+
+                        /* ID */
+                        JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("ID", crt->cgroup_id),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", crt->cgroup_path ? empty_to_root(crt->cgroup_path) : NULL),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("Slice", unit_slice_name(u)),
+
+                        /* Memory */
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryCurrent", memory_accounting_metric_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryPeak", memory_accounting_metric_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemorySwapCurrent", memory_accounting_metric_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemorySwapPeak", memory_accounting_metric_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryZSwapCurrent", memory_accounting_metric_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("MemoryAvailable", memory_available_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveMemoryMax", effective_limit_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveMemoryHigh", effective_limit_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveMemoryNodes", effective_mems_build_json, u),
+
+                        /* CPU */
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("CPUUsageNSec", cpu_usage_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveCPUs", effective_cpus_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("TasksCurrent", tasks_current_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("EffectiveTasksMax", effective_limit_build_json, u),
+
+                        /* IP */
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPIngressBytes", get_ip_counter_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPIngressPackets", get_ip_counter_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPEgressBytes", get_ip_counter_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IPEgressPackets", get_ip_counter_build_json, u),
+
+                        /* IO */
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOReadBytes", get_io_counter_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOReadOperations", get_io_counter_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOWriteBytes", get_io_counter_build_json, u),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("IOWriteOperations", get_io_counter_build_json, u));
+}
index 37fd07fa945a0e46c97d1de2d954d57cf1700ffb..befd1fdde84943212b1cf55988af419ef736cb97 100644 (file)
@@ -4,3 +4,4 @@
 #include "forward.h"
 
 int unit_cgroup_context_build_json(sd_json_variant **ret, const char *name, void *userdata);
+int unit_cgroup_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata);
index faf4ab4d4e69a4554eaa892aedf3eeecdae7e84d..467f4225a68b345b3944eda2f8b2c1ab77e0f63d 100644 (file)
@@ -300,7 +300,8 @@ static int unit_runtime_build_json(sd_json_variant **ret, const char *name, void
                         SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(u->invocation_id), "InvocationID", SD_JSON_BUILD_UUID(u->invocation_id)),
                         JSON_BUILD_PAIR_CALLBACK_NON_NULL("Markers", markers_build_json, &u->markers),
                         JSON_BUILD_PAIR_CALLBACK_NON_NULL("ActivationDetails", activation_details_build_json, u->activation_details),
-                        SD_JSON_BUILD_PAIR_BOOLEAN("DebugInvocation", u->debug_invocation));
+                        SD_JSON_BUILD_PAIR_BOOLEAN("DebugInvocation", u->debug_invocation),
+                        JSON_BUILD_PAIR_CALLBACK_NON_NULL("CGroup", unit_cgroup_runtime_build_json, u));
 }
 
 static int list_unit_one(sd_varlink *link, Unit *unit, bool more) {
index adea53fb26e72e9f6e6a934992823f90b8c5df56..955a7345aad98d7241431061489b466af8b8d103 100644 (file)
@@ -393,6 +393,67 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_FIELD_COMMENT("Trigger unit name"),
                 SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0));
 
+static SD_VARLINK_DEFINE_STRUCT_TYPE(
+                CGroupRuntime,
+
+                /* ID */
+                SD_VARLINK_FIELD_COMMENT("ID of the CGroup"),
+                SD_VARLINK_DEFINE_FIELD(ID, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path of the CGroup"),
+                SD_VARLINK_DEFINE_FIELD(Path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Slice of the CGroup"),
+                SD_VARLINK_DEFINE_FIELD(Slice, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+
+                /* Memory */
+                SD_VARLINK_FIELD_COMMENT("The current amount of memory used by the cgroup, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(MemoryCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The peak amount of memory used by the cgroup since its creation, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(MemoryPeak, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The current amount of swap space used by the cgroup, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(MemorySwapCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The peak amount of swap space used by the cgroup since its creation, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(MemorySwapPeak, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The current amount of zswap space used by the cgroup, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(MemoryZSwapCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The available memory for the cgroup, in bytes."),
+                SD_VARLINK_DEFINE_FIELD(MemoryAvailable, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The effective maximum amount of memory allowed for the cgroup, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(EffectiveMemoryMax, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The effective high watermark for memory usage by the cgroup, in bytes"),
+                SD_VARLINK_DEFINE_FIELD(EffectiveMemoryHigh, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Memory NUMA nodes that the cgroup is allowed to use"),
+                SD_VARLINK_DEFINE_FIELD(EffectiveMemoryNodes, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
+
+                /* CPU */
+                SD_VARLINK_FIELD_COMMENT("The total CPU usage time in nanoseconds (ns) for the cgroup"),
+                SD_VARLINK_DEFINE_FIELD(CPUUsageNSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("NUMA nodes that the cgroup is allowed to use"),
+                SD_VARLINK_DEFINE_FIELD(EffectiveCPUs, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The current number of tasks (i.e., processes or threads) running within the cgroup"),
+                SD_VARLINK_DEFINE_FIELD(TasksCurrent, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The maximum number of tasks that the cgroup is allowed to run concurrently"),
+                SD_VARLINK_DEFINE_FIELD(EffectiveTasksMax, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+
+                /* IP */
+                SD_VARLINK_FIELD_COMMENT("The total number of bytes received by the cgroup's IP stack"),
+                SD_VARLINK_DEFINE_FIELD(IPIngressBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The total number of incoming packets received by the cgroup's IP stack"),
+                SD_VARLINK_DEFINE_FIELD(IPIngressPackets, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("iThe total number of bytes sent by the cgroup's IP stack"),
+                SD_VARLINK_DEFINE_FIELD(IPEgressBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The total number of outgoing packets sent by the cgroup's IP stack"),
+                SD_VARLINK_DEFINE_FIELD(IPEgressPackets, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+
+                /* IO */
+                SD_VARLINK_FIELD_COMMENT("The total number of bytes read from block devices by the cgroup"),
+                SD_VARLINK_DEFINE_FIELD(IOReadBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The total number of read operations performed on block devices by the cgroup"),
+                SD_VARLINK_DEFINE_FIELD(IOReadOperations, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The total number of bytes written to block devices by the cgroup"),
+                SD_VARLINK_DEFINE_FIELD(IOWriteBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("The total number of write operations performed on block devices by the cgroup"),
+                SD_VARLINK_DEFINE_FIELD(IOWriteOperations, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
+
 static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 UnitRuntime,
                 SD_VARLINK_FIELD_COMMENT("If not empty, the field contains the name of another unit that this unit follows in state"),
@@ -450,7 +511,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_FIELD_COMMENT("Provides details about why a unit was activated"),
                 SD_VARLINK_DEFINE_FIELD_BY_TYPE(ActivationDetails, ActivationDetails, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("When true, logs about this unit will be at debug level regardless of other log level settings"),
-                SD_VARLINK_DEFINE_FIELD(DebugInvocation, SD_VARLINK_BOOL, 0));
+                SD_VARLINK_DEFINE_FIELD(DebugInvocation, SD_VARLINK_BOOL, 0),
+                SD_VARLINK_FIELD_COMMENT("The cgroup runtime of the unit"),
+                SD_VARLINK_DEFINE_FIELD_BY_TYPE(CGroup, CGroupRuntime, SD_VARLINK_NULLABLE));
 
 static SD_VARLINK_DEFINE_ERROR(NoSuchUnit);
 
@@ -496,5 +559,7 @@ SD_VARLINK_DEFINE_INTERFACE(
                 &vl_type_CGroupDeviceAllow,
                 SD_VARLINK_SYMBOL_COMMENT("CGroup context of a unit"),
                 &vl_type_CGroupContext,
+                SD_VARLINK_SYMBOL_COMMENT("CGroup runtime of a unit"),
+                &vl_type_CGroupRuntime,
                 SD_VARLINK_SYMBOL_COMMENT("No matching unit found"),
                 &vl_error_NoSuchUnit);