]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkctl: convert to OPTION and VERB macros
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Wed, 29 Apr 2026 22:10:00 +0000 (00:10 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 5 May 2026 11:49:52 +0000 (13:49 +0200)
--help output is identical except for common options strings and
whitespace.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
src/network/networkctl.c

index 7fe34ac1eb77827117221bed870629bdbbbc5673..e77950bbe8c4f9bf69499735d5d24c33b0f36071 100644 (file)
@@ -1,11 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <getopt.h>
-
 #include "sd-json.h"
 
 #include "alloc-util.h"
 #include "build.h"
+#include "format-table.h"
+#include "help-util.h"
 #include "log.h"
 #include "logs-show.h"
 #include "main-func.h"
 #include "networkctl-lldp.h"
 #include "networkctl-misc.h"
 #include "networkctl-status-link.h"
+#include "options.h"
 #include "parse-argument.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "pretty-print.h"
 #include "string-util.h"
 #include "verbs.h"
 
@@ -38,144 +38,146 @@ bool arg_ask_password = true;
 
 STATIC_DESTRUCTOR_REGISTER(arg_drop_in, freep);
 
+VERB_SCOPE(, verb_list_links,                 "list",               "[PATTERN...]",  VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY,
+           "List links");
+VERB_SCOPE(, verb_link_status,                "status",             "[PATTERN...]",  VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,
+           "Show link status");
+VERB_SCOPE(, verb_link_lldp_status,           "lldp",               "[PATTERN...]",  VERB_ANY, VERB_ANY, 0,
+           "Show LLDP neighbors");
+VERB_SCOPE(, verb_list_address_labels,        "label",              NULL,            1,        1,        0,
+           "Show current address label entries in the kernel");
+VERB_SCOPE(, verb_link_delete,                "delete",             "DEVICES...",    2,        VERB_ANY, 0,
+           "Delete virtual netdevs");
+VERB_SCOPE(, verb_link_varlink_simple_method, "up",                 "DEVICES...",    2,        VERB_ANY, 0,
+           "Bring devices up");
+VERB_SCOPE(, verb_link_varlink_simple_method, "down",               "DEVICES...",    2,        VERB_ANY, 0,
+           "Bring devices down");
+VERB_SCOPE(, verb_link_varlink_simple_method, "renew",              "DEVICES...",    2,        VERB_ANY, VERB_ONLINE_ONLY,
+           "Renew dynamic configurations");
+VERB_SCOPE(, verb_link_varlink_simple_method, "forcerenew",         "DEVICES...",    2,        VERB_ANY, VERB_ONLINE_ONLY,
+           "Trigger DHCP reconfiguration of all connected clients");
+VERB_SCOPE(, verb_link_varlink_simple_method, "reconfigure",        "DEVICES...",    2,        VERB_ANY, VERB_ONLINE_ONLY,
+           "Reconfigure interfaces");
+VERB_SCOPE(, verb_reload,                     "reload",             NULL,            1,        1,        VERB_ONLINE_ONLY,
+           "Reload .network and .netdev files");
+VERB_SCOPE(, verb_edit,                       "edit",               "FILES|DEVICES...",   2,   VERB_ANY, 0,
+           "Edit network configuration files");
+VERB_SCOPE(, verb_cat,                        "cat",                "[FILES|DEVICES...]", 1,   VERB_ANY, 0,
+           "Show network configuration files");
+VERB_SCOPE(, verb_mask,                       "mask",               "FILES...",      2,        VERB_ANY, 0,
+           "Mask network configuration files");
+VERB_SCOPE(, verb_unmask,                     "unmask",             "FILES...",      2,        VERB_ANY, 0,
+           "Unmask network configuration files");
+VERB_SCOPE(, verb_persistent_storage,         "persistent-storage", "BOOL",          2,        2,        0,
+           "Notify systemd-networkd if persistent storage is ready");
+
 static int help(void) {
-        _cleanup_free_ char *link = NULL;
+        _cleanup_(table_unrefp) Table *verbs = NULL, *options = NULL;
         int r;
 
-        r = terminal_urlify_man("networkctl", "1", &link);
+        r = verbs_get_help_table(&verbs);
+        if (r < 0)
+                return r;
+
+        r = option_parser_get_help_table(&options);
+        if (r < 0)
+                return r;
+
+        (void) table_sync_column_widths(0, verbs, options);
+
+        help_cmdline("[OPTIONS...] COMMAND");
+        help_abstract("Query and control the networking subsystem.");
+
+        help_section("Commands");
+        r = table_print_or_warn(verbs);
+        if (r < 0)
+                return r;
+
+        help_section("Options");
+        r = table_print_or_warn(options);
         if (r < 0)
-                return log_oom();
-
-        printf("%s [OPTIONS...] COMMAND\n\n"
-               "%sQuery and control the networking subsystem.%s\n"
-               "\nCommands:\n"
-               "  list [PATTERN...]      List links\n"
-               "  status [PATTERN...]    Show link status\n"
-               "  lldp [PATTERN...]      Show LLDP neighbors\n"
-               "  label                  Show current address label entries in the kernel\n"
-               "  delete DEVICES...      Delete virtual netdevs\n"
-               "  up DEVICES...          Bring devices up\n"
-               "  down DEVICES...        Bring devices down\n"
-               "  renew DEVICES...       Renew dynamic configurations\n"
-               "  forcerenew DEVICES...  Trigger DHCP reconfiguration of all connected clients\n"
-               "  reconfigure DEVICES... Reconfigure interfaces\n"
-               "  reload                 Reload .network and .netdev files\n"
-               "  edit FILES|DEVICES...  Edit network configuration files\n"
-               "  cat [FILES|DEVICES...] Show network configuration files\n"
-               "  mask FILES...          Mask network configuration files\n"
-               "  unmask FILES...        Unmask network configuration files\n"
-               "  persistent-storage BOOL\n"
-               "                         Notify systemd-networkd if persistent storage is ready\n"
-               "\nOptions:\n"
-               "  -h --help              Show this help\n"
-               "     --version           Show package version\n"
-               "     --no-pager          Do not pipe output into a pager\n"
-               "     --no-legend         Do not show the headers and footers\n"
-               "     --no-ask-password   Do not prompt for password\n"
-               "  -a --all               Show status for all links\n"
-               "  -s --stats             Show detailed link statistics\n"
-               "  -l --full              Do not ellipsize output\n"
-               "  -n --lines=INTEGER     Number of journal entries to show\n"
-               "     --json=pretty|short|off\n"
-               "                         Generate JSON output\n"
-               "     --no-reload         Do not reload systemd-networkd or systemd-udevd\n"
-               "                         after editing network config\n"
-               "     --drop-in=NAME      Edit specified drop-in instead of main config file\n"
-               "     --runtime           Edit runtime config files\n"
-               "     --stdin             Read new contents of edited file from stdin\n"
-               "\nSee the %s for details.\n",
-               program_invocation_short_name,
-               ansi_highlight(),
-               ansi_normal(),
-               link);
+                return r;
 
+        help_man_page_reference("networkctl", "1");
         return 0;
 }
 
-static int parse_argv(int argc, char *argv[]) {
-        enum {
-                ARG_VERSION = 0x100,
-                ARG_NO_PAGER,
-                ARG_NO_LEGEND,
-                ARG_NO_ASK_PASSWORD,
-                ARG_JSON,
-                ARG_NO_RELOAD,
-                ARG_DROP_IN,
-                ARG_RUNTIME,
-                ARG_STDIN,
-        };
-
-        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       },
-                { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
-                { "all",             no_argument,       NULL, 'a'                 },
-                { "stats",           no_argument,       NULL, 's'                 },
-                { "full",            no_argument,       NULL, 'l'                 },
-                { "lines",           required_argument, NULL, 'n'                 },
-                { "json",            required_argument, NULL, ARG_JSON            },
-                { "no-reload",       no_argument,       NULL, ARG_NO_RELOAD       },
-                { "drop-in",         required_argument, NULL, ARG_DROP_IN         },
-                { "runtime",         no_argument,       NULL, ARG_RUNTIME         },
-                { "stdin",           no_argument,       NULL, ARG_STDIN           },
-                {}
-        };
-
-        int c, r;
+VERB_COMMON_HELP_HIDDEN(help);
+
+static int parse_argv(int argc, char *argv[], char ***remaining_args) {
+        int r;
 
         assert(argc >= 0);
         assert(argv);
+        assert(remaining_args);
 
-        while ((c = getopt_long(argc, argv, "hasln:", options, NULL)) >= 0) {
+        OptionParser opts = { argc, argv };
 
+        FOREACH_OPTION_OR_RETURN(c, &opts)
                 switch (c) {
 
-                case 'h':
+                OPTION_COMMON_HELP:
                         return help();
 
-                case ARG_VERSION:
+                OPTION_COMMON_VERSION:
                         return version();
 
-                case ARG_NO_PAGER:
+                OPTION_COMMON_NO_PAGER:
                         arg_pager_flags |= PAGER_DISABLE;
                         break;
 
-                case ARG_NO_LEGEND:
+                OPTION_COMMON_NO_LEGEND:
                         arg_legend = false;
                         break;
 
-                case ARG_NO_RELOAD:
-                        arg_no_reload = true;
+                OPTION_COMMON_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
                         break;
 
-                case ARG_NO_ASK_PASSWORD:
-                        arg_ask_password = false;
+                OPTION('a', "all", NULL, "Show status for all links"):
+                        arg_all = true;
                         break;
 
-                case ARG_RUNTIME:
-                        arg_runtime = true;
+                OPTION('s', "stats", NULL, "Show detailed link statistics"):
+                        arg_stats = true;
                         break;
 
-                case ARG_STDIN:
-                        arg_stdin = true;
+                OPTION('l', "full", NULL, "Do not ellipsize output"):
+                        arg_full = true;
+                        break;
+
+                OPTION('n', "lines", "INTEGER", "Number of journal entries to show"):
+                        if (safe_atou(opts.arg, &arg_lines) < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Failed to parse lines '%s'", opts.arg);
                         break;
 
-                case ARG_DROP_IN:
-                        if (isempty(optarg))
+                OPTION_COMMON_JSON:
+                        r = parse_json_argument(opts.arg, &arg_json_format_flags);
+                        if (r <= 0)
+                                return r;
+                        break;
+
+                OPTION_LONG("no-reload", NULL,
+                            "Do not reload systemd-networkd or systemd-udevd after editing network config"):
+                        arg_no_reload = true;
+                        break;
+
+                OPTION_LONG("drop-in", "NAME",
+                            "Edit specified drop-in instead of main config file"):
+                        if (isempty(opts.arg))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty drop-in file name.");
 
-                        if (!endswith(optarg, ".conf")) {
+                        if (!endswith(opts.arg, ".conf")) {
                                 char *conf;
 
-                                conf = strjoin(optarg, ".conf");
+                                conf = strjoin(opts.arg, ".conf");
                                 if (!conf)
                                         return log_oom();
 
                                 free_and_replace(arg_drop_in, conf);
                         } else {
-                                r = free_and_strdup(&arg_drop_in, optarg);
+                                r = free_and_strdup(&arg_drop_in, opts.arg);
                                 if (r < 0)
                                         return log_oom();
                         }
@@ -186,77 +188,32 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
-                case 'a':
-                        arg_all = true;
-                        break;
-
-                case 's':
-                        arg_stats = true;
-                        break;
-
-                case 'l':
-                        arg_full = true;
-                        break;
-
-                case 'n':
-                        if (safe_atou(optarg, &arg_lines) < 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Failed to parse lines '%s'", optarg);
+                OPTION_LONG("runtime", NULL, "Edit runtime config files"):
+                        arg_runtime = true;
                         break;
 
-                case ARG_JSON:
-                        r = parse_json_argument(optarg, &arg_json_format_flags);
-                        if (r <= 0)
-                                return r;
+                OPTION_LONG("stdin", NULL, "Read new contents of edited file from stdin"):
+                        arg_stdin = true;
                         break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        assert_not_reached();
                 }
-        }
 
+        *remaining_args = option_parser_get_args(&opts);
         return 1;
 }
 
-static int networkctl_main(int argc, char *argv[]) {
-        static const Verb verbs[] = {
-                { "list",               VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list_links                    },
-                { "status",             VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,              verb_link_status                   },
-                { "lldp",               VERB_ANY, VERB_ANY, 0,                             verb_link_lldp_status              },
-                { "label",              1,        1,        0,                             verb_list_address_labels           },
-                { "delete",             2,        VERB_ANY, 0,                             verb_link_delete                   },
-                { "up",                 2,        VERB_ANY, 0,                             verb_link_varlink_simple_method    },
-                { "down",               2,        VERB_ANY, 0,                             verb_link_varlink_simple_method    },
-                { "renew",              2,        VERB_ANY, VERB_ONLINE_ONLY,              verb_link_varlink_simple_method    },
-                { "forcerenew",         2,        VERB_ANY, VERB_ONLINE_ONLY,              verb_link_varlink_simple_method    },
-                { "reconfigure",        2,        VERB_ANY, VERB_ONLINE_ONLY,              verb_link_varlink_simple_method    },
-                { "reload",             1,        1,        VERB_ONLINE_ONLY,              verb_reload                        },
-                { "edit",               2,        VERB_ANY, 0,                             verb_edit                          },
-                { "cat",                1,        VERB_ANY, 0,                             verb_cat                           },
-                { "mask",               2,        VERB_ANY, 0,                             verb_mask                          },
-                { "unmask",             2,        VERB_ANY, 0,                             verb_unmask                        },
-                { "persistent-storage", 2,        2,        0,                             verb_persistent_storage            },
-                {}
-        };
-
-        return dispatch_verb(argc, argv, verbs, NULL);
-}
-
 static int run(int argc, char* argv[]) {
+        char **args = NULL;
         int r;
 
         log_setup();
 
-        r = parse_argv(argc, argv);
+        r = parse_argv(argc, argv, &args);
         if (r <= 0)
                 return r;
 
         journal_browse_prepare();
 
-        return networkctl_main(argc, argv);
+        return dispatch_verb_with_args(args, NULL);
 }
 
 DEFINE_MAIN_FUNCTION(run);