From: Zbigniew Jędrzejewski-Szmek Date: Mon, 27 Apr 2026 22:30:08 +0000 (+0200) Subject: run: convert run0 option parser to macros X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=03a58fe2517a8015ba83eb9db87e69d60f9a0438;p=thirdparty%2Fsystemd.git run: convert run0 option parser to macros This exercises the new option namespace code. --help output is generally the same, except for the formatting changes related to use of the new output helpers and the common option macros. --same-root-dir is now documented (followup for 475729b80532dfbbce98705dade6570ce5cc29f0). Co-developed-by: Claude Opus 4.7 --- diff --git a/src/run/run.c b/src/run/run.c index 36ebe32f2d4..885ad23b15f 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include #include @@ -182,51 +181,27 @@ static int help(void) { } static int help_sudo_mode(void) { - _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *opts_table = NULL; int r; - r = terminal_urlify_man("run0", "1", &link); - if (r < 0) - return log_oom(); - /* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with * sudo's short switches, hence please do not introduce new short switches unless they have a roughly * equivalent purpose on sudo. Use long options for everything private to run0. */ - printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n" - "\n%sElevate privileges interactively.%s\n\n" - " -h --help Show this help\n" - " -V --version Show package version\n" - " --no-ask-password Do not prompt for password\n" - " --machine=CONTAINER Operate on local container\n" - " --unit=UNIT Run under the specified unit name\n" - " --property=NAME=VALUE Set service or scope unit property\n" - " --description=TEXT Description for unit\n" - " --slice=SLICE Run in the specified slice\n" - " --slice-inherit Inherit the slice\n" - " -u --user=USER Run as system user\n" - " -g --group=GROUP Run as system group\n" - " --nice=NICE Nice level\n" - " -D --chdir=PATH Set working directory\n" - " --via-shell Invoke command via target user's login shell\n" - " -i Shortcut for --via-shell --chdir='~'\n" - " --setenv=NAME[=VALUE] Set environment variable\n" - " --background=COLOR Set ANSI color for background\n" - " --pty Request allocation of a pseudo TTY for stdio\n" - " --pty-late Just like --pty, but leave TTY access to agents\n" - " until unit is started up\n" - " --pipe Request direct pipe for stdio\n" - " --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n" - " --lightweight=BOOLEAN Control whether to register a session with service manager\n" - " or without\n" - " --area=AREA Home area to log into\n" - " --empower Give privileges to selected or current user\n" - "\nSee the %s for details.\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - link); + r = option_parser_get_help_table_full("run0", /* group= */ NULL, &opts_table); + if (r < 0) + return r; + + help_cmdline("[OPTIONS...] COMMAND [ARGUMENTS...]"); + help_abstract("Elevate privileges interactively."); + help_section("Options:"); + + r = table_print_or_warn(opts_table); + if (r < 0) + return r; + + help_man_page_reference("run0", "1"); return 0; } @@ -795,149 +770,96 @@ static Glyph pty_window_glyph(void) { } static int parse_argv_sudo_mode(int argc, char *argv[]) { - - enum { - ARG_NO_ASK_PASSWORD = 0x100, - ARG_MACHINE, - ARG_UNIT, - ARG_PROPERTY, - ARG_DESCRIPTION, - ARG_SLICE, - ARG_SLICE_INHERIT, - ARG_NICE, - ARG_SETENV, - ARG_BACKGROUND, - ARG_PTY, - ARG_PTY_LATE, - ARG_PIPE, - ARG_SHELL_PROMPT_PREFIX, - ARG_LIGHTWEIGHT, - ARG_AREA, - ARG_VIA_SHELL, - ARG_EMPOWER, - ARG_SAME_ROOT_DIR, - }; + int r; /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions * though (but limit the extension to long options). */ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "machine", required_argument, NULL, ARG_MACHINE }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "property", required_argument, NULL, ARG_PROPERTY }, - { "description", required_argument, NULL, ARG_DESCRIPTION }, - { "slice", required_argument, NULL, ARG_SLICE }, - { "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT }, - { "user", required_argument, NULL, 'u' }, - { "group", required_argument, NULL, 'g' }, - { "nice", required_argument, NULL, ARG_NICE }, - { "chdir", required_argument, NULL, 'D' }, - { "via-shell", no_argument, NULL, ARG_VIA_SHELL }, - { "login", no_argument, NULL, 'i' }, /* compat with sudo, --via-shell + --chdir='~' */ - { "setenv", required_argument, NULL, ARG_SETENV }, - { "background", required_argument, NULL, ARG_BACKGROUND }, - { "pty", no_argument, NULL, ARG_PTY }, - { "pty-late", no_argument, NULL, ARG_PTY_LATE }, - { "pipe", no_argument, NULL, ARG_PIPE }, - { "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX }, - { "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT }, - { "area", required_argument, NULL, ARG_AREA }, - { "empower", no_argument, NULL, ARG_EMPOWER }, - { "same-root-dir", no_argument, NULL, ARG_SAME_ROOT_DIR }, - {}, - }; - - int r, c; - assert(argc >= 0); assert(argv); - /* 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, "+hVu:g:D:i", options, NULL)) >= 0) + OptionParser opts = { argc, argv, OPTION_PARSER_STOP_AT_FIRST_NONOPTION, "run0" }; + FOREACH_OPTION(c, &opts, /* on_error= */ return c) switch (c) { - case 'h': + OPTION_NAMESPACE("run0"): {} + + OPTION_COMMON_HELP: return help_sudo_mode(); - case 'V': + OPTION('V', "version", NULL, "Show package version"): return version(); - case ARG_NO_ASK_PASSWORD: + OPTION_COMMON_NO_ASK_PASSWORD: arg_ask_password = false; break; - case ARG_MACHINE: - r = parse_machine_argument(optarg, &arg_host, &arg_transport); + OPTION_LONG("machine", "CONTAINER", "Operate on local container"): + r = parse_machine_argument(opts.arg, &arg_host, &arg_transport); if (r < 0) return r; break; - case ARG_UNIT: - arg_unit = optarg; + OPTION_LONG("unit", "UNIT", "Run under the specified unit name"): + arg_unit = opts.arg; break; - case ARG_PROPERTY: - if (strv_extend(&arg_property, optarg) < 0) + OPTION_LONG("property", "NAME=VALUE", "Set service or scope unit property"): + if (strv_extend(&arg_property, opts.arg) < 0) return log_oom(); - break; - case ARG_DESCRIPTION: - r = free_and_strdup_warn(&arg_description, optarg); + OPTION_LONG("description", "TEXT", "Description for unit"): + r = free_and_strdup_warn(&arg_description, opts.arg); if (r < 0) return r; break; - case ARG_SLICE: - r = free_and_strdup_warn(&arg_slice, optarg); + OPTION_LONG("slice", "SLICE", "Run in the specified slice"): + r = free_and_strdup_warn(&arg_slice, opts.arg); if (r < 0) return r; break; - case ARG_SLICE_INHERIT: + OPTION_LONG("slice-inherit", NULL, "Inherit the slice"): arg_slice_inherit = true; break; - case 'u': - r = free_and_strdup_warn(&arg_exec_user, optarg); + OPTION('u', "user", "USER", "Run as system user"): + r = free_and_strdup_warn(&arg_exec_user, opts.arg); if (r < 0) return r; break; - case 'g': - arg_exec_group = optarg; + OPTION('g', "group", "GROUP", "Run as system group"): + arg_exec_group = opts.arg; break; - case ARG_NICE: - r = parse_nice(optarg, &arg_nice); + OPTION_LONG("nice", "NICE", "Nice level"): + r = parse_nice(opts.arg, &arg_nice); if (r < 0) - return log_error_errno(r, "Failed to parse nice value: %s", optarg); + return log_error_errno(r, "Failed to parse nice value: %s", opts.arg); arg_nice_set = true; break; - case 'D': - if (streq(optarg, "~")) - r = free_and_strdup_warn(&arg_working_directory, optarg); + OPTION('D', "chdir", "PATH", "Set working directory"): + if (streq(opts.arg, "~")) + r = free_and_strdup_warn(&arg_working_directory, opts.arg); else /* Root will be manually suppressed later. */ - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_working_directory); + r = parse_path_argument(opts.arg, /* suppress_root= */ false, &arg_working_directory); if (r < 0) return r; - break; - case ARG_VIA_SHELL: + OPTION_LONG("via-shell", NULL, "Invoke command via target user's login shell"): arg_via_shell = true; break; - case 'i': + OPTION_LONG("login", NULL, NULL): {} /* hidden compat alias for -i */ + OPTION_SHORT('i', NULL, "Shortcut for --via-shell --chdir='~'"): r = free_and_strdup_warn(&arg_working_directory, "~"); if (r < 0) return r; @@ -945,69 +867,61 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { arg_via_shell = true; break; - case ARG_SETENV: - r = strv_env_replace_strdup_passthrough(&arg_environment, optarg); + OPTION_LONG("setenv", "NAME[=VALUE]", "Set environment variable"): + r = strv_env_replace_strdup_passthrough(&arg_environment, opts.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", opts.arg); break; - case ARG_BACKGROUND: - r = parse_background_argument(optarg, &arg_background); + OPTION_LONG("background", "COLOR", "Set ANSI color for background"): + r = parse_background_argument(opts.arg, &arg_background); if (r < 0) return r; - break; - case ARG_PTY: - case ARG_PTY_LATE: + OPTION_LONG("pty", NULL, "Request allocation of a pseudo TTY for stdio"): {} + OPTION_LONG("pty-late", NULL, + "Just like --pty, but leave TTY access to agents until unit is started up"): arg_stdio |= ARG_STDIO_PTY; - arg_pty_late = c == ARG_PTY_LATE; + arg_pty_late = streq(opts.opt->long_code, "pty-late"); break; - case ARG_PIPE: + OPTION_LONG("pipe", NULL, "Request direct pipe for stdio"): arg_stdio |= ARG_STDIO_DIRECT; break; - case ARG_SHELL_PROMPT_PREFIX: - r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg); + OPTION_LONG("shell-prompt-prefix", "PREFIX", "Set $SHELL_PROMPT_PREFIX"): + r = free_and_strdup_warn(&arg_shell_prompt_prefix, opts.arg); if (r < 0) return r; break; - case ARG_LIGHTWEIGHT: - r = parse_tristate_argument_with_auto("--lightweight=", optarg, &arg_lightweight); + OPTION_LONG("lightweight", "BOOLEAN", + "Control whether to register a session with service manager or without"): + r = parse_tristate_argument_with_auto("--lightweight=", opts.arg, &arg_lightweight); if (r < 0) return r; break; - case ARG_AREA: + OPTION_LONG("area", "AREA", "Home area to log into"): /* We allow an empty --area= specification to allow logging into the primary home directory */ - if (!isempty(optarg) && !filename_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid area name, refusing: %s", optarg); + if (!isempty(opts.arg) && !filename_is_valid(opts.arg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid area name, refusing: %s", opts.arg); - r = free_and_strdup_warn(&arg_area, optarg); + r = free_and_strdup_warn(&arg_area, opts.arg); if (r < 0) return r; - break; - case ARG_EMPOWER: + OPTION_LONG("empower", NULL, "Give privileges to selected or current user"): arg_empower = true; break; - case ARG_SAME_ROOT_DIR: + OPTION_LONG("same-root-dir", NULL, NULL): /* hidden */ r = free_and_strdup_warn(&arg_root_directory, "/"); if (r < 0) return r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } if (!arg_working_directory) { @@ -1055,8 +969,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { arg_send_sighup = true; _cleanup_strv_free_ char **l = NULL; - if (argc > optind) { - l = strv_copy(argv + optind); + char **args = option_parser_get_args(&opts); + if (!strv_isempty(args)) { + l = strv_copy(args); if (!l) return log_oom(); } else if (!arg_via_shell) {