diff --git a/NEWS b/NEWS
index 85cc7ac2d1..cdf3c3a0ad 100644
--- a/NEWS
+++ b/NEWS
@@ -14264,7 +14264,7 @@ CHANGES WITH 235:
the "utmp" group already, and it appears to be generally understood
that members of "utmp" can modify/flush the utmp/wtmp/lastlog/btmp
databases. Previously this was implemented correctly for all these
- databases excepts btmp, which has been opened up like this now
+ databases except btmp, which has been opened up like this now
too. Note that while the other databases are world-readable
(i.e. 0644), btmp is not and remains more restrictive.
diff --git a/man/systemd-update-done.service.xml b/man/systemd-update-done.service.xml
index d9d78262a1..8bb92ca5b5 100644
--- a/man/systemd-update-done.service.xml
+++ b/man/systemd-update-done.service.xml
@@ -79,6 +79,7 @@
+
diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c
index 13382b9994..ec07a914c5 100644
--- a/src/ac-power/ac-power.c
+++ b/src/ac-power/ac-power.c
@@ -1,13 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include
-
#include "alloc-util.h"
#include "ansi-color.h"
#include "battery-util.h"
#include "build.h"
+#include "format-table.h"
#include "log.h"
#include "main-func.h"
+#include "options.h"
#include "pretty-print.h"
#include "string-util.h"
@@ -20,77 +20,56 @@ static enum {
static int help(void) {
_cleanup_free_ char *link = NULL;
+ _cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-ac-power", "1", &link);
if (r < 0)
return log_oom();
- printf("%1$s [OPTION]\n"
- "\n%2$sReport whether we are connected to an external power source.%3$s\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " -v --verbose Show state as text\n"
- " --low Check if battery is discharging and low\n"
- "\nSee the %4$s for details.\n",
+ r = option_parser_get_help_table(&options);
+ if (r < 0)
+ return r;
+
+ printf("%s [OPTIONS...]\n"
+ "\n%sReport whether we are connected to an external power source.%s\n"
+ "\nOptions:\n",
program_invocation_short_name,
ansi_highlight(),
- ansi_normal(),
- link);
+ ansi_normal());
+ table_print(options, stdout);
+ printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- ARG_LOW,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "verbose", no_argument, NULL, 'v' },
- { "low", no_argument, NULL, ARG_LOW },
- {}
- };
-
- int c;
-
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hv", options, NULL)) >= 0)
+ OptionParser state = {};
+ const char *arg;
+ FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
switch (c) {
+ OPTION_COMMON_HELP:
+ return help();
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
+ OPTION_COMMON_VERSION:
return version();
- case 'v':
+ OPTION('v', "verbose", NULL, "Show state as text"):
arg_verbose = true;
break;
- case ARG_LOW:
+ OPTION_LONG("low", NULL, "Check if battery is discharging and low"):
arg_action = ACTION_LOW;
break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached();
}
- if (optind < argc)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s takes no arguments.",
- program_invocation_short_name);
+ if (option_parser_get_n_args(&state, argc, argv) > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
return 1;
}
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index f6634b54f6..13e2368795 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include
#include
#include "sd-varlink.h"
@@ -10,10 +9,12 @@
#include "build.h"
#include "bus-polkit.h"
#include "constants.h"
+#include "format-table.h"
#include "hashmap.h"
#include "json-util.h"
#include "log.h"
#include "main-func.h"
+#include "options.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "string-table.h"
@@ -39,124 +40,72 @@ STATIC_DESTRUCTOR_REGISTER(arg_message, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
+ _cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-ask-password", "1", &link);
if (r < 0)
return log_oom();
- printf("%1$s [OPTIONS...] MESSAGE\n\n"
- "%3$sQuery the user for a passphrase, via the TTY or a UI agent.%4$s\n\n"
- " -h --help Show this help\n"
- " --icon=NAME Icon name\n"
- " --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
- " --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
- " --credential=NAME\n"
- " Credential name for ImportCredential=, LoadCredential= or\n"
- " SetCredential= credentials\n"
- " --timeout=SEC Timeout in seconds\n"
- " --echo=yes|no|masked\n"
- " Control whether to show password while typing (echo)\n"
- " -e --echo Equivalent to --echo=yes\n"
- " --emoji=yes|no|auto\n"
- " Show a lock and key emoji\n"
- " --no-tty Ask question via agent even on TTY\n"
- " --accept-cached Accept cached passwords\n"
- " --multiple List multiple passwords if available\n"
- " --no-output Do not print password to standard output\n"
- " -n Do not suffix password written to standard output with\n"
- " newline\n"
- " --user Ask only our own user's agents\n"
- " --system Ask agents of the system and of all users\n"
- "\nSee the %2$s for details.\n",
+ r = option_parser_get_help_table(&options);
+ if (r < 0)
+ return r;
+
+ printf("%s [OPTIONS...] MESSAGE\n"
+ "\n%sQuery the user for a passphrase, via the TTY or a UI agent.%s\n"
+ "\nOptions:\n",
program_invocation_short_name,
- link,
ansi_highlight(),
ansi_normal());
+ table_print(options, stdout);
+ printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_ICON = 0x100,
- ARG_TIMEOUT,
- ARG_EMOJI,
- ARG_NO_TTY,
- ARG_ACCEPT_CACHED,
- ARG_MULTIPLE,
- ARG_ID,
- ARG_KEYNAME,
- ARG_NO_OUTPUT,
- ARG_VERSION,
- ARG_CREDENTIAL,
- ARG_USER,
- ARG_SYSTEM,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "icon", required_argument, NULL, ARG_ICON },
- { "timeout", required_argument, NULL, ARG_TIMEOUT },
- { "echo", optional_argument, NULL, 'e' },
- { "emoji", required_argument, NULL, ARG_EMOJI },
- { "no-tty", no_argument, NULL, ARG_NO_TTY },
- { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
- { "multiple", no_argument, NULL, ARG_MULTIPLE },
- { "id", required_argument, NULL, ARG_ID },
- { "keyname", required_argument, NULL, ARG_KEYNAME },
- { "no-output", no_argument, NULL, ARG_NO_OUTPUT },
- { "credential", required_argument, NULL, ARG_CREDENTIAL },
- { "user", no_argument, NULL, ARG_USER },
- { "system", no_argument, NULL, ARG_SYSTEM },
- {}
- };
-
const char *emoji = NULL;
- int c, r;
+ int r;
assert(argc >= 0);
assert(argv);
- /* Note the asymmetry: the long option --echo= allows an optional argument, the short option does
- * not. */
-
- /* 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, "+hen", options, NULL)) >= 0)
+ OptionParser state = {};
+ const char *arg;
+ FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
switch (c) {
-
- case 'h':
+ OPTION_COMMON_HELP:
return help();
- case ARG_VERSION:
+ OPTION_COMMON_VERSION:
return version();
- case ARG_ICON:
- arg_icon = optarg;
+ OPTION_LONG("icon", "NAME", "Icon name"):
+ arg_icon = arg;
break;
- case ARG_TIMEOUT:
- r = parse_sec(optarg, &arg_timeout);
+ OPTION_LONG("timeout", "SEC", "Timeout in seconds"):
+ r = parse_sec(arg, &arg_timeout);
if (r < 0)
- return log_error_errno(r, "Failed to parse --timeout= parameter: %s", optarg);
-
+ return log_error_errno(r, "Failed to parse --timeout= parameter: %s", arg);
break;
- case 'e':
- if (!optarg) {
+ /* Note the asymmetry: the long option --echo= allows an optional argument,
+ * the short option does not. */
+ OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "echo", "yes|no|masked",
+ "Control whether to show password while typing"): {}
+ OPTION('e', "echo", NULL, "Equivalent to --echo=yes"):
+ if (!arg) {
/* Short option -e is used, or no argument to long option --echo= */
arg_flags |= ASK_PASSWORD_ECHO;
arg_flags &= ~ASK_PASSWORD_SILENT;
- } else if (isempty(optarg) || streq(optarg, "masked"))
+ } else if (isempty(arg) || streq(arg, "masked"))
/* Empty argument or explicit string "masked" for default behaviour. */
arg_flags &= ~(ASK_PASSWORD_ECHO|ASK_PASSWORD_SILENT);
else {
- r = parse_boolean_argument("--echo=", optarg, NULL);
+ r = parse_boolean_argument("--echo=", arg, NULL);
if (r < 0)
return r;
@@ -165,55 +114,50 @@ static int parse_argv(int argc, char *argv[]) {
}
break;
- case ARG_EMOJI:
- emoji = optarg;
+ OPTION_LONG("emoji", "yes|no|auto", "Show a lock and key emoji"):
+ emoji = arg;
break;
- case ARG_NO_TTY:
+ OPTION_LONG("no-tty", NULL, "Ask question via agent even on TTY"):
arg_flags |= ASK_PASSWORD_NO_TTY;
break;
- case ARG_ACCEPT_CACHED:
+ OPTION_LONG("accept-cached", NULL, "Accept cached passwords"):
arg_flags |= ASK_PASSWORD_ACCEPT_CACHED;
break;
- case ARG_MULTIPLE:
+ OPTION_LONG("multiple", NULL, "List multiple passwords if available"):
arg_multiple = true;
break;
- case ARG_ID:
- arg_id = optarg;
+ OPTION_LONG("id", "ID", "Query identifier (e.g. \"cryptsetup:/dev/sda5\")"):
+ arg_id = arg;
break;
- case ARG_KEYNAME:
- arg_key_name = optarg;
+ OPTION_LONG("keyname", "NAME", "Kernel key name for caching passwords"):
+ arg_key_name = arg;
break;
- case ARG_NO_OUTPUT:
+ OPTION_LONG("no-output", NULL, "Do not print password to standard output"):
arg_no_output = true;
break;
- case ARG_CREDENTIAL:
- arg_credential_name = optarg;
+ OPTION_LONG("credential", "NAME",
+ "Credential name for ImportCredential=, LoadCredential= or SetCredential= credentials"):
+ arg_credential_name = arg;
break;
- case ARG_USER:
+ OPTION_LONG("user", NULL, "Ask only our own user's agents"):
arg_flags |= ASK_PASSWORD_USER;
break;
- case ARG_SYSTEM:
+ OPTION_LONG("system", NULL, "Ask agents of the system and of all users"):
arg_flags &= ~ASK_PASSWORD_USER;
break;
- case 'n':
+ OPTION_SHORT('n', NULL, "Do not suffix password written to standard output with newline"):
arg_newline = false;
break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached();
}
if (isempty(emoji) || streq(emoji, "auto"))
@@ -226,8 +170,10 @@ static int parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, !r);
}
- if (argc > optind) {
- arg_message = strv_join(argv + optind, " ");
+ char **args = option_parser_get_args(&state, argc, argv);
+
+ if (!strv_isempty(args)) {
+ arg_message = strv_join(args, " ");
if (!arg_message)
return log_oom();
} else if (FLAGS_SET(arg_flags, ASK_PASSWORD_ECHO)) {
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index ee7d2a4d07..bdd62398e5 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include
#include
#include
@@ -12,8 +11,10 @@
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "format-table.h"
#include "log.h"
#include "main-func.h"
+#include "options.h"
#include "pager.h"
#include "path-util.h"
#include "pretty-print.h"
@@ -108,88 +109,69 @@ static int cat_config(char **files) {
static int help(void) {
_cleanup_free_ char *link = NULL;
+ _cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-binfmt.service", "8", &link);
if (r < 0)
return log_oom();
- printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
- "Registers binary formats with the kernel.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --cat-config Show configuration files\n"
- " --tldr Show non-comment parts of configuration\n"
- " --no-pager Do not pipe output into a pager\n"
- " --unregister Unregister all existing entries\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...] [CONFIGURATION FILE...]\n"
+ "\n%sRegisters binary formats with the kernel.%s\n"
+ "\nOptions:\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal());
+ table_print(options, stdout);
+
+ 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_NO_PAGER,
- ARG_UNREGISTER,
- };
-
- 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 },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "unregister", no_argument, NULL, ARG_UNREGISTER },
- {}
- };
-
- int c;
+static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ OptionParser state = {};
+ const char *arg;
+ FOREACH_OPTION(&state, c, argc, argv, &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_NO_PAGER:
+ OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
- case ARG_UNREGISTER:
+ OPTION_LONG("unregister", NULL, "Unregister all existing entries"):
arg_unregister = true;
break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached();
}
- if ((arg_unregister || arg_cat_flags != CAT_CONFIG_OFF) && argc > optind)
+ char **args = option_parser_get_args(&state, argc, argv);
+
+ if ((arg_unregister || arg_cat_flags != CAT_CONFIG_OFF) && !strv_isempty(args))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Positional arguments are not allowed with --cat-config/--tldr or --unregister.");
+ *ret_args = args;
return 1;
}
@@ -208,7 +190,8 @@ static int binfmt_mounted_and_writable_warn(void) {
static int run(int argc, char *argv[]) {
int r;
- r = parse_argv(argc, argv);
+ char **args = NULL;
+ r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -221,13 +204,13 @@ static int run(int argc, char *argv[]) {
if (arg_unregister)
return disable_binfmt();
- if (argc > optind) {
+ if (!strv_isempty(args)) {
r = binfmt_mounted_and_writable_warn();
if (r <= 0)
return r;
- for (int i = optind; i < argc; i++)
- RET_GATHER(r, apply_file(argv[i], false));
+ STRV_FOREACH(f, args)
+ RET_GATHER(r, apply_file(*f, false));
} else {
_cleanup_strv_free_ char **files = NULL;
diff --git a/src/bless-boot/bless-boot.c b/src/bless-boot/bless-boot.c
index c4a9eeee76..daabff405f 100644
--- a/src/bless-boot/bless-boot.c
+++ b/src/bless-boot/bless-boot.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include
#include
#include "alloc-util.h"
@@ -9,9 +8,11 @@
#include "efivars.h"
#include "fd-util.h"
#include "find-esp.h"
+#include "format-table.h"
#include "fs-util.h"
#include "log.h"
#include "main-func.h"
+#include "options.h"
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
@@ -34,76 +35,65 @@ typedef enum Status {
static int help(void) {
_cleanup_free_ char *link = NULL;
+ _cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
r = terminal_urlify_man("systemd-bless-boot.service", "8", &link);
if (r < 0)
return log_oom();
+ r = option_parser_get_help_table(&options);
+ if (r < 0)
+ return r;
+
+ r = verbs_get_help_table(&verbs);
+ if (r < 0)
+ return r;
+
+ (void) table_sync_column_width(options, 0, verbs, 0);
+
printf("%s [OPTIONS...] COMMAND\n"
"\n%sMark the boot process as good or bad.%s\n"
- "\nCommands:\n"
- " status Show status of current boot loader entry\n"
- " good Mark this boot as good\n"
- " bad Mark this boot as bad\n"
- " indeterminate Undo any marking as good or bad\n"
- "\nOptions:\n"
- " -h --help Show this help\n"
- " --version Print version\n"
- " --path=PATH Path to the $BOOT partition (may be used multiple times)\n"
- "\nSee the %s for details.\n",
+ "\nCommands:\n",
program_invocation_short_name,
ansi_highlight(),
- ansi_normal(),
- link);
+ ansi_normal());
+ table_print(verbs, stdout);
+ printf("\nOptions:\n");
+ table_print(options, stdout);
+
+ 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();
-}
+VERB_COMMON_HELP_HIDDEN(help);
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_PATH = 0x100,
- ARG_VERSION,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "path", required_argument, NULL, ARG_PATH },
- {}
- };
-
- int c, r;
+static int parse_argv(int argc, char *argv[], char ***ret_args) {
+ int r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- switch (c) {
+ OptionParser state = {};
+ const char *arg;
- case 'h':
+ FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
+ switch (c) {
+ OPTION_COMMON_HELP:
return help();
- case ARG_VERSION:
+ OPTION_COMMON_VERSION:
return version();
- case ARG_PATH:
- r = strv_extend(&arg_path, optarg);
+ OPTION_LONG("path", "PATH", "Path to the $BOOT partition (may be used multiple times)"):
+ r = strv_extend(&arg_path, arg);
if (r < 0)
return log_oom();
break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached();
}
+ *ret_args = option_parser_get_args(&state, argc, argv);
return 1;
}
@@ -344,6 +334,7 @@ static int make_bad(const char *prefix, uint64_t done, const char *suffix, char
return 0;
}
+VERB(verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Show status of current boot loader entry");
static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
uint64_t left, done;
@@ -451,6 +442,12 @@ static int rename_in_dir_idempotent(int fd, const char *from, const char *to) {
return 1;
}
+VERB_FULL(verb_set, "good", NULL, VERB_ANY, 1, 0, STATUS_GOOD,
+ "Mark this boot as good");
+VERB_FULL(verb_set, "bad", NULL, VERB_ANY, 1, 0, STATUS_BAD,
+ "Mark this boot as bad");
+VERB_FULL(verb_set, "indeterminate", NULL, VERB_ANY, 1, 0, STATUS_INDETERMINATE,
+ "Undo any marking as good or bad");
static int verb_set(int argc, char *argv[], uintptr_t data, void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
const char *target, *source1, *source2;
@@ -561,20 +558,12 @@ exists:
}
static int run(int argc, char *argv[]) {
- static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, verb_help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "good", VERB_ANY, 1, 0, verb_set, STATUS_GOOD },
- { "bad", VERB_ANY, 1, 0, verb_set, STATUS_BAD },
- { "indeterminate", VERB_ANY, 1, 0, verb_set, STATUS_INDETERMINATE },
- {}
- };
-
+ char **args = NULL;
int r;
log_setup();
- r = parse_argv(argc, argv);
+ r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -586,7 +575,7 @@ static int run(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Marking a boot is only supported on EFI systems.");
- return dispatch_verb(argc, argv, verbs, NULL);
+ return dispatch_verb_with_args(args, NULL);
}
DEFINE_MAIN_FUNCTION(run);
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index 39004183d9..1941e88d37 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -143,6 +143,7 @@
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
+#define CONCATENATE3(x, y, z) CONCATENATE(x, CONCATENATE(y, z))
#define assert_cc(expr) _Static_assert(expr, #expr)
diff --git a/src/growfs/growfs.c b/src/growfs/growfs.c
index d991b82d67..8481257ab7 100644
--- a/src/growfs/growfs.c
+++ b/src/growfs/growfs.c
@@ -189,7 +189,7 @@ static int parse_argv(int argc, char *argv[]) {
if (optind + 1 != argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s excepts exactly one argument (the mount point).",
+ "%s expects exactly one argument (the mount point).",
program_invocation_short_name);
arg_target = argv[optind];
diff --git a/src/shared/options.c b/src/shared/options.c
index 903ed62440..8ea22c9b7a 100644
--- a/src/shared/options.c
+++ b/src/shared/options.c
@@ -21,7 +21,8 @@ static bool option_arg_required(const Option *opt) {
static bool option_is_metadata(const Option *opt) {
/* A metadata entry that is not a real option, like the group marker */
- return FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_GROUP_MARKER);
+ return FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_GROUP_MARKER) ||
+ FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_HELP_ENTRY);
}
static void kill_arg(char* argv[], int argc, int index) {
@@ -240,7 +241,7 @@ int option_parse(
return option->id;
}
-char** option_parser_get_args(OptionParser *state, int argc, char *argv[]) {
+char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]) {
/* Returns positional args as a strv.
* If "--" was found, it has been removed. */
@@ -274,8 +275,6 @@ int _option_parser_get_help_table(
if (group_marker)
break; /* End of group */
- assert(!option_is_metadata(opt));
-
if (!opt->help)
/* No help string — we do not show the option */
continue;
diff --git a/src/shared/options.h b/src/shared/options.h
index f548538bd0..fd2d048db0 100644
--- a/src/shared/options.h
+++ b/src/shared/options.h
@@ -2,11 +2,13 @@
#pragma once
#include "shared-forward.h"
+#include "strv.h"
typedef enum OptionFlags {
OPTION_OPTIONAL_ARG = 1U << 0, /* Same as optional_argument in getopt */
OPTION_STOPS_PARSING = 1U << 1, /* This option acts like "--" */
OPTION_GROUP_MARKER = 1U << 2, /* Fake option entry to separate groups */
+ OPTION_HELP_ENTRY = 1U << 3, /* Fake option entry to insert an additional help line */
} OptionFlags;
typedef struct Option {
@@ -43,7 +45,9 @@ typedef struct Option {
#define OPTION_FULL(fl, sc, lc, mv, h) _OPTION(__COUNTER__, fl, sc, lc, mv, h)
#define OPTION(sc, lc, mv, h) OPTION_FULL(/* fl= */ 0, sc, lc, mv, h)
#define OPTION_LONG(lc, mv, h) OPTION(/* sc= */ 0, lc, mv, h)
+#define OPTION_LONG_FLAGS(fl, lc, mv, h) OPTION_FULL(fl, /* sc= */ 0, lc, mv, h)
#define OPTION_SHORT(sc, mv, h) OPTION(sc, /* lc= */ NULL, mv, h)
+#define OPTION_SHORT_FLAGS(fl, sc, mv, h) OPTION_FULL(fl, sc, /* lc= */ NULL, mv, h)
#define OPTION_COMMON_HELP \
OPTION('h', "help", NULL, "Show this help")
@@ -53,6 +57,10 @@ typedef struct Option {
OPTION_LONG("no-pager", NULL, "Do not start a pager")
#define OPTION_COMMON_NO_LEGEND \
OPTION_LONG("no-legend", NULL, "Do not show headers and footers")
+#define OPTION_COMMON_CAT_CONFIG \
+ OPTION_LONG("cat-config", NULL, "Show configuration files")
+#define OPTION_COMMON_TLDR \
+ OPTION_LONG("tldr", NULL, "Show non-comment parts of configuration")
#define OPTION_COMMON_JSON \
OPTION_LONG("json", "FORMAT", "Generate JSON output (pretty, short, or off)")
@@ -89,7 +97,11 @@ int option_parse(
#define FOREACH_OPTION(parser, opt, argc, argv, ret_a, on_error) \
FOREACH_OPTION_FULL(parser, opt, argc, argv, /* ret_o= */ NULL, ret_a, on_error)
-char** option_parser_get_args(OptionParser *state, int argc, char *argv[]);
+char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]);
+static inline size_t option_parser_get_n_args(const OptionParser *state, int argc, char *argv[]) {
+ return strv_length(option_parser_get_args(state, argc, argv));
+}
+
int _option_parser_get_help_table(
const Option options[],
const Option options_end[],
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
index bf2d440bb0..47f219fab6 100644
--- a/src/shared/verbs.c
+++ b/src/shared/verbs.c
@@ -160,6 +160,9 @@ int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **re
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
assert(verb->dispatch);
+ if (!verb->help)
+ continue;
+
/* We indent the option string by two spaces. We could set the minimum cell width and
* right-align for a similar result, but that'd be more work. This is only used for
* display. */
@@ -170,8 +173,7 @@ int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **re
if (r < 0)
return table_log_add_error(r);
- const char *help = verb->help ?: "FIXME";
- _cleanup_strv_free_ char **s = strv_split(help, /* separators= */ NULL);
+ _cleanup_strv_free_ char **s = strv_split(verb->help, /* separators= */ NULL);
if (!s)
return log_oom();
diff --git a/src/shared/verbs.h b/src/shared/verbs.h
index 062e49466f..7980db62fe 100644
--- a/src/shared/verbs.h
+++ b/src/shared/verbs.h
@@ -21,13 +21,15 @@ typedef struct {
} Verb;
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
+ DISABLE_WARNING_REDUNDANT_DECLS \
static int d(int, char**, uintptr_t, void*); \
+ REENABLE_WARNING \
_section_("SYSTEMD_VERBS") \
_alignptr_ \
_used_ \
_retain_ \
_variable_no_sanitize_address_ \
- static const Verb CONCATENATE(d, _data) = { \
+ static const Verb CONCATENATE3(d, _data_, __COUNTER__) = { \
.verb = v, \
.min_args = amin, \
.max_args = amax, \
@@ -66,8 +68,15 @@ int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **re
#define verbs_get_help_table(ret) \
_verbs_get_help_table(ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, ret)
-#define VERB_COMMON_HELP(impl) \
- VERB(verb_help, "help", NULL, VERB_ANY, VERB_ANY, 0, "Show this help"); \
+#define _VERB_COMMON_HELP_IMPL(impl) \
static int verb_help(int argc, char **argv, uintptr_t data, void *userdata) { \
return impl(); \
}
+
+#define VERB_COMMON_HELP(impl) \
+ VERB(verb_help, "help", NULL, VERB_ANY, VERB_ANY, 0, "Show this help"); \
+ _VERB_COMMON_HELP_IMPL(impl)
+
+#define VERB_COMMON_HELP_HIDDEN(impl) \
+ VERB(verb_help, "help", NULL, VERB_ANY, VERB_ANY, 0, NULL); \
+ _VERB_COMMON_HELP_IMPL(impl)
diff --git a/src/test/test-options.c b/src/test/test-options.c
index ab3a429369..201d16d51f 100644
--- a/src/test/test-options.c
+++ b/src/test/test-options.c
@@ -57,6 +57,8 @@ static void test_option_parse_one(
char **args = option_parser_get_args(&state, argc, argv);
ASSERT_TRUE(strv_equal(args, remaining));
ASSERT_STREQ(argv[0], saved_argv0);
+
+ ASSERT_EQ(option_parser_get_n_args(&state, argc, argv), strv_length(remaining));
}
static void test_option_invalid_one(
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index c50acca045..03e5479aae 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -1,16 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include
#include
#include "alloc-util.h"
+#include "build.h"
#include "chase.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "format-table.h"
#include "label-util.h"
#include "log.h"
#include "main-func.h"
+#include "options.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "string-util.h"
@@ -61,65 +63,56 @@ static int save_timestamp(const char *dir, struct timespec *ts) {
static int help(void) {
_cleanup_free_ char *link = NULL;
+ _cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-update-done", "8", &link);
if (r < 0)
return log_oom();
- printf("%1$s [OPTIONS...]\n\n"
- "%5$sMark /etc/ and /var/ as fully updated.%6$s\n"
- "\n%3$sOptions:%4$s\n"
- " -h --help Show this help\n"
- " --root=PATH Operate on root directory PATH\n"
- "\nSee the %2$s for details.\n",
- program_invocation_short_name,
- link,
- ansi_underline(),
- ansi_normal(),
- ansi_highlight(),
- ansi_normal());
+ r = option_parser_get_help_table(&options);
+ if (r < 0)
+ return r;
+ printf("%s [OPTIONS...]\n"
+ "\n%sMark /etc/ and /var/ as fully updated.%s\n"
+ "\n%sOptions:%s\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ ansi_underline(),
+ ansi_normal());
+ table_print(options, stdout);
+
+ printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_ROOT = 0x100,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "root", required_argument, NULL, ARG_ROOT },
- {},
- };
-
- int r, c;
+ int r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ OptionParser state = {};
+ const char *arg;
+ FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
switch (c) {
-
- case 'h':
+ OPTION_COMMON_HELP:
return help();
- case ARG_ROOT:
- r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
+ OPTION_COMMON_VERSION:
+ return version();
+
+ OPTION_LONG("root", "PATH", "Operate on root directory PATH"):
+ r = parse_path_argument(arg, /* suppress_root= */ true, &arg_root);
if (r < 0)
return r;
break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached();
}
- if (optind < argc)
+ if (option_parser_get_n_args(&state, argc, argv) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
return 1;
diff --git a/src/validatefs/validatefs.c b/src/validatefs/validatefs.c
index 74a784d8ab..c507e4d98a 100644
--- a/src/validatefs/validatefs.c
+++ b/src/validatefs/validatefs.c
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include
-
#include "sd-device.h"
#include "alloc-util.h"
@@ -12,11 +10,13 @@
#include "device-util.h"
#include "errno-util.h"
#include "fd-util.h"
+#include "format-table.h"
#include "gpt.h"
#include "initrd-util.h"
#include "log.h"
#include "main-func.h"
#include "mountpoint-util.h"
+#include "options.h"
#include "parse-argument.h"
#include "path-util.h"
#include "pretty-print.h"
@@ -32,93 +32,77 @@ STATIC_DESTRUCTOR_REGISTER(arg_target, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ _cleanup_(table_unrefp) Table *options = NULL;
int r;
- _cleanup_free_ char *link = NULL;
r = terminal_urlify_man("systemd-validatefs@.service", "8", &link);
if (r < 0)
return log_oom();
- printf("%1$s [OPTIONS...] /path/to/mountpoint\n"
- "\n%3$sCheck file system validation constraints.%4$s\n\n"
- " -h --help Show this help and exit\n"
- " --version Print version string and exit\n"
- " --root=PATH|auto Operate relative to the specified path\n"
- "\nSee the %2$s for details.\n",
+ r = option_parser_get_help_table(&options);
+ if (r < 0)
+ return r;
+
+ printf("%s [OPTIONS...] /path/to/mountpoint\n"
+ "\n%sCheck file system validation constraints.%s\n"
+ "\nOptions:\n",
program_invocation_short_name,
- link,
ansi_highlight(),
ansi_normal());
+ table_print(options, stdout);
+ printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- ARG_ROOT,
- };
-
- int c, r;
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version" , no_argument, NULL, ARG_VERSION },
- { "root", required_argument, NULL, ARG_ROOT },
- {}
- };
+ int r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ OptionParser state = {};
+ const char *arg;
+
+ FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
switch (c) {
- case 'h':
+ OPTION_COMMON_HELP:
return help();
- case ARG_VERSION:
+ OPTION_COMMON_VERSION:
return version();
- case ARG_ROOT:
- if (streq(optarg, "auto")) {
- arg_root = mfree(arg_root);
+ OPTION_LONG("root", "PATH|auto", "Operate relative to the specified path"):
+ if (streq(arg, "auto"))
+ r = free_and_strdup_warn(&arg_root, in_initrd() ? "/sysroot" : NULL);
+ else {
+ if (!path_is_absolute(arg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--root= argument must be 'auto' or absolute path, got: %s", arg);
- if (in_initrd()) {
- arg_root = strdup("/sysroot");
- if (!arg_root)
- return log_oom();
- }
-
- break;
+ r = parse_path_argument(arg, /* suppress_root= */ true, &arg_root);
}
-
- if (!path_is_absolute(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--root= argument must be 'auto' or absolute path, got: %s", optarg);
-
- r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
if (r < 0)
return r;
break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached();
}
- if (optind + 1 != argc)
+ char **args = option_parser_get_args(&state, argc, argv);
+
+ if (strv_length(args) != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s excepts exactly one argument (the mount point).",
+ "%s expects exactly one argument (the mount point).",
program_invocation_short_name);
- arg_target = strdup(argv[optind]);
+ arg_target = strdup(args[0]);
if (!arg_target)
return log_oom();
if (arg_root && !path_startswith(arg_target, arg_root))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path '%s' does not start with specified root '%s', refusing.", arg_target, arg_root);
-
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Specified path '%s' does not start with specified root '%s', refusing.",
+ arg_target, arg_root);
return 1;
}
diff --git a/test/test-network/conf/25-routing-policy-rule-test1.network b/test/test-network/conf/25-routing-policy-rule-test1.network
index 66ea59d3a9..4c83fd5fb7 100644
--- a/test/test-network/conf/25-routing-policy-rule-test1.network
+++ b/test/test-network/conf/25-routing-policy-rule-test1.network
@@ -67,7 +67,7 @@ Priority=202
Table=22
# The four routing policy rules below intentionally have the same config
-# excepts for their To= addresses. See issue #35874.
+# except for their To= addresses. See issue #35874.
[RoutingPolicyRule]
To=192.0.2.0/26
Table=1001