]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysupdate: show component description and enablement in 'systemd-sysupdate components... 42935/head
authorLennart Poettering <lennart@amutable.com>
Wed, 24 Jun 2026 10:18:31 +0000 (12:18 +0200)
committerLennart Poettering <lennart@amutable.com>
Thu, 9 Jul 2026 07:56:48 +0000 (09:56 +0200)
This is losely based on the features output, but shows the new metadata
for components.

src/sysupdate/sysupdate.c

index 0704aabd9e361d31451cb6caecb7d00f5cca0098..16aff0b6812846a353156bbb540149c3268a2eda 100644 (file)
@@ -163,6 +163,50 @@ static int context_from_cmdline(Context *ret) {
         return 0;
 }
 
+static int context_from_base_with_component(const Context *base, const char *component, Context *ret) {
+        assert(base);
+        assert(component);
+        assert(ret);
+
+        /* Copies the specified context, but changes the component to the specified one */
+
+        _cleanup_(context_done) Context context = CONTEXT_NULL;
+
+        context.instances_max = base->instances_max;
+        context.sync = base->sync;
+        context.reboot = base->reboot;
+        context.verify = base->verify;
+        context.offline = base->offline;
+        context.cleanup = base->cleanup;
+
+        if (strdup_to(&context.root, base->root) < 0)
+                return log_oom();
+
+        if (strdup_to(&context.image, base->image) < 0)
+                return log_oom();
+
+        if (strdup_to(&context.transfer_source, base->transfer_source) < 0)
+                return log_oom();
+
+        if (base->image_policy) {
+                context.image_policy = image_policy_copy(base->image_policy);
+                if (!context.image_policy)
+                        return log_oom();
+        }
+
+        if (strdup_to(&context.component, component) < 0)
+                return log_oom();
+
+        /* NB: we do not copy .loop_device/.mounted_dir here, since that's for lifetime tracking only, and
+         * not needed for access (we only need to copy .root/.image) for that. Under the assumption that the
+         * contexts initialized via context_from_base_with_component() have a shorter lifetime than the
+         * contexts they are copied from we don't bother with lifetime tracking of the loopback device/mount
+         * point here. */
+
+        *ret = TAKE_GENERIC(context, Context, CONTEXT_NULL);
+        return 0;
+}
+
 /* Stores any long-running server state which needs to persist between varlink calls, such as state for
  * pending polkit requests */
 typedef struct Server {
@@ -1078,10 +1122,9 @@ static int context_process_image(
 
         assert(c);
 
-        if (!c->image)
+        if (c->root || !c->image) /* Idempotent or nothing to do */
                 return 0;
 
-        assert(!c->root);
         assert(!c->mounted_dir);
         assert(!c->loop_device);
 
@@ -2279,12 +2322,58 @@ static int verb_components(int argc, char *argv[], uintptr_t _data, void *userda
                         return 0;
                 }
 
-                if (has_default_component)
-                        printf("%s<default>%s\n",
-                               ansi_highlight(), ansi_normal());
+                _cleanup_(table_unrefp) Table *t = table_new("", "component", "description", "documentation");
+                if (!t)
+                        return log_oom();
+
+                table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
+
+                if (has_default_component) {
+                        r = table_add_many(
+                                        t,
+                                        TABLE_EMPTY,
+                                        TABLE_STRING, "<default>",
+                                        TABLE_SET_COLOR, ansi_highlight(),
+                                        TABLE_STRING, "Default Component",
+                                        TABLE_EMPTY);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
 
-                STRV_FOREACH(i, component_names)
-                        puts(*i);
+                STRV_FOREACH(i, component_names) {
+                        _cleanup_(context_done) Context cc = CONTEXT_NULL;
+
+                        r = context_from_base_with_component(
+                                        &context,
+                                        *i,
+                                        &cc);
+                        if (r < 0)
+                                return r;
+
+                        r = context_load_offline(
+                                        &cc,
+                                        /* process_image_flags= */ 0,
+                                        /* read_definitions_flags= */ 0);
+                        if (r == -ENOMEM)
+                                return r;
+                        if (r < 0)
+                                continue;
+
+                        const char *doc = cc.component_documentation ? cc.component_documentation[0] : NULL;
+
+                        r = table_add_many(
+                                        t,
+                                        TABLE_BOOLEAN_CHECKMARK, cc.component_enabled,
+                                        TABLE_SET_COLOR, ansi_highlight_green_red(cc.component_enabled),
+                                        TABLE_STRING, *i,
+                                        TABLE_STRING, cc.component_description,
+                                        TABLE_STRING, doc,
+                                        TABLE_SET_URL, doc);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
         } else {
                 _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;