From 75e1963df1b4de95a1a463d11c47ecb66d62a917 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Feb 2026 23:42:02 +0100 Subject: [PATCH 01/11] report: add the usual --json= argument --- src/report/report.c | 51 ++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index dcaf91c238..d568b7ab71 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -12,6 +12,7 @@ #include "fd-util.h" #include "log.h" #include "main-func.h" +#include "parse-argument.h" #include "path-lookup.h" #include "pretty-print.h" #include "runtime-scope.h" @@ -23,6 +24,7 @@ #define TIMEOUT_USEC (30 * USEC_PER_SEC) /* 30 seconds */ static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; +static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO; typedef struct Context { unsigned n_open_connections; @@ -150,20 +152,27 @@ static void context_done(Context *context) { free(context->metrics); } -static void metrics_output_sorted(Context *context) { +static int metrics_output_sorted(Context *context) { + int r; + assert(context); typesafe_qsort(context->metrics, context->n_metrics, metric_compare); - FOREACH_ARRAY(m, context->metrics, context->n_metrics) - sd_json_variant_dump( + FOREACH_ARRAY(m, context->metrics, context->n_metrics) { + r = sd_json_variant_dump( *m, - SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO | SD_JSON_FORMAT_FLUSH, + arg_json_format_flags | SD_JSON_FORMAT_FLUSH, stdout, - NULL); + /* prefix= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to write JSON: %m"); + } if (context->n_metrics == 0) - log_warning("No reporting sockets found."); + log_info("No reporting sockets found."); + + return 0; } static int metrics_query(void) { @@ -225,9 +234,7 @@ static int metrics_query(void) { return log_error_errno(r, "Failed to run event loop: %m"); } - metrics_output_sorted(&context); - - return 0; + return metrics_output_sorted(&context); } static int help(void) { @@ -244,6 +251,8 @@ static int help(void) { " --version Show package version\n" " --user Connect to user service manager\n" " --system Connect to system service manager (default)\n" + " --json=pretty|short\n" + " Configure JSON output\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -258,17 +267,19 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION = 0x100, ARG_USER, ARG_SYSTEM, + ARG_JSON, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "json", required_argument, NULL, ARG_JSON }, {} }; - int c; + int c, r; assert(argc >= 0); assert(argv); @@ -277,16 +288,28 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { case 'h': return help(); + case ARG_VERSION: return version(); + case ARG_USER: arg_runtime_scope = RUNTIME_SCOPE_USER; break; + case ARG_SYSTEM: arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; break; + + case ARG_JSON: + r = parse_json_argument(optarg, &arg_json_format_flags); + if (r <= 0) + return r; + + break; + case '?': return -EINVAL; + default: assert_not_reached(); } From 1f8b09dfc63c96a75d30e68a45b497626a923195 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Feb 2026 23:42:15 +0100 Subject: [PATCH 02/11] report: use sd_json_variant_unref_many() --- src/report/report.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index d568b7ab71..538eae04e9 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -147,9 +147,7 @@ static void sd_varlink_unref_many(sd_varlink **array, size_t n) { static void context_done(Context *context) { assert(context); - for (size_t i = 0; i < context->n_metrics; i++) - sd_json_variant_unref(context->metrics[i]); - free(context->metrics); + sd_json_variant_unref_many(context->metrics, context->n_metrics); } static int metrics_output_sorted(Context *context) { From 0ec663a41fc49a3e4ec592c4e0037f4bc7e8f6fc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Feb 2026 23:42:36 +0100 Subject: [PATCH 03/11] report: do not treat an empty report dir as an issue We should permit that the report varlink dir is created on the fly when the first socket is bound there. Hence, let's treat a non-existant dir equivalent to an empty one. We usually do this in our tree like this, do it here too. --- src/report/report.c | 53 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index 538eae04e9..2e4b358e5e 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -183,11 +183,6 @@ static int metrics_query(void) { log_debug("Looking for reports in %s/", metrics_path); - _cleanup_closedir_ DIR *d = opendir(metrics_path); - if (!d) - return log_full_errno(errno == ENOENT ? LOG_WARNING : LOG_ERR, errno, - "Failed to open metrics directory %s: %m", metrics_path); - _cleanup_(sd_event_unrefp) sd_event *event = NULL; r = sd_event_default(&event); if (r < 0) @@ -197,32 +192,38 @@ static int metrics_query(void) { if (r < 0) return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m"); - size_t n_varlinks = MAX_CONCURRENT_METRICS_SOCKETS; - sd_varlink **varlinks = new0(sd_varlink *, n_varlinks); - if (!varlinks) - return log_oom(); - - CLEANUP_ARRAY(varlinks, n_varlinks, sd_varlink_unref_many); - _cleanup_(context_done) Context context = {}; - FOREACH_DIRENT(de, d, - return log_warning_errno(errno, "Failed to read %s: %m", metrics_path)) { - - if (!IN_SET(de->d_type, DT_SOCK, DT_UNKNOWN)) - continue; - - _cleanup_free_ char *p = path_join(metrics_path, de->d_name); - if (!p) + _cleanup_closedir_ DIR *d = opendir(metrics_path); + if (!d) { + if (errno != ENOENT) + return log_error_errno(errno, "Failed to open metrics directory %s: %m", metrics_path); + } else { + size_t n_varlinks = MAX_CONCURRENT_METRICS_SOCKETS; + sd_varlink **varlinks = new0(sd_varlink *, n_varlinks); + if (!varlinks) return log_oom(); - r = metrics_call(p, event, &varlinks[context.n_open_connections], &context); - if (r < 0) - continue; + CLEANUP_ARRAY(varlinks, n_varlinks, sd_varlink_unref_many); - if (++context.n_open_connections >= MAX_CONCURRENT_METRICS_SOCKETS) { - log_warning("Too many concurrent metrics sockets, stop iterating"); - break; + FOREACH_DIRENT(de, d, + return log_warning_errno(errno, "Failed to read %s: %m", metrics_path)) { + + if (!IN_SET(de->d_type, DT_SOCK, DT_UNKNOWN)) + continue; + + _cleanup_free_ char *p = path_join(metrics_path, de->d_name); + if (!p) + return log_oom(); + + r = metrics_call(p, event, &varlinks[context.n_open_connections], &context); + if (r < 0) + continue; + + if (++context.n_open_connections >= MAX_CONCURRENT_METRICS_SOCKETS) { + log_warning("Too many concurrent metrics sockets, stop iterating"); + break; + } } } From 9fa9f10088288e15626f8df9b2380c1642ac96ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Feb 2026 23:43:31 +0100 Subject: [PATCH 04/11] report: -p is not defined --- src/report/report.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/report/report.c b/src/report/report.c index 2e4b358e5e..8da9e14994 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -283,7 +283,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) switch (c) { case 'h': return help(); From 3317ea84bc40c0976e22de7fe6906c65c77036d2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 22:57:41 +0100 Subject: [PATCH 05/11] report: pass Context as first argument to metrics_call() Typically the context object should be the first one. And the return parameters should be the last ones. --- src/report/report.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index 8da9e14994..600dd7501a 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -99,7 +99,7 @@ static int metrics_on_query_reply( return 0; } -static int metrics_call(const char *path, sd_event *event, sd_varlink **ret, Context *context) { +static int metrics_call(Context *context, const char *path, sd_event *event, sd_varlink **ret) { _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; int r; @@ -216,7 +216,7 @@ static int metrics_query(void) { if (!p) return log_oom(); - r = metrics_call(p, event, &varlinks[context.n_open_connections], &context); + r = metrics_call(&context, p, event, &varlinks[context.n_open_connections]); if (r < 0) continue; From 6c051e84c72d920c738128506658d65d8aeeb39e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 23:01:00 +0100 Subject: [PATCH 06/11] report: move event loop object into Context It's one of the primary objects that make up the program "context" conceptually, hence it also should be part of the Context object. This allows us to just have it available if the Context object is seen. --- src/report/report.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index 600dd7501a..e48286c5ff 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -27,6 +27,7 @@ static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO; typedef struct Context { + sd_event *event; unsigned n_open_connections; sd_json_variant **metrics; /* Collected metrics for sorting */ size_t n_metrics; @@ -93,18 +94,17 @@ static int metrics_on_query_reply( context->n_open_connections--; if (context->n_open_connections == 0) - (void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), EXIT_SUCCESS); + (void) sd_event_exit(context->event, EXIT_SUCCESS); } return 0; } -static int metrics_call(Context *context, const char *path, sd_event *event, sd_varlink **ret) { +static int metrics_call(Context *context, const char *path, sd_varlink **ret) { _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; int r; assert(path); - assert(event); assert(ret); assert(context); @@ -118,7 +118,7 @@ static int metrics_call(Context *context, const char *path, sd_event *event, sd_ if (r < 0) return log_error_errno(r, "Failed to set varlink timeout: %m"); - r = sd_varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL); + r = sd_varlink_attach_event(vl, context->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); @@ -148,6 +148,7 @@ static void context_done(Context *context) { assert(context); sd_json_variant_unref_many(context->metrics, context->n_metrics); + sd_event_unref(context->event); } static int metrics_output_sorted(Context *context) { @@ -183,17 +184,16 @@ static int metrics_query(void) { log_debug("Looking for reports in %s/", metrics_path); - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - r = sd_event_default(&event); + _cleanup_(context_done) Context context = {}; + + r = sd_event_default(&context.event); if (r < 0) return log_error_errno(r, "Failed to get event loop: %m"); - r = sd_event_set_signal_exit(event, true); + r = sd_event_set_signal_exit(context.event, true); if (r < 0) return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m"); - _cleanup_(context_done) Context context = {}; - _cleanup_closedir_ DIR *d = opendir(metrics_path); if (!d) { if (errno != ENOENT) @@ -216,7 +216,7 @@ static int metrics_query(void) { if (!p) return log_oom(); - r = metrics_call(&context, p, event, &varlinks[context.n_open_connections]); + r = metrics_call(&context, p, &varlinks[context.n_open_connections]); if (r < 0) continue; @@ -228,7 +228,7 @@ static int metrics_query(void) { } if (context.n_open_connections > 0) { - r = sd_event_loop(event); + r = sd_event_loop(context.event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); } From 1b9506601f6d2b48c1f57126fef6062baba96741 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 23:10:54 +0100 Subject: [PATCH 07/11] report: keep track of varlink connections inside of Context object Let's also move the Varlink connection management into the Context object. Let's also switch to Set* for it, so that we get get auto-expanding behaviour. --- src/report/report.c | 52 +++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index e48286c5ff..f62daa35b7 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -16,9 +16,11 @@ #include "path-lookup.h" #include "pretty-print.h" #include "runtime-scope.h" +#include "set.h" #include "sort-util.h" #include "string-util.h" #include "time-util.h" +#include "varlink-util.h" #define MAX_CONCURRENT_METRICS_SOCKETS 20 #define TIMEOUT_USEC (30 * USEC_PER_SEC) /* 30 seconds */ @@ -28,7 +30,7 @@ static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO typedef struct Context { sd_event *event; - unsigned n_open_connections; + Set *links; sd_json_variant **metrics; /* Collected metrics for sorting */ size_t n_metrics; } Context; @@ -90,23 +92,22 @@ static int metrics_on_query_reply( } if (!FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES)) { - assert(context->n_open_connections > 0); - context->n_open_connections--; + assert_se(set_remove(context->links, link) == link); + link = sd_varlink_close_unref(link); - if (context->n_open_connections == 0) + if (set_isempty(context->links)) (void) sd_event_exit(context->event, EXIT_SUCCESS); } return 0; } -static int metrics_call(Context *context, const char *path, sd_varlink **ret) { +static int metrics_call(Context *context, const char *path) { _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; int r; - assert(path); - assert(ret); assert(context); + assert(path); r = sd_varlink_connect_address(&vl, path); if (r < 0) @@ -130,23 +131,17 @@ static int metrics_call(Context *context, const char *path, sd_varlink **ret) { if (r < 0) return log_error_errno(r, "Failed to issue io.systemd.Metrics.List call: %m"); - *ret = TAKE_PTR(vl); + if (set_ensure_put(&context->links, &varlink_hash_ops, vl) < 0) + return log_oom(); + TAKE_PTR(vl); return 0; } -static void sd_varlink_unref_many(sd_varlink **array, size_t n) { - assert(array); - - FOREACH_ARRAY(v, array, n) - sd_varlink_unref(*v); - - free(array); -} - static void context_done(Context *context) { assert(context); + set_free(context->links); sd_json_variant_unref_many(context->metrics, context->n_metrics); sd_event_unref(context->event); } @@ -199,35 +194,26 @@ static int metrics_query(void) { if (errno != ENOENT) return log_error_errno(errno, "Failed to open metrics directory %s: %m", metrics_path); } else { - size_t n_varlinks = MAX_CONCURRENT_METRICS_SOCKETS; - sd_varlink **varlinks = new0(sd_varlink *, n_varlinks); - if (!varlinks) - return log_oom(); - - CLEANUP_ARRAY(varlinks, n_varlinks, sd_varlink_unref_many); - FOREACH_DIRENT(de, d, return log_warning_errno(errno, "Failed to read %s: %m", metrics_path)) { if (!IN_SET(de->d_type, DT_SOCK, DT_UNKNOWN)) continue; + if (set_size(context.links) >= MAX_CONCURRENT_METRICS_SOCKETS) { + log_warning("Too many concurrent metrics sockets, stopping iterating."); + break; + } + _cleanup_free_ char *p = path_join(metrics_path, de->d_name); if (!p) return log_oom(); - r = metrics_call(&context, p, &varlinks[context.n_open_connections]); - if (r < 0) - continue; - - if (++context.n_open_connections >= MAX_CONCURRENT_METRICS_SOCKETS) { - log_warning("Too many concurrent metrics sockets, stop iterating"); - break; - } + (void) metrics_call(&context, p); } } - if (context.n_open_connections > 0) { + if (!set_isempty(context.links)) { r = sd_event_loop(context.event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); From a7aebb22507cf6b2d825b7e3182c5ae1c647c823 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 23:12:30 +0100 Subject: [PATCH 08/11] report: don't claim no reporting sockets were found, when it's no metrics that were found Let's add a proper message about missing sources, and call them "sources", i.e. take a more high-level view on things. --- src/report/report.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index f62daa35b7..986ab18bf4 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -164,7 +164,7 @@ static int metrics_output_sorted(Context *context) { } if (context->n_metrics == 0) - log_info("No reporting sockets found."); + log_info("No metrics collected."); return 0; } @@ -213,7 +213,9 @@ static int metrics_query(void) { } } - if (!set_isempty(context.links)) { + if (set_isempty(context.links)) + log_info("No metrics sources found."); + else { r = sd_event_loop(context.event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); From 9b09b11d210d450b5a89d1711e3c0070e1752ce3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 23:26:21 +0100 Subject: [PATCH 09/11] report: rework limits logic Let's count how many sources we skip, and output that as a summary, and return an error exit code in this case. Let's also put a limit on the metrics collected, not just the sources. Also, our macros that put limits on things are usually called XYZ_MAX, follow the same scheme here. --- src/report/report.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index 986ab18bf4..62affd0a7c 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -22,7 +22,8 @@ #include "time-util.h" #include "varlink-util.h" -#define MAX_CONCURRENT_METRICS_SOCKETS 20 +#define METRICS_MAX 1024U +#define METRICS_LINKS_MAX 128U #define TIMEOUT_USEC (30 * USEC_PER_SEC) /* 30 seconds */ static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; @@ -32,7 +33,7 @@ typedef struct Context { sd_event *event; Set *links; sd_json_variant **metrics; /* Collected metrics for sorting */ - size_t n_metrics; + size_t n_metrics, n_skipped_metrics; } Context; static int metric_compare(sd_json_variant *const *a, sd_json_variant *const *b) { @@ -85,6 +86,11 @@ static int metrics_on_query_reply( else log_error("Varlink error: %s", error_id); } else { + if (context->n_metrics >= METRICS_MAX) { + context->n_skipped_metrics++; + return 0; + } + /* Collect metrics for later sorting */ if (!GREEDY_REALLOC(context->metrics, context->n_metrics + 1)) return log_oom(); @@ -189,6 +195,7 @@ static int metrics_query(void) { if (r < 0) return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m"); + size_t n_skipped_sources = 0; _cleanup_closedir_ DIR *d = opendir(metrics_path); if (!d) { if (errno != ENOENT) @@ -200,8 +207,8 @@ static int metrics_query(void) { if (!IN_SET(de->d_type, DT_SOCK, DT_UNKNOWN)) continue; - if (set_size(context.links) >= MAX_CONCURRENT_METRICS_SOCKETS) { - log_warning("Too many concurrent metrics sockets, stopping iterating."); + if (set_size(context.links) >= METRICS_LINKS_MAX) { + n_skipped_sources++; break; } @@ -219,9 +226,22 @@ static int metrics_query(void) { r = sd_event_loop(context.event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); + + r = metrics_output_sorted(&context); + if (r < 0) + return r; + + if (n_skipped_sources > 0) + log_warning("Too many metrics sources, only %u sources contacted, %zu sources skipped.", set_size(context.links), n_skipped_sources); + if (context.n_skipped_metrics > 0) + log_warning("Too many metrics, only %zu metrics collected, %zu metrics skipped.", context.n_metrics, context.n_skipped_metrics); + + if (n_skipped_sources > 0 || + context.n_skipped_metrics > 0) + return EXIT_FAILURE; } - return metrics_output_sorted(&context); + return EXIT_SUCCESS; } static int help(void) { From e18008703832056f0aedf13f977fec0e6e2236af Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 23:34:09 +0100 Subject: [PATCH 10/11] man: add super basic man page --- man/rules/meson.build | 1 + man/systemd-report.xml | 74 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 man/systemd-report.xml diff --git a/man/rules/meson.build b/man/rules/meson.build index d062187d6b..2faed1d29e 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -1132,6 +1132,7 @@ manpages = [ 'ENABLE_RANDOMSEED'], ['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''], ['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'], + ['systemd-report', '1', [], ''], ['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'], ['systemd-rfkill.service', '8', diff --git a/man/systemd-report.xml b/man/systemd-report.xml new file mode 100644 index 0000000000..c35993372a --- /dev/null +++ b/man/systemd-report.xml @@ -0,0 +1,74 @@ + + + + + + + + systemd-report + systemd + + + + systemd-report + 1 + + + + systemd-report + Generate report of system facts and metrics + + + + + systemd-report OPTIONS + + + + + Description + + systemd-report requests facts and metrics from the system and writes them to + standard output. + + + + Options + + The following options are understood: + + + + + + + + Query per-system metrics sources (the default), or the per-user metrics sources. + + + + + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1 + + + From 2d34ff822bfc1d2e7a5b1e726b94f5a1c7f6297a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 7 Feb 2026 23:55:23 +0100 Subject: [PATCH 11/11] TODO: add some items about report/metrics --- TODO | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TODO b/TODO index 62e877976a..699f514274 100644 --- a/TODO +++ b/TODO @@ -118,6 +118,14 @@ Deprecations and removals: Features: +* report: + - should the list of metrics use JSON-SEQ? or maybe be wrapped in a json + array (the latter might be necessary, once we sign the combination) + - "io.systemd.Manager.unit_active_state" is a weird mix of CamelCase and snake_case + - metrics from pid1: suppress metrics form units that are inactive and have nothing to report + - how to plug facts into this? i.e. hostname, ssh keys, and so on + - switch to daan's suggested hierarchy? + * mountfsd: when looking for non-foreign uid owned inodes up the tree, stop at mounts