]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysupdate: Add verb to inspect features
authorAdrian Vovk <adrianvovk@gmail.com>
Wed, 3 Jul 2024 21:49:36 +0000 (17:49 -0400)
committerAdrian Vovk <adrianvovk@gmail.com>
Fri, 18 Oct 2024 21:58:47 +0000 (17:58 -0400)
man/systemd-sysupdate.xml
src/sysupdate/sysupdate-transfer.c
src/sysupdate/sysupdate-transfer.h
src/sysupdate/sysupdate.c
test/units/TEST-72-SYSUPDATE.sh

index f7f1521a5d4207fcafef427413ffc0c3bdf2f5ba..778ff2e793ef3f627f2241f813506975bba41b4a 100644 (file)
         <xi:include href="version-info.xml" xpointer="v251"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>features</option> <optional><replaceable>FEATURE</replaceable></optional></term>
+
+        <listitem><para>If invoked without an argument, enumerates optional features and shows a summarizing
+        table, including which features are enabled or disabled. If a feature argument is specified, shows
+        details about the specific feature, including the transfers that are controlled by the feature.</para>
+
+        <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>check-new</option></term>
 
index 350a12ee0f88b5d963370e50b905b11e95553219..1143c3cf637ac10c8bcb8b6607318d77282f67cf 100644 (file)
@@ -45,11 +45,16 @@ Transfer* transfer_free(Transfer *t) {
 
         t->temporary_path = rm_rf_subvolume_and_free(t->temporary_path);
 
+        free(t->id);
+
         free(t->min_version);
         strv_free(t->protected_versions);
         free(t->current_symlink);
         free(t->final_path);
 
+        strv_free(t->features);
+        strv_free(t->requisite_features);
+
         strv_free(t->changelog);
         strv_free(t->appstream);
 
@@ -520,6 +525,7 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H
         };
 
         _cleanup_free_ char *filename = NULL;
+        char *e;
         int r;
 
         assert(path);
@@ -545,6 +551,10 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H
         if (r < 0)
                 return r;
 
+        e = ASSERT_PTR(endswith(filename, ".transfer") ?: endswith(filename, ".conf"));
+        *e = 0; /* Remove the file extension */
+        t->id = TAKE_PTR(filename);
+
         t->enabled = transfer_decide_if_enabled(t, known_features);
 
         if (!RESOURCE_IS_SOURCE(t->source.type))
index 841161846f451ca627972d223df963e8ee99b689..505c573d63b82bbefcdad7e5dea20203b0c2c9d6 100644 (file)
@@ -15,11 +15,15 @@ typedef struct Transfer Transfer;
 #include "sysupdate.h"
 
 struct Transfer {
+        char *id;
+
         char *min_version;
         char **protected_versions;
         char *current_symlink;
         bool verify;
 
+        char **features;
+        char **requisite_features;
         bool enabled;
 
         Resource source, target;
index 0cdcb34a588caaed7566313604115f94cbc8e222..6b97af111b14aecacb49e1f00ff3ad6c5fa9eaed 100644 (file)
@@ -1201,6 +1201,140 @@ static int verb_list(int argc, char **argv, void *userdata) {
         }
 }
 
+static int verb_features(int argc, char **argv, void *userdata) {
+        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+        _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
+        _cleanup_(context_freep) Context* context = NULL;
+        _cleanup_(table_unrefp) Table *table = NULL;
+        const char *feature_id;
+        Feature *f;
+        int r;
+
+        assert(argc <= 2);
+        feature_id = argc >= 2 ? argv[1] : NULL;
+
+        r = process_image(/* ro= */ true, &mounted_dir, &loop_device);
+        if (r < 0)
+                return r;
+
+        r = context_make_offline(&context, loop_device ? loop_device->node : NULL);
+        if (r < 0)
+                return r;
+
+        if (feature_id) {
+                _cleanup_strv_free_ char **transfers = NULL;
+
+                f = hashmap_get(context->features, feature_id);
+                if (!f)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                                               "Optional feature not found: %s",
+                                               feature_id);
+
+                table = table_new_vertical();
+                if (!table)
+                        return log_oom();
+
+                FOREACH_ARRAY(tr, context->transfers, context->n_transfers) {
+                        Transfer *t = *tr;
+
+                        if (!strv_contains(t->features, f->id) && !strv_contains(t->requisite_features, f->id))
+                                continue;
+
+                        r = strv_extend(&transfers, t->id);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                FOREACH_ARRAY(tr, context->disabled_transfers, context->n_disabled_transfers) {
+                        Transfer *t = *tr;
+
+                        if (!strv_contains(t->features, f->id) && !strv_contains(t->requisite_features, f->id))
+                                continue;
+
+                        r = strv_extend(&transfers, t->id);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                r = table_add_many(table,
+                                   TABLE_FIELD, "Name",
+                                   TABLE_STRING, f->id,
+                                   TABLE_FIELD, "Enabled",
+                                   TABLE_BOOLEAN, f->enabled);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (f->description) {
+                        r = table_add_many(table, TABLE_FIELD, "Description", TABLE_STRING, f->description);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (f->documentation) {
+                        r = table_add_many(table,
+                                           TABLE_FIELD, "Documentation",
+                                           TABLE_STRING, f->documentation,
+                                           TABLE_SET_URL, f->documentation);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (f->appstream) {
+                        r = table_add_many(table,
+                                           TABLE_FIELD, "AppStream",
+                                           TABLE_STRING, f->appstream,
+                                           TABLE_SET_URL, f->appstream);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (!strv_isempty(transfers)) {
+                        r = table_add_many(table, TABLE_FIELD, "Transfers", TABLE_STRV_WRAPPED, transfers);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+        } else if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+                table = table_new("", "feature", "description", "documentation");
+                if (!table)
+                        return log_oom();
+
+                HASHMAP_FOREACH(f, context->features) {
+                        r = table_add_many(table,
+                                           TABLE_BOOLEAN_CHECKMARK, f->enabled,
+                                           TABLE_SET_COLOR, ansi_highlight_green_red(f->enabled),
+                                           TABLE_STRING, f->id,
+                                           TABLE_STRING, f->description,
+                                           TABLE_STRING, f->documentation,
+                                           TABLE_SET_URL, f->documentation);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+        } else {
+                _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
+                _cleanup_strv_free_ char **features = NULL;
+
+                HASHMAP_FOREACH(f, context->features) {
+                        r = strv_extend(&features, f->id);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_STRV("features", features));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create JSON: %m");
+
+                r = sd_json_variant_dump(json, arg_json_format_flags, stdout, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to print JSON: %m");
+        }
+
+        return 0;
+}
+
 static int verb_check_new(int argc, char **argv, void *userdata) {
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
@@ -1517,6 +1651,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
                "\n%5$sUpdate OS images.%6$s\n"
                "\n%3$sCommands:%4$s\n"
                "  list [VERSION]          Show installed and available versions\n"
+               "  features [FEATURE]      Show optional features\n"
                "  check-new               Check if there's a new version available\n"
                "  update [VERSION]        Install new version now\n"
                "  vacuum                  Make room, by deleting old versions\n"
@@ -1729,8 +1864,9 @@ static int sysupdate_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "list",       VERB_ANY, 2, VERB_DEFAULT, verb_list              },
                 { "components", VERB_ANY, 1, 0,            verb_components        },
+                { "features",   VERB_ANY, 2, 0,            verb_features          },
                 { "check-new",  VERB_ANY, 1, 0,            verb_check_new         },
-                { "update",     VERB_ANY, 2, 0,            verb_update               },
+                { "update",     VERB_ANY, 2, 0,            verb_update            },
                 { "vacuum",     VERB_ANY, 1, 0,            verb_vacuum            },
                 { "reboot",     1,        1, 0,            verb_pending_or_reboot },
                 { "pending",    1,        1, 0,            verb_pending_or_reboot },
index 0598ef2c39d6b9baddbac8d219c7a135d4f740c2..e5c62c635ef316bec34529daf68a0df507ff617e 100755 (executable)
@@ -303,6 +303,8 @@ EOF
     verify_version_current "$blockdev" "$sector_size" v5 2
 
     # Now let's try enabling an optional feature
+    "$SYSUPDATE" features | grep "optional"
+    "$SYSUPDATE" features optional | grep "99-optional"
     test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v5.efi.extra.d/optional.efi"
     mkdir "$CONFIGDIR/optional.feature.d"
     echo -e "[Feature]\nEnabled=true" > "$CONFIGDIR/optional.feature.d/enable.conf"