]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
updatectl: convert to the new option and verb parsers
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Sat, 4 Apr 2026 19:52:46 +0000 (21:52 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 7 Apr 2026 08:55:53 +0000 (10:55 +0200)
Cosmetic differences in --help only.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
src/sysupdate/updatectl.c

index 636a3f064b94ff0da5deff9ebf8b43798fb61af4..c6e5c33fdfe481a4696b59ac34c72afbe93ebf82 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <getopt.h>
 #include <locale.h>
 
 #include "sd-bus.h"
@@ -19,6 +18,7 @@
 #include "hashmap.h"
 #include "json-util.h"
 #include "main-func.h"
+#include "options.h"
 #include "pager.h"
 #include "polkit-agent.h"
 #include "pretty-print.h"
@@ -644,6 +644,8 @@ static int describe(sd_bus *bus, const char *target_path, const char *version) {
         return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend);
 }
 
+VERB(verb_list, "list", "[TARGET[@VERSION]]", VERB_ANY, 2, VERB_DEFAULT|VERB_ONLINE_ONLY,
+     "List available targets and versions");
 static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) {
         sd_bus *bus = ASSERT_PTR(userdata);
         _cleanup_free_ char *target_path = NULL, *version = NULL;
@@ -755,6 +757,8 @@ static int check_finished(sd_bus_message *reply, void *userdata, sd_bus_error *r
         return 0;
 }
 
+VERB(verb_check, "check", "[TARGET...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,
+     "Check for updates");
 static int verb_check(int argc, char *argv[], uintptr_t _data, void *userdata) {
         sd_bus *bus = ASSERT_PTR(userdata);
         _cleanup_(table_unrefp) Table *table = NULL;
@@ -1291,6 +1295,8 @@ static int do_update(sd_bus *bus, char **targets) {
         return did_anything ? 1 : 0;
 }
 
+VERB(verb_update, "update", "[TARGET[@VERSION]...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,
+     "Install updates");
 static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) {
         sd_bus *bus = ASSERT_PTR(userdata);
         _cleanup_strv_free_ char **targets = NULL;
@@ -1344,6 +1350,8 @@ static int do_vacuum(sd_bus *bus, const char *target, const char *path) {
         return count + disabled > 0 ? 1 : 0;
 }
 
+VERB(verb_vacuum, "vacuum", "[TARGET...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,
+     "Clean up old updates");
 static int verb_vacuum(int argc, char *argv[], uintptr_t _data, void *userdata) {
         sd_bus *bus = ASSERT_PTR(userdata);
         _cleanup_strv_free_ char **targets = NULL, **target_paths = NULL;
@@ -1488,6 +1496,8 @@ static int list_features(sd_bus *bus) {
         return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend);
 }
 
+VERB(verb_features, "features", "[FEATURE]", VERB_ANY, 2, VERB_ONLINE_ONLY,
+     "List and inspect optional features on host OS");
 static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata) {
         sd_bus *bus = ASSERT_PTR(userdata);
         _cleanup_(table_unrefp) Table *table = NULL;
@@ -1537,6 +1547,10 @@ static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata
         return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, false);
 }
 
+VERB(verb_enable, "enable", "FEATURE...", 2, VERB_ANY, VERB_ONLINE_ONLY,
+     "Enable optional feature on host OS");
+VERB(verb_enable, "disable", "FEATURE...", 2, VERB_ANY, VERB_ONLINE_ONLY,
+     "Disable optional feature on host OS");
 static int verb_enable(int argc, char *argv[], uintptr_t _data, void *userdata) {
         sd_bus *bus = ASSERT_PTR(userdata);
         bool did_anything = false, enable;
@@ -1618,111 +1632,102 @@ static int verb_enable(int argc, char *argv[], uintptr_t _data, void *userdata)
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
+        _cleanup_(table_unrefp) Table *verbs = NULL, *verbs2 = NULL, *options = NULL;
         int r;
 
         r = terminal_urlify_man("updatectl", "1", &link);
         if (r < 0)
                 return log_oom();
 
-        printf("%1$s [OPTIONS...] [VERSION]\n"
-               "\n%5$sManage system updates.%6$s\n"
-               "\n%3$sCommands:%4$s\n"
-               "  list [TARGET[@VERSION]]       List available targets and versions\n"
-               "  check [TARGET...]             Check for updates\n"
-               "  update [TARGET[@VERSION]...]  Install updates\n"
-               "  vacuum [TARGET...]            Clean up old updates\n"
-               "  features [FEATURE]            List and inspect optional features on host OS\n"
-               "  enable FEATURE...             Enable optional feature on host OS\n"
-               "  disable FEATURE...            Disable optional feature on host OS\n"
-               "  -h --help                     Show this help\n"
-               "     --version                  Show package version\n"
-               "\n%3$sOptions:%4$s\n"
-               "     --reboot             Reboot after updating to newer version\n"
-               "     --offline            Do not fetch metadata from the network\n"
-               "     --now                Download/delete resources immediately\n"
-               "  -H --host=[USER@]HOST   Operate on remote host\n"
-               "     --no-pager           Do not pipe output into a pager\n"
-               "     --no-legend          Do not show the headers and footers\n"
-               "\nSee the %2$s for details.\n"
-               , program_invocation_short_name
-               , link
-               , ansi_underline(), ansi_normal()
-               , ansi_highlight(), ansi_normal()
-        );
+        r = verbs_get_help_table(&verbs);
+        if (r < 0)
+                return r;
 
-        return 0;
-}
+        r = option_parser_get_help_table_group("Verbs", &verbs2);
+        if (r < 0)
+                return r;
 
-static int parse_argv(int argc, char *argv[]) {
+        r = option_parser_get_help_table(&options);
+        if (r < 0)
+                return r;
 
-        enum {
-                ARG_VERSION = 0x100,
-                ARG_NO_PAGER,
-                ARG_NO_LEGEND,
-                ARG_REBOOT,
-                ARG_OFFLINE,
-                ARG_NOW,
-        };
+        (void) table_sync_column_widths(0, verbs, verbs2, options);
 
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'             },
-                { "version",   no_argument,       NULL, ARG_VERSION     },
-                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER    },
-                { "no-legend", no_argument,       NULL, ARG_NO_LEGEND   },
-                { "host",      required_argument, NULL, 'H'             },
-                { "reboot",    no_argument,       NULL, ARG_REBOOT      },
-                { "offline",   no_argument,       NULL, ARG_OFFLINE     },
-                { "now",       no_argument,       NULL, ARG_NOW         },
-                {}
-        };
+        printf("%s [OPTIONS...] [VERSION]\n"
+               "\n%sManage system updates.%s\n"
+               "\n%sCommands:%s\n",
+               program_invocation_short_name,
+               ansi_highlight(),
+               ansi_normal(),
+               ansi_underline(),
+               ansi_normal());
+
+        r = table_print_or_warn(verbs);
+        if (r < 0)
+                return r;
+        r = table_print_or_warn(verbs2);
+        if (r < 0)
+                return r;
+
+        printf("\n%sOptions:%s\n",
+               ansi_underline(),
+               ansi_normal());
+
+        r = table_print_or_warn(options);
+        if (r < 0)
+                return r;
+
+        printf("\nSee the %s for details.\n", link);
+        return 0;
+}
 
-        int c;
+VERB_COMMON_HELP_HIDDEN(help);
 
+static int parse_argv(int argc, char *argv[], char ***ret_args) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hH:", options, NULL)) >= 0) {
-                switch (c) {
+        OptionParser state = { argc, argv };
+        const char *arg;
 
-                case 'h':
-                        return help();
+        FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
+                switch (c) {
 
-                case ARG_VERSION:
-                        return version();
+                OPTION_LONG("reboot", NULL, "Reboot after updating to newer version"):
+                        arg_reboot = true;
+                        break;
 
-                case ARG_NO_PAGER:
-                        arg_pager_flags |= PAGER_DISABLE;
+                OPTION_LONG("offline", NULL, "Do not fetch metadata from the network"):
+                        arg_offline = true;
                         break;
 
-                case ARG_NO_LEGEND:
-                        arg_legend = false;
+                OPTION_LONG("now", NULL, "Download/delete resources immediately"):
+                        arg_now = true;
                         break;
 
-                case 'H':
+                OPTION_COMMON_HOST:
                         arg_transport = BUS_TRANSPORT_REMOTE;
-                        arg_host = optarg;
+                        arg_host = arg;
                         break;
 
-                case ARG_REBOOT:
-                        arg_reboot = true;
+                OPTION_COMMON_NO_PAGER:
+                        arg_pager_flags |= PAGER_DISABLE;
                         break;
 
-                case ARG_OFFLINE:
-                        arg_offline = true;
+                OPTION_COMMON_NO_LEGEND:
+                        arg_legend = false;
                         break;
 
-                case ARG_NOW:
-                        arg_now = true;
-                        break;
+                OPTION_GROUP("Verbs"): {}
 
-                case '?':
-                        return -EINVAL;
+                OPTION_COMMON_HELP:
+                        return help();
 
-                default:
-                        assert_not_reached();
+                OPTION_COMMON_VERSION:
+                        return version();
                 }
-        }
 
+        *ret_args = option_parser_get_args(&state);
         return 1;
 }
 
@@ -1730,23 +1735,13 @@ static int run(int argc, char *argv[]) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
-        static const Verb verbs[] = {
-                { "list",     VERB_ANY, 2,        VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list     },
-                { "check",    VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,              verb_check    },
-                { "update",   VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,              verb_update   },
-                { "vacuum",   VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,              verb_vacuum   },
-                { "features", VERB_ANY, 2,        VERB_ONLINE_ONLY,              verb_features },
-                { "enable",   2,        VERB_ANY, VERB_ONLINE_ONLY,              verb_enable   },
-                { "disable",  2,        VERB_ANY, VERB_ONLINE_ONLY,              verb_enable   },
-                {}
-        };
-
         setlocale(LC_ALL, "");
         log_setup();
 
         (void) signal(SIGWINCH, columns_lines_cache_reset);
 
-        r = parse_argv(argc, argv);
+        char **args = NULL;
+        r = parse_argv(argc, argv, &args);
         if (r <= 0)
                 return r;
 
@@ -1759,7 +1754,7 @@ static int run(int argc, char *argv[]) {
 
         (void) sd_bus_set_allow_interactive_authorization(bus, true);
 
-        return dispatch_verb(argc, argv, verbs, bus);
+        return dispatch_verb_with_args(args, bus);
 }
 
 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);