From: Zbigniew Jędrzejewski-Szmek Date: Wed, 29 Apr 2026 22:10:00 +0000 (+0200) Subject: networkctl: convert to OPTION and VERB macros X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c97084d5b0cf19e4895b05469e0c595333b9574b;p=thirdparty%2Fsystemd.git networkctl: convert to OPTION and VERB macros --help output is identical except for common options strings and whitespace. Co-developed-by: Claude Opus 4.7 --- diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 7fe34ac1eb7..e77950bbe8c 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - #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" @@ -16,10 +16,10 @@ #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);