A batch of conversions to the new options parsers (#41302)

This was partially done with Claude, but it needed an insane amount of prodding to
not do stupid things.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-03-25 11:39:36 +01:00
committed by GitHub
16 changed files with 254 additions and 354 deletions
+1 -1
View File
@@ -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.
+1
View File
@@ -79,6 +79,7 @@
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
+23 -44
View File
@@ -1,13 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#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;
}
+51 -105
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <unistd.h>
#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)) {
+34 -51
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -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;
+42 -53
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <unistd.h>
#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);
+1
View File
@@ -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)
+1 -1
View File
@@ -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];
+3 -4
View File
@@ -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;
+13 -1
View File
@@ -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[],
+4 -2
View File
@@ -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();
+12 -3
View File
@@ -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)
+2
View File
@@ -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(
+29 -36
View File
@@ -1,16 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <sys/stat.h>
#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;
+36 -52
View File
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#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;
}
@@ -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