diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 42e3f6df29..7052fd000b 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include #include @@ -18,6 +17,7 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" +#include "format-table.h" #include "glob-util.h" #include "hostname-setup.h" #include "hostname-util.h" @@ -28,6 +28,7 @@ #include "main-func.h" #include "memory-util.h" #include "microhttpd-util.h" +#include "options.h" #include "os-util.h" #include "output-mode.h" #include "parse-util.h" @@ -1090,92 +1091,52 @@ static mhd_result request_handler( static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-journal-gatewayd.service", "8", &link); if (r < 0) return log_oom(); - printf("%s [OPTIONS...] ...\n\n" - "HTTP server for journal events.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --cert=CERT.PEM Server certificate in PEM format\n" - " --key=KEY.PEM Server key in PEM format\n" - " --trust=CERT.PEM Certificate authority certificate in PEM format\n" - " --system Serve system journal\n" - " --user Serve the user journal for the current user\n" - " -m --merge Serve all available journals\n" - " -D --directory=PATH Serve journal files in directory\n" - " --file=PATH Serve this journal file\n" - "\nSee the %s for details.\n", - program_invocation_short_name, - link); + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + printf("%s [OPTIONS...] ...\n\n" + "HTTP server for journal events.\n\n", + program_invocation_short_name); + + r = table_print_or_warn(options); + if (r < 0) + return r; + + printf("\nSee the %s for details.\n", link); return 0; } static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_KEY, - ARG_CERT, - ARG_TRUST, - ARG_USER, - ARG_SYSTEM, - ARG_MERGE, - ARG_FILE, - }; - - int r, c; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "key", required_argument, NULL, ARG_KEY }, - { "cert", required_argument, NULL, ARG_CERT }, - { "trust", required_argument, NULL, ARG_TRUST }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "merge", no_argument, NULL, 'm' }, - { "directory", required_argument, NULL, 'D' }, - { "file", required_argument, NULL, ARG_FILE }, - {} - }; - assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hD:", options, NULL)) >= 0) + OptionParser state = { argc, argv }; + const char *arg; + int r; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case ARG_KEY: - if (arg_key_pem) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Key file specified twice"); - r = read_full_file_full( - AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &arg_key_pem, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read key file: %m"); - assert(arg_key_pem); - break; - - case ARG_CERT: + OPTION_LONG("cert", "CERT.PEM", "Server certificate in PEM format"): if (arg_cert_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Certificate file specified twice"); r = read_full_file_full( - AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + AT_FDCWD, arg, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_cert_pem, NULL); @@ -1184,13 +1145,27 @@ static int parse_argv(int argc, char *argv[]) { assert(arg_cert_pem); break; - case ARG_TRUST: + OPTION_LONG("key", "KEY.PEM", "Server key in PEM format"): + if (arg_key_pem) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Key file specified twice"); + r = read_full_file_full( + AT_FDCWD, arg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &arg_key_pem, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read key file: %m"); + assert(arg_key_pem); + break; + + OPTION_LONG("trust", "CERT.PEM", "Certificate authority certificate in PEM format"): #if HAVE_GNUTLS if (arg_trust_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "CA certificate file specified twice"); r = read_full_file_full( - AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + AT_FDCWD, arg, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_trust_pem, NULL); @@ -1203,38 +1178,32 @@ static int parse_argv(int argc, char *argv[]) { "Option --trust= is not available."); #endif - case ARG_SYSTEM: + OPTION_LONG("system", NULL, "Serve system journal"): arg_journal_type |= SD_JOURNAL_SYSTEM; break; - case ARG_USER: + OPTION_LONG("user", NULL, "Serve the user journal for the current user"): arg_journal_type |= SD_JOURNAL_CURRENT_USER; break; - case 'm': + OPTION('m', "merge", NULL, "Serve all available journals"): arg_merge = true; break; - case 'D': - r = free_and_strdup_warn(&arg_directory, optarg); + OPTION('D', "directory", "PATH", "Serve journal files in directory"): + r = free_and_strdup_warn(&arg_directory, arg); if (r < 0) return r; break; - case ARG_FILE: - r = glob_extend(&arg_file, optarg, GLOB_NOCHECK); + OPTION_LONG("file", "PATH", "Serve this journal file"): + r = glob_extend(&arg_file, arg, GLOB_NOCHECK); if (r < 0) return log_error_errno(r, "Failed to add paths: %m"); break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - if (optind < argc) + if (option_parser_get_n_args(&state) > 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program does not take arguments."); diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index 01f27dcded..5b4d6f451e 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -100,15 +100,13 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { OPTION_COMMON_VERSION: return version(); - OPTION_LONG("private-key", "KEY", "Private key in PEM format"): + OPTION_COMMON_PRIVATE_KEY("Private key in PEM format"): r = free_and_strdup_warn(&arg_private_key, arg); if (r < 0) return r; break; - OPTION_LONG("private-key-source", "SOURCE", - "Specify how to use KEY for --private-key= " - "(file, provider:PROVIDER, engine:ENGINE)"): + OPTION_COMMON_PRIVATE_KEY_SOURCE: r = parse_openssl_key_source_argument( arg, &arg_private_key_source, @@ -117,17 +115,13 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { return r; break; - OPTION_LONG("certificate", "PATH|URI", - "PEM certificate to use for signing, " - "or a provider-specific designation if --certificate-source= is used"): + OPTION_COMMON_CERTIFICATE("PEM certificate to use for signing"): r = free_and_strdup_warn(&arg_certificate, arg); if (r < 0) return r; break; - OPTION_LONG("certificate-source", "SOURCE", - "Specify how to interpret the certificate from --certificate= " - "(file, provider:PROVIDER)"): + OPTION_COMMON_CERTIFICATE_SOURCE: r = parse_openssl_certificate_source_argument( arg, &arg_certificate_source, @@ -226,7 +220,7 @@ static int verb_validate(int argc, char *argv[], uintptr_t _data, void *userdata VERB_NOARG(verb_extract_public, "extract-public", "Extract a public key"); -VERB(verb_extract_public, "public", NULL, VERB_ANY, 1, 0, NULL); /* Deprecated alias */ +VERB_NOARG(verb_extract_public, "public", /* help= */ NULL); /* Deprecated but kept for backward compat. */ static int verb_extract_public(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL; int r; diff --git a/src/login/inhibit.c b/src/login/inhibit.c index 493f06f24e..4002d3fe4a 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -18,6 +17,7 @@ #include "format-table.h" #include "log.h" #include "main-func.h" +#include "options.h" #include "pager.h" #include "parse-argument.h" #include "pidref.h" @@ -170,139 +170,100 @@ static int print_inhibitors(sd_bus *bus) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-inhibit", "1", &link); if (r < 0) return log_oom(); - printf("%s [OPTIONS...] COMMAND ...\n" - "\n%sExecute a process while inhibiting shutdown/sleep/idle.%s\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-ask-password Do not attempt interactive authorization\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --json=pretty|short|off\n" - " Generate JSON output\n" - " --what=WHAT Operations to inhibit, colon separated list of:\n" - " shutdown, sleep, idle, handle-power-key,\n" - " handle-suspend-key, handle-hibernate-key,\n" - " handle-lid-switch\n" - " --who=STRING A descriptive string who is inhibiting\n" - " --why=STRING A descriptive string why is being inhibited\n" - " --mode=MODE One of block, block-weak, or delay\n" - " --list List active inhibitors\n" - "\nSee the %s for details.\n", + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + + printf("%s [OPTIONS...] COMMAND ...\n\n" + "%sExecute a process while inhibiting shutdown/sleep/idle.%s\n\n", program_invocation_short_name, ansi_highlight(), - ansi_normal(), - link); + ansi_normal()); + r = table_print_or_warn(options); + if (r < 0) + return r; + + printf("\nSee the %s for details.\n", link); return 0; } -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_WHAT, - ARG_WHO, - ARG_WHY, - ARG_MODE, - ARG_LIST, - ARG_NO_ASK_PASSWORD, - ARG_NO_PAGER, - ARG_NO_LEGEND, - ARG_JSON, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "what", required_argument, NULL, ARG_WHAT }, - { "who", required_argument, NULL, ARG_WHO }, - { "why", required_argument, NULL, ARG_WHY }, - { "mode", required_argument, NULL, ARG_MODE }, - { "list", no_argument, NULL, ARG_LIST }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "json", required_argument, NULL, ARG_JSON }, - {} - }; - - int c, r; - +static int parse_argv(int argc, char *argv[], char ***remaining_args) { assert(argc >= 0); assert(argv); + assert(remaining_args); - /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long() - * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */ - optind = 0; - while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) + OptionParser state = { argc, argv, OPTION_PARSER_STOP_AT_FIRST_NONOPTION }; + const char *arg; + int r; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case ARG_WHAT: - arg_what = optarg; - break; - - case ARG_WHO: - arg_who = optarg; - break; - - case ARG_WHY: - arg_why = optarg; - break; - - case ARG_MODE: - arg_mode = optarg; - break; - - case ARG_LIST: - arg_action = ACTION_LIST; - break; - - case ARG_NO_ASK_PASSWORD: + OPTION_COMMON_NO_ASK_PASSWORD: arg_ask_password = false; break; - 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_JSON: - r = parse_json_argument(optarg, &arg_json_format_flags); + OPTION_COMMON_JSON: + r = parse_json_argument(arg, &arg_json_format_flags); if (r <= 0) return r; - break; - case '?': - return -EINVAL; + OPTION_LONG("what", "WHAT", + "Operations to inhibit, colon separated list " + "(shutdown, sleep, idle, handle-power-key, " + "handle-suspend-key, handle-hibernate-key, " + "handle-lid-switch)"): + arg_what = arg; + break; - default: - assert_not_reached(); + OPTION_LONG("who", "STRING", + "A descriptive string who is inhibiting"): + arg_who = arg; + break; + + OPTION_LONG("why", "STRING", + "A descriptive string why is being inhibited"): + arg_why = arg; + break; + + OPTION_LONG("mode", "MODE", "One of block, block-weak, or delay"): + arg_mode = arg; + break; + + OPTION_LONG("list", NULL, "List active inhibitors"): + arg_action = ACTION_LIST; + break; } - if (arg_action == ACTION_INHIBIT && optind == argc) + char **args = option_parser_get_args(&state); + + if (arg_action == ACTION_INHIBIT && strv_isempty(args)) arg_action = ACTION_LIST; - else if (arg_action == ACTION_INHIBIT && optind >= argc) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Missing command line to execute."); - + *remaining_args = args; return 1; } @@ -312,7 +273,8 @@ static int run(int argc, char *argv[]) { log_setup(); - r = parse_argv(argc, argv); + char **args = NULL; + r = parse_argv(argc, argv, &args); if (r <= 0) return r; @@ -337,7 +299,7 @@ static int run(int argc, char *argv[]) { arg_what = "idle:sleep:shutdown"; if (!arg_who) { - w = strv_join(argv + optind, " "); + w = strv_join(args, " "); if (!w) return log_oom(); @@ -354,7 +316,7 @@ static int run(int argc, char *argv[]) { if (fd < 0) return log_error_errno(fd, "Failed to inhibit: %s", bus_error_message(&error, fd)); - arguments = strv_copy(argv + optind); + arguments = strv_copy(args); if (!arguments) return log_oom(); @@ -370,7 +332,7 @@ static int run(int argc, char *argv[]) { _exit(EXIT_FAILURE); } - return pidref_wait_for_terminate_and_check(argv[optind], &pidref, WAIT_LOG_ABNORMAL); + return pidref_wait_for_terminate_and_check(args[0], &pidref, WAIT_LOG_ABNORMAL); } } diff --git a/src/ptyfwd/ptyfwd-tool.c b/src/ptyfwd/ptyfwd-tool.c index 519bd3641d..089c84a40c 100644 --- a/src/ptyfwd/ptyfwd-tool.c +++ b/src/ptyfwd/ptyfwd-tool.c @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include "alloc-util.h" #include "build.h" #include "event-util.h" #include "fd-util.h" +#include "format-table.h" #include "log.h" #include "main-func.h" +#include "options.h" #include "parse-argument.h" #include "pidref.h" #include "pretty-print.h" @@ -28,95 +29,77 @@ STATIC_DESTRUCTOR_REGISTER(arg_title, freep); static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-pty-forward", "1", &link); if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...] COMMAND ...\n" - "\n%5$sRun command with a custom terminal background color or title.%6$s\n" - "\n%3$sOptions:%4$s\n" - " -h --help Show this help\n" - " --version Print version\n" - " -q --quiet Suppress information messages during runtime\n" - " --read-only Do not accept any user input on stdin\n" - " --background=COLOR Set ANSI color for background\n" - " --title=TITLE Set terminal title\n" - "\nSee the %2$s for details.\n", + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + + printf("%s [OPTIONS...] COMMAND ...\n" + "\n%sRun command with a custom terminal background color or title.%s\n" + "\n%sOptions:%s\n", program_invocation_short_name, - link, - ansi_underline(), - ansi_normal(), ansi_highlight(), + ansi_normal(), + 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; } -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_READ_ONLY, - ARG_BACKGROUND, - ARG_TITLE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "quiet", no_argument, NULL, 'q' }, - { "read-only", no_argument, NULL, ARG_READ_ONLY }, - { "background", required_argument, NULL, ARG_BACKGROUND }, - { "title", required_argument, NULL, ARG_TITLE }, - {} - }; - - int c, r; - +static int parse_argv(int argc, char *argv[], char ***remaining_args) { assert(argc >= 0); assert(argv); + assert(remaining_args); - optind = 0; - while ((c = getopt_long(argc, argv, "+hq", options, NULL)) >= 0) + OptionParser state = { argc, argv, OPTION_PARSER_STOP_AT_FIRST_NONOPTION }; + const char *arg; + int r; + + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case 'q': + OPTION('q', "quiet", NULL, "Suppress information messages during runtime"): arg_quiet = true; break; - case ARG_READ_ONLY: + OPTION_LONG("read-only", NULL, "Do not accept any user input on stdin"): arg_read_only = true; break; - case ARG_BACKGROUND: - r = parse_background_argument(optarg, &arg_background); + OPTION_LONG("background", "COLOR", "Set ANSI color for background"): + r = parse_background_argument(arg, &arg_background); if (r < 0) return r; break; - case ARG_TITLE: - r = free_and_strdup_warn(&arg_title, optarg); + OPTION_LONG("title", "TITLE", "Set terminal title"): + r = free_and_strdup_warn(&arg_title, arg); if (r < 0) return r; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - if (optind >= argc) + if (option_parser_get_n_args(&state) == 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected command line, refusing."); + *remaining_args = option_parser_get_args(&state); return 1; } @@ -155,11 +138,12 @@ static int run(int argc, char *argv[]) { log_setup(); - r = parse_argv(argc, argv); + char **args = NULL; + r = parse_argv(argc, argv, &args); if (r <= 0) return r; - _cleanup_strv_free_ char **l = strv_copy(argv + optind); + _cleanup_strv_free_ char **l = strv_copy(args); if (!l) return log_oom(); diff --git a/src/socket-activate/socket-activate.c b/src/socket-activate/socket-activate.c index e7102b62a1..2f0c81a111 100644 --- a/src/socket-activate/socket-activate.c +++ b/src/socket-activate/socket-activate.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include #include @@ -14,9 +13,11 @@ #include "errno-util.h" #include "escape.h" #include "fd-util.h" +#include "format-table.h" #include "format-util.h" #include "log.h" #include "main-func.h" +#include "options.h" #include "pidfd-util.h" #include "pidref.h" #include "pretty-print.h" @@ -320,83 +321,63 @@ static int install_chld_handler(void) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-socket-activate", "1", &link); if (r < 0) return log_oom(); + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + printf("%s [OPTIONS...] COMMAND ...\n" "\n%sListen on sockets and launch child on connection.%s\n" - "\nOptions:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " -l --listen=ADDR Listen for raw connections at ADDR\n" - " -d --datagram Listen on datagram instead of stream socket\n" - " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n" - " -a --accept Spawn separate child for each connection\n" - " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n" - " --fdname=NAME[:NAME...] Specify names for file descriptors\n" - " --inetd Enable inetd file descriptor passing protocol\n" - " --now Start instantly instead of waiting for connection\n" - "\nNote: file descriptors from sd_listen_fds() will be passed through.\n" - "\nSee the %s for details.\n", + "\n%sOptions:%s\n", program_invocation_short_name, ansi_highlight(), ansi_normal(), - link); + ansi_underline(), + ansi_normal()); + r = table_print_or_warn(options); + if (r < 0) + return r; + + printf("\nNote: file descriptors from sd_listen_fds() will be passed through.\n" + "\nSee the %s for details.\n", link); return 0; } -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_FDNAME, - ARG_SEQPACKET, - ARG_INETD, - ARG_NOW, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "datagram", no_argument, NULL, 'd' }, - { "seqpacket", no_argument, NULL, ARG_SEQPACKET }, - { "listen", required_argument, NULL, 'l' }, - { "accept", no_argument, NULL, 'a' }, - { "setenv", required_argument, NULL, 'E' }, - { "environment", required_argument, NULL, 'E' }, /* legacy alias */ - { "fdname", required_argument, NULL, ARG_FDNAME }, - { "inetd", no_argument, NULL, ARG_INETD }, - { "now", no_argument, NULL, ARG_NOW }, - {} - }; - - int c, r; - +static int parse_argv(int argc, char *argv[], char ***remaining_args) { assert(argc >= 0); assert(argv); + assert(remaining_args); - /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long() - * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */ - optind = 0; - while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0) + OptionParser state = { argc, argv, OPTION_PARSER_STOP_AT_FIRST_NONOPTION }; + const char *arg; + int r; + + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case 'l': - r = strv_extend(&arg_listen, optarg); + OPTION('l', "listen", "ADDR", + "Listen for raw connections at ADDR"): + r = strv_extend(&arg_listen, arg); if (r < 0) return log_oom(); break; - case 'd': + OPTION('d', "datagram", NULL, + "Listen on datagram instead of stream socket"): if (arg_socket_type == SOCK_SEQPACKET) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--datagram may not be combined with --seqpacket."); @@ -404,7 +385,8 @@ static int parse_argv(int argc, char *argv[]) { arg_socket_type = SOCK_DGRAM; break; - case ARG_SEQPACKET: + OPTION_LONG("seqpacket", NULL, + "Listen on SOCK_SEQPACKET instead of stream socket"): if (arg_socket_type == SOCK_DGRAM) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--seqpacket may not be combined with --datagram."); @@ -412,20 +394,24 @@ static int parse_argv(int argc, char *argv[]) { arg_socket_type = SOCK_SEQPACKET; break; - case 'a': + OPTION('a', "accept", NULL, + "Spawn separate child for each connection"): arg_accept = true; break; - case 'E': - r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg); + OPTION('E', "setenv", "NAME[=VALUE]", + "Pass an environment variable to children"): {} + OPTION_LONG("environment", "NAME[=VALUE]", /* help= */ NULL): /* legacy alias */ + r = strv_env_replace_strdup_passthrough(&arg_setenv, arg); if (r < 0) - return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg); + return log_error_errno(r, "Cannot assign environment variable %s: %m", arg); break; - case ARG_FDNAME: { + OPTION_LONG("fdname", "NAME[:NAME...]", + "Specify names for file descriptors"): { _cleanup_strv_free_ char **names = NULL; - names = strv_split(optarg, ":"); + names = strv_split(arg, ":"); if (!names) return log_oom(); @@ -446,22 +432,19 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_INETD: + OPTION_LONG("inetd", NULL, + "Enable inetd file descriptor passing protocol"): arg_inetd = true; break; - case ARG_NOW: + OPTION_LONG("now", NULL, + "Start instantly instead of waiting for connection"): arg_now = true; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - if (optind == argc) + *remaining_args = option_parser_get_args(&state); + if (strv_isempty(*remaining_args)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s: command to execute is missing.", program_invocation_short_name); @@ -488,11 +471,12 @@ static int run(int argc, char **argv) { log_setup(); - r = parse_argv(argc, argv); + char **args = NULL; + r = parse_argv(argc, argv, &args); if (r <= 0) return r; - exec_argv = strv_copy(argv + optind); + exec_argv = strv_copy(args); if (!exec_argv) return log_oom(); diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c index 7a4596e472..2b0ed742c1 100644 --- a/src/storagetm/storagetm.c +++ b/src/storagetm/storagetm.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include #include @@ -17,12 +16,14 @@ #include "device-util.h" #include "fd-util.h" #include "fileio.h" +#include "format-table.h" #include "fs-util.h" #include "hashmap.h" #include "id128-util.h" #include "local-addresses.h" #include "main-func.h" #include "mountpoint-util.h" +#include "options.h" #include "os-util.h" #include "path-util.h" #include "plymouth-util.h" @@ -47,99 +48,86 @@ STATIC_DESTRUCTOR_REGISTER(arg_nqn, freep); static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-storagetm", "8", &link); if (r < 0) return log_oom(); + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + printf("%s [OPTIONS...] [DEVICE...]\n" - "\n%sExpose a block device or regular file as NVMe-TCP volume.%s\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --nqn=STRING Select NQN (NVMe Qualified Name)\n" - " -a --all Expose all devices\n" - " --list-devices List candidate block devices to operate on\n" - "\nSee the %s for details.\n", + "\n%sExpose a block device or regular file as NVMe-TCP volume.%s\n\n", program_invocation_short_name, ansi_highlight(), - ansi_normal(), - link); + ansi_normal()); + r = table_print_or_warn(options); + if (r < 0) + return r; + + printf("\nSee the %s for details.\n", link); return 0; } static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_NQN = 0x100, - ARG_VERSION, - ARG_LIST_DEVICES, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "nqn", required_argument, NULL, ARG_NQN }, - { "all", no_argument, NULL, 'a' }, - { "list-devices", no_argument, NULL, ARG_LIST_DEVICES }, - {} - }; - - int r, c; - assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) + OptionParser state = { argc, argv }; + const char *arg; + int r; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case ARG_NQN: - if (!filename_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "NQN invalid: %s", optarg); + OPTION_LONG("nqn", "STRING", + "Select NQN (NVMe Qualified Name)"): + if (!filename_is_valid(arg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "NQN invalid: %s", arg); - if (free_and_strdup(&arg_nqn, optarg) < 0) + if (free_and_strdup(&arg_nqn, arg) < 0) return log_oom(); break; - case 'a': + OPTION('a', "all", NULL, "Expose all devices"): arg_all++; break; - case ARG_LIST_DEVICES: + OPTION_LONG("list-devices", NULL, + "List candidate block devices to operate on"): r = blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_IGNORE_ZRAM, /* ret_devices= */ NULL, /* ret_n_devices= */ NULL); if (r < 0) return r; return 0; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } + char **args = option_parser_get_args(&state); if (arg_all > 0) { - if (argc > optind) + if (!strv_isempty(args)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expects no further arguments if --all/-a is specified."); } else { - if (optind >= argc) + if (strv_isempty(args)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expecting device name or --all/-a."); - for (int i = optind; i < argc; i++) - if (!path_is_valid(argv[i])) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid path: %s", argv[i]); + STRV_FOREACH(a, args) + if (!path_is_valid(*a)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid path: %s", *a); - arg_devices = strv_copy(argv + optind); + arg_devices = strv_copy(args); + if (!arg_devices) + return log_oom(); } if (!arg_nqn) { diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index cf7dea24f5..39041ea384 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include @@ -10,10 +9,12 @@ #include "constants.h" #include "creds-util.h" #include "errno-util.h" +#include "format-table.h" #include "glob-util.h" #include "hashmap.h" #include "log.h" #include "main-func.h" +#include "options.h" #include "pager.h" #include "path-util.h" #include "pretty-print.h" @@ -29,13 +30,13 @@ static PagerFlags arg_pager_flags = 0; STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep); -typedef struct Option { +typedef struct SysctlOption { char *key; char *value; bool ignore_failure; -} Option; +} SysctlOption; -static Option* option_free(Option *o) { +static SysctlOption* sysctl_option_free(SysctlOption *o) { if (!o) return NULL; @@ -45,11 +46,11 @@ static Option* option_free(Option *o) { return mfree(o); } -DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(SysctlOption*, sysctl_option_free); DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( - option_hash_ops, + sysctl_option_hash_ops, char, string_hash_func, string_compare_func, - Option, option_free); + SysctlOption, sysctl_option_free); static bool test_prefix(const char *p) { if (strv_isempty(arg_prefixes)) @@ -58,20 +59,20 @@ static bool test_prefix(const char *p) { return path_startswith_strv(p, arg_prefixes); } -static Option* option_new( +static SysctlOption* sysctl_option_new( const char *key, const char *value, bool ignore_failure) { - _cleanup_(option_freep) Option *o = NULL; + _cleanup_(sysctl_option_freep) SysctlOption *o = NULL; assert(key); - o = new(Option, 1); + o = new(SysctlOption, 1); if (!o) return NULL; - *o = (Option) { + *o = (SysctlOption) { .key = strdup(key), .value = value ? strdup(value) : NULL, .ignore_failure = ignore_failure, @@ -108,7 +109,7 @@ static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_ return 0; } -static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, Option *option, const char *prefix) { +static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, SysctlOption *option, const char *prefix) { _cleanup_strv_free_ char **paths = NULL; _cleanup_free_ char *pattern = NULL; int r; @@ -179,7 +180,7 @@ static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, Option return r; } -static int apply_glob_option(OrderedHashmap *sysctl_options, Option *option) { +static int apply_glob_option(OrderedHashmap *sysctl_options, SysctlOption *option) { int r = 0; if (strv_isempty(arg_prefixes)) @@ -191,7 +192,7 @@ static int apply_glob_option(OrderedHashmap *sysctl_options, Option *option) { } static int apply_all(OrderedHashmap *sysctl_options) { - Option *option; + SysctlOption *option; int r = 0; ORDERED_HASHMAP_FOREACH(option, sysctl_options) { @@ -253,7 +254,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (!string_is_glob(key) && !test_prefix(key)) return 0; - Option *existing = ordered_hashmap_get(*sysctl_options, key); + SysctlOption *existing = ordered_hashmap_get(*sysctl_options, key); if (existing) { if (streq_ptr(value, existing->value)) { existing->ignore_failure = existing->ignore_failure || ignore_failure; @@ -262,14 +263,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool log_syntax(NULL, LOG_DEBUG, fname, line, 0, "Overwriting earlier assignment of '%s'.", key); - option_free(ordered_hashmap_remove(*sysctl_options, key)); + sysctl_option_free(ordered_hashmap_remove(*sysctl_options, key)); } - _cleanup_(option_freep) Option *option = option_new(key, value, ignore_failure); + _cleanup_(sysctl_option_freep) SysctlOption *option = sysctl_option_new(key, value, ignore_failure); if (!option) return log_oom(); - r = ordered_hashmap_ensure_put(sysctl_options, &option_hash_ops, option->key, option); + r = ordered_hashmap_ensure_put(sysctl_options, &sysctl_option_hash_ops, option->key, option); if (r < 0) return log_error_errno(r, "Failed to add sysctl variable '%s' to hashmap: %m", key); @@ -314,122 +315,110 @@ static int cat_config(char **files) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *commands = NULL, *options = NULL; int r; r = terminal_urlify_man("systemd-sysctl.service", "8", &link); if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...] [CONFIGURATION FILE...]\n" - "\n%2$sApplies kernel sysctl settings.%4$s\n" - "\n%3$sCommands:%4$s\n" - " --cat-config Show configuration files\n" - " --tldr Show non-comment parts of configuration\n" - " -h --help Show this help\n" - " --version Show package version\n" - "\n%3$sOptions:%4$s\n" - " --prefix=PATH Only apply rules with the specified prefix\n" - " --no-pager Do not pipe output into a pager\n" - " --strict Fail on any kind of failures\n" - " --inline Treat arguments as configuration lines\n" - "\nSee the %5$s for details.\n", + r = option_parser_get_help_table(&commands); + if (r < 0) + return r; + + r = option_parser_get_help_table_group("Options", &options); + if (r < 0) + return r; + + (void) table_sync_column_widths(0, commands, options); + + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n" + "\n%sApplies kernel sysctl settings.%s\n" + "\n%sCommands:%s\n", program_invocation_short_name, ansi_highlight(), - ansi_underline(), ansi_normal(), - link); + ansi_underline(), + ansi_normal()); + r = table_print_or_warn(commands); + 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; } -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_CAT_CONFIG, - ARG_TLDR, - ARG_PREFIX, - ARG_NO_PAGER, - ARG_STRICT, - ARG_INLINE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, - { "tldr", no_argument, NULL, ARG_TLDR }, - { "prefix", required_argument, NULL, ARG_PREFIX }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "strict", no_argument, NULL, ARG_STRICT }, - { "inline", no_argument, NULL, ARG_INLINE }, - {} - }; - - int c; - +static int parse_argv(int argc, char *argv[], char ***remaining_args) { assert(argc >= 0); assert(argv); + assert(remaining_args); - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + OptionParser state = { argc, argv }; + const char *arg; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case ARG_CAT_CONFIG: + OPTION_COMMON_CAT_CONFIG: arg_cat_flags = CAT_CONFIG_ON; break; - case ARG_TLDR: + OPTION_COMMON_TLDR: arg_cat_flags = CAT_TLDR; break; - case ARG_PREFIX: { - const char *s; - char *p; + OPTION_GROUP("Options"): {} + + OPTION_LONG("prefix", "PATH", + "Only apply rules with the specified prefix"): { + _cleanup_free_ char *normalized = strdup(arg); + if (!normalized) + return log_oom(); + sysctl_normalize(normalized); /* We used to require people to specify absolute paths * in /proc/sys in the past. This is kinda useless, but * we need to keep compatibility. We now support any * sysctl name available. */ - sysctl_normalize(optarg); + const char *s = path_startswith(normalized, "/proc/sys"); - s = path_startswith(optarg, "/proc/sys"); - p = strdup(s ?: optarg); - if (!p) - return log_oom(); - - if (strv_consume(&arg_prefixes, p) < 0) + if (strv_extend(&arg_prefixes, s ?: normalized) < 0) return log_oom(); break; } - case ARG_NO_PAGER: + OPTION_COMMON_NO_PAGER: arg_pager_flags |= PAGER_DISABLE; break; - case ARG_STRICT: + OPTION_LONG("strict", NULL, + "Fail on any kind of failures"): arg_strict = true; break; - case ARG_INLINE: + OPTION_LONG("inline", NULL, + "Treat arguments as configuration lines"): arg_inline = true; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - if (arg_cat_flags != CAT_CONFIG_OFF && argc > optind) + *remaining_args = option_parser_get_args(&state); + + if (arg_cat_flags != CAT_CONFIG_OFF && !strv_isempty(*remaining_args)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Positional arguments are not allowed with --cat-config/--tldr."); @@ -440,7 +429,8 @@ static int run(int argc, char *argv[]) { _cleanup_ordered_hashmap_free_ OrderedHashmap *sysctl_options = NULL; int r; - r = parse_argv(argc, argv); + char **args = NULL; + r = parse_argv(argc, argv, &args); if (r <= 0) return r; @@ -448,10 +438,10 @@ static int run(int argc, char *argv[]) { umask(0022); - if (argc > optind) { + if (!strv_isempty(args)) { unsigned pos = 0; - STRV_FOREACH(arg, strv_skip(argv, optind)) { + STRV_FOREACH(arg, args) { if (arg_inline) /* Use (argument):n, where n==1 for the first positional arg */ RET_GATHER(r, parse_line("(argument)", ++pos, *arg, /* invalid_config= */ NULL, &sysctl_options)); diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 76b6507f9a..a4bf835108 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include "sd-daemon.h" @@ -16,6 +15,7 @@ #include "loop-util.h" #include "main-func.h" #include "mount-util.h" +#include "options.h" #include "os-util.h" #include "pager.h" #include "parse-argument.h" @@ -1275,6 +1275,8 @@ static int process_image( return 0; } +VERB(verb_list, "list", "[VERSION]", VERB_ANY, 2, VERB_DEFAULT, + "Show installed and available versions"); static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; @@ -1343,6 +1345,8 @@ static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { } } +VERB(verb_features, "features", "[FEATURE]", VERB_ANY, 2, 0, + "Show optional features"); static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; @@ -1477,6 +1481,8 @@ static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata return 0; } +VERB_NOARG(verb_check_new, "check-new", + "Check if there's a new version available"); static int verb_check_new(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; @@ -1518,29 +1524,6 @@ static int verb_check_new(int argc, char *argv[], uintptr_t _data, void *userdat return EXIT_SUCCESS; } -static int verb_vacuum(int argc, char *argv[], uintptr_t _data, 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; - int r; - - assert(argc <= 1); - - if (arg_instances_max < 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "The --instances-max argument must be >= 1 while vacuuming"); - - r = process_image(/* ro= */ false, &mounted_dir, &loop_device); - if (r < 0) - return r; - - r = context_make_offline(&context, loop_device ? loop_device->node : NULL, /* requires_enabled_transfers= */ false); - if (r < 0) - return r; - - return context_vacuum(context, 0, NULL); -} - typedef enum { UPDATE_ACTION_ACQUIRE = 1 << 0, UPDATE_ACTION_INSTALL = 1 << 1, @@ -1613,6 +1596,8 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag return 0; } +VERB(verb_update, "update", "[VERSION]", VERB_ANY, 2, 0, + "Install new version now"); static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) { UpdateActionFlags flags = UPDATE_ACTION_INSTALL; @@ -1622,10 +1607,41 @@ static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) return verb_update_impl(argc, argv, flags); } +VERB(verb_acquire, "acquire", "[VERSION]", VERB_ANY, 2, 0, + "Acquire (download) new version now"); static int verb_acquire(int argc, char *argv[], uintptr_t _data, void *userdata) { return verb_update_impl(argc, argv, UPDATE_ACTION_ACQUIRE); } +VERB_NOARG(verb_vacuum, "vacuum", + "Make room, by deleting old versions"); +static int verb_vacuum(int argc, char *argv[], uintptr_t _data, 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; + int r; + + assert(argc <= 1); + + if (arg_instances_max < 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "The --instances-max argument must be >= 1 while vacuuming"); + + r = process_image(/* ro= */ false, &mounted_dir, &loop_device); + if (r < 0) + return r; + + r = context_make_offline(&context, loop_device ? loop_device->node : NULL, /* requires_enabled_transfers= */ false); + if (r < 0) + return r; + + return context_vacuum(context, 0, NULL); +} + +VERB(verb_pending_or_reboot, "pending", NULL, 1, 1, 0, + "Report whether a newer version is installed than currently booted"); +VERB(verb_pending_or_reboot, "reboot", NULL, 1, 1, 0, + "Reboot if a newer version is installed than booted"); static int verb_pending_or_reboot(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(context_freep) Context* context = NULL; _cleanup_free_ char *booted_version = NULL; @@ -1700,6 +1716,8 @@ static int component_name_valid(const char *c) { return filename_is_valid(j); } +VERB_NOARG(verb_components, "components", + "Show list of components"); static int verb_components(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; @@ -1792,190 +1810,149 @@ static int verb_components(int argc, char *argv[], uintptr_t _data, void *userda static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *common_options = NULL, *options = NULL, *verbs = NULL; int r; r = terminal_urlify_man("systemd-sysupdate", "8", &link); if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...] [VERSION]\n" - "\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" - " acquire [VERSION] Acquire (download) new version now\n" - " vacuum Make room, by deleting old versions\n" - " pending Report whether a newer version is installed than\n" - " currently booted\n" - " reboot Reboot if a newer version is installed than booted\n" - " components Show list of components\n" - " -h --help Show this help\n" - " --version Show package version\n" - "\n%3$sOptions:%4$s\n" - " -C --component=NAME Select component to update\n" - " --definitions=DIR Find transfer definitions in specified directory\n" - " --root=PATH Operate on an alternate filesystem root\n" - " --image=PATH Operate on disk image as filesystem root\n" - " --image-policy=POLICY\n" - " Specify disk image dissection policy\n" - " -m --instances-max=INT How many instances to maintain\n" - " --sync=BOOL Controls whether to sync data to disk\n" - " --verify=BOOL Force signature verification on or off\n" - " --reboot Reboot after updating to newer version\n" - " --offline Do not fetch metadata from the network\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --json=pretty|short|off\n" - " Generate JSON output\n" - " --transfer-source=PATH\n" - " Specify the directory to transfer sources from\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; + r = option_parser_get_help_table(&common_options); + if (r < 0) + return r; + + r = option_parser_get_help_table_group("Options", &options); + if (r < 0) + return r; + + (void) table_sync_column_widths(0, verbs, common_options, options); + + printf("%s [OPTIONS...] [VERSION]\n" + "\n%sUpdate OS images.%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(common_options); + 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; } -static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) { - return help(); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_LEGEND, - ARG_SYNC, - ARG_DEFINITIONS, - ARG_JSON, - ARG_ROOT, - ARG_IMAGE, - ARG_IMAGE_POLICY, - ARG_REBOOT, - ARG_VERIFY, - ARG_OFFLINE, - ARG_TRANSFER_SOURCE, - }; - - 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 }, - { "definitions", required_argument, NULL, ARG_DEFINITIONS }, - { "instances-max", required_argument, NULL, 'm' }, - { "sync", required_argument, NULL, ARG_SYNC }, - { "json", required_argument, NULL, ARG_JSON }, - { "root", required_argument, NULL, ARG_ROOT }, - { "image", required_argument, NULL, ARG_IMAGE }, - { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, - { "reboot", no_argument, NULL, ARG_REBOOT }, - { "component", required_argument, NULL, 'C' }, - { "verify", required_argument, NULL, ARG_VERIFY }, - { "offline", no_argument, NULL, ARG_OFFLINE }, - { "transfer-source", required_argument, NULL, ARG_TRANSFER_SOURCE }, - {} - }; - - int c, r; +VERB_COMMON_HELP_HIDDEN(help); +static int parse_argv(int argc, char *argv[], char ***remaining_args) { assert(argc >= 0); assert(argv); + assert(remaining_args); - while ((c = getopt_long(argc, argv, "hm:C:", options, NULL)) >= 0) { + OptionParser state = { argc, argv }; + const char *arg; + int r; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case ARG_NO_PAGER: - arg_pager_flags |= PAGER_DISABLE; + OPTION_GROUP("Options"): break; - case ARG_NO_LEGEND: - arg_legend = false; - break; - - case 'm': - r = safe_atou64(optarg, &arg_instances_max); - if (r < 0) - return log_error_errno(r, "Failed to parse --instances-max= parameter: %s", optarg); - - break; - - case ARG_SYNC: - r = parse_boolean_argument("--sync=", optarg, &arg_sync); - if (r < 0) - return r; - break; - - case ARG_DEFINITIONS: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_definitions); - if (r < 0) - return r; - break; - - case ARG_JSON: - r = parse_json_argument(optarg, &arg_json_format_flags); - if (r <= 0) - return r; - - break; - - case ARG_ROOT: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root); - if (r < 0) - return r; - break; - - case ARG_IMAGE: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image); - if (r < 0) - return r; - break; - - case ARG_IMAGE_POLICY: - r = parse_image_policy_argument(optarg, &arg_image_policy); - if (r < 0) - return r; - break; - - case ARG_REBOOT: - arg_reboot = true; - break; - - case 'C': - if (isempty(optarg)) { + OPTION('C', "component", "NAME", + "Select component to update"): + if (isempty(arg)) { arg_component = mfree(arg_component); break; } - r = component_name_valid(optarg); + r = component_name_valid(arg); if (r < 0) return log_error_errno(r, "Failed to determine if component name is valid: %m"); if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Component name invalid: %s", optarg); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Component name invalid: %s", arg); - r = free_and_strdup_warn(&arg_component, optarg); + r = free_and_strdup_warn(&arg_component, arg); if (r < 0) return r; break; - case ARG_VERIFY: { + OPTION_LONG("definitions", "DIR", + "Find transfer definitions in specified directory"): + r = parse_path_argument(arg, /* suppress_root= */ false, &arg_definitions); + if (r < 0) + return r; + break; + + OPTION_LONG("root", "PATH", + "Operate on an alternate filesystem root"): + r = parse_path_argument(arg, /* suppress_root= */ false, &arg_root); + if (r < 0) + return r; + break; + + OPTION_LONG("image", "PATH", + "Operate on disk image as filesystem root"): + r = parse_path_argument(arg, /* suppress_root= */ false, &arg_image); + if (r < 0) + return r; + break; + + OPTION_LONG("image-policy", "POLICY", + "Specify disk image dissection policy"): + r = parse_image_policy_argument(arg, &arg_image_policy); + if (r < 0) + return r; + break; + + OPTION_LONG("transfer-source", "PATH", + "Specify the directory to transfer sources from"): + r = parse_path_argument(arg, /* suppress_root= */ false, &arg_transfer_source); + if (r < 0) + return r; + + break; + + OPTION('m', "instances-max", "INT", + "How many instances to maintain"): + r = safe_atou64(arg, &arg_instances_max); + if (r < 0) + return log_error_errno(r, "Failed to parse --instances-max= parameter: %s", arg); + + break; + + OPTION_LONG("sync", "BOOL", + "Controls whether to sync data to disk"): + r = parse_boolean_argument("--sync=", arg, &arg_sync); + if (r < 0) + return r; + break; + + OPTION_LONG("verify", "BOOL", + "Force signature verification on or off"): { bool b; - r = parse_boolean_argument("--verify=", optarg, &b); + r = parse_boolean_argument("--verify=", arg, &b); if (r < 0) return r; @@ -1983,24 +1960,31 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_OFFLINE: + OPTION_LONG("reboot", NULL, + "Reboot after updating to newer version"): + arg_reboot = true; + break; + + OPTION_LONG("offline", NULL, + "Do not fetch metadata from the network"): arg_offline = true; break; - case ARG_TRANSFER_SOURCE: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_transfer_source); - if (r < 0) + OPTION_COMMON_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + + OPTION_COMMON_NO_LEGEND: + arg_legend = false; + break; + + OPTION_COMMON_JSON: + r = parse_json_argument(arg, &arg_json_format_flags); + if (r <= 0) return r; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - } if (arg_image && arg_root) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported."); @@ -2011,38 +1995,21 @@ static int parse_argv(int argc, char *argv[]) { if (arg_definitions && arg_component) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined."); + *remaining_args = option_parser_get_args(&state); return 1; } -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 }, - { "acquire", VERB_ANY, 2, 0, verb_acquire }, - { "vacuum", VERB_ANY, 1, 0, verb_vacuum }, - { "reboot", 1, 1, 0, verb_pending_or_reboot }, - { "pending", 1, 1, 0, verb_pending_or_reboot }, - { "help", VERB_ANY, 1, 0, verb_help }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - static int run(int argc, char *argv[]) { int r; log_setup(); - r = parse_argv(argc, argv); + char **args = NULL; + r = parse_argv(argc, argv, &args); if (r <= 0) return r; - return sysupdate_main(argc, argv); + return dispatch_verb_with_args(args, NULL); } DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 1ee2239aa4..b927871d21 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -4,7 +4,6 @@ ***/ #include -#include #include #include #include @@ -23,12 +22,14 @@ #include "errno-util.h" #include "exit-status.h" #include "fd-util.h" -#include "format-util.h" #include "fileio.h" +#include "format-table.h" +#include "format-util.h" #include "inotify-util.h" #include "io-util.h" #include "main-func.h" #include "mkdir-label.h" +#include "options.h" #include "path-util.h" #include "pidref.h" #include "pretty-print.h" @@ -442,112 +443,85 @@ static int process_and_watch_password_files(bool watch) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-tty-ask-password-agent", "1", &link); if (r < 0) return log_oom(); - printf("%s [OPTIONS...]\n\n" - "%sProcess system password requests.%s\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --list Show pending password requests\n" - " --query Process pending password requests\n" - " --watch Continuously process password requests\n" - " --wall Continuously forward password requests to wall\n" - " --plymouth Ask question with Plymouth instead of on TTY\n" - " --console[=DEVICE] Ask question on /dev/console (or DEVICE if specified)\n" - " instead of the current TTY\n" - "\nSee the %s for details.\n", + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + + printf("%s [OPTIONS...]\n" + "\n%sProcess system password requests.%s\n\n", program_invocation_short_name, ansi_highlight(), - ansi_normal(), - link); + ansi_normal()); + r = table_print_or_warn(options); + if (r < 0) + return r; + + printf("\nSee the %s for details.\n", link); return 0; } static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_LIST = 0x100, - ARG_QUERY, - ARG_WATCH, - ARG_WALL, - ARG_PLYMOUTH, - ARG_CONSOLE, - ARG_VERSION - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "list", no_argument, NULL, ARG_LIST }, - { "query", no_argument, NULL, ARG_QUERY }, - { "watch", no_argument, NULL, ARG_WATCH }, - { "wall", no_argument, NULL, ARG_WALL }, - { "plymouth", no_argument, NULL, ARG_PLYMOUTH }, - { "console", optional_argument, NULL, ARG_CONSOLE }, - {} - }; - - int r, c; - assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + OptionParser state = { argc, argv }; + const char *arg; + int r; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case ARG_LIST: + OPTION_LONG("list", NULL, "Show pending password requests"): arg_action = ACTION_LIST; break; - case ARG_QUERY: + OPTION_LONG("query", NULL, "Process pending password requests"): arg_action = ACTION_QUERY; break; - case ARG_WATCH: + OPTION_LONG("watch", NULL, "Continuously process password requests"): arg_action = ACTION_WATCH; break; - case ARG_WALL: + OPTION_LONG("wall", NULL, "Continuously forward password requests to wall"): arg_action = ACTION_WALL; break; - case ARG_PLYMOUTH: + OPTION_LONG("plymouth", NULL, + "Ask question with Plymouth instead of on TTY"): arg_plymouth = true; break; - case ARG_CONSOLE: + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "console", "DEVICE", + "Ask question on /dev/console (or DEVICE if specified) instead of the current TTY"): arg_console = true; - if (optarg) { - if (isempty(optarg)) + if (arg) { + if (isempty(arg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty console device path is not allowed."); - r = free_and_strdup_warn(&arg_device, optarg); + r = free_and_strdup_warn(&arg_device, arg); if (r < 0) return r; } break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - if (optind != argc) + if (option_parser_get_n_args(&state) > 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s takes no arguments.", program_invocation_short_name);