From: Zbigniew Jędrzejewski-Szmek Date: Tue, 14 Apr 2026 14:27:35 +0000 (+0200) Subject: firstboot: convert to the new option parser X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5a2777c93ee79313232279f74447af7df937602c;p=thirdparty%2Fsystemd.git firstboot: convert to the new option parser The descriptions for boolean options are adjusted to work better with BOOL as the metavar. Description of --copy is fixed, fixup for f649325ba73a7165a14c1f1134b30b12a96d3718. Co-developed-by: Claude Opus 4.6 --- diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 4720af57074..3cfe6ed7f25 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include "sd-bus.h" @@ -24,6 +23,7 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" +#include "format-table.h" #include "fs-util.h" #include "glyph-util.h" #include "hostname-util.h" @@ -38,6 +38,7 @@ #include "main-func.h" #include "memory-util.h" #include "mount-util.h" +#include "options.h" #include "os-util.h" #include "parse-argument.h" #include "parse-util.h" @@ -1240,380 +1241,254 @@ static int process_reset(int rfd) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; r = terminal_urlify_man("systemd-firstboot", "1", &link); if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...]\n" - "\n%3$sConfigures basic settings of the system.%4$s\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - " --image=PATH Operate on disk image as filesystem root\n" - " --image-policy=POLICY Specify disk image dissection policy\n" - " --locale=LOCALE Set primary locale (LANG=)\n" - " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" - " --keymap=KEYMAP Set keymap\n" - " --timezone=TIMEZONE Set timezone\n" - " --hostname=NAME Set hostname\n" - " --setup-machine-id Set a random machine ID\n" - " --machine-id=ID Set specified machine ID\n" - " --root-password=PASSWORD Set root password from plaintext password\n" - " --root-password-file=FILE Set root password from file\n" - " --root-password-hashed=HASH Set root password from hashed password\n" - " --root-shell=SHELL Set root shell\n" - " --kernel-command-line=CMDLINE\n" - " Set kernel command line\n" - " --prompt-locale Prompt the user for locale settings\n" - " --prompt-keymap Prompt the user for keymap settings\n" - " --prompt-keymap-auto Prompt the user for keymap settings if invoked\n" - " on local console\n" - " --prompt-timezone Prompt the user for timezone\n" - " --prompt-hostname Prompt the user for hostname\n" - " --prompt-root-password Prompt the user for root password\n" - " --prompt-root-shell Prompt the user for root shell\n" - " --prompt Prompt for all of the above\n" - " --copy-locale Copy locale from host\n" - " --copy-keymap Copy keymap from host\n" - " --copy-timezone Copy timezone from host\n" - " --copy-root-password Copy root password from host\n" - " --copy-root-shell Copy root shell from host\n" - " --copy Copy locale, keymap, timezone, root password\n" - " --force Overwrite existing files\n" - " --delete-root-password Delete root password\n" - " --welcome=no Disable the welcome text\n" - " --chrome=no Don't show color bar at top and bottom of\n" - " terminal\n" - " --mute-console=yes Tell kernel/PID 1 to not write to the console\n" - " while running\n" - " --reset Remove existing files\n" - "\nSee the %2$s for details.\n", + r = option_parser_get_help_table(&options); + if (r < 0) + return r; + + printf("%s [OPTIONS...]\n\n" + "%sConfigures basic settings of the system.%s\n\n", program_invocation_short_name, - link, ansi_highlight(), 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_ROOT, - ARG_IMAGE, - ARG_IMAGE_POLICY, - ARG_LOCALE, - ARG_LOCALE_MESSAGES, - ARG_KEYMAP, - ARG_TIMEZONE, - ARG_HOSTNAME, - ARG_SETUP_MACHINE_ID, - ARG_MACHINE_ID, - ARG_ROOT_PASSWORD, - ARG_ROOT_PASSWORD_FILE, - ARG_ROOT_PASSWORD_HASHED, - ARG_ROOT_SHELL, - ARG_KERNEL_COMMAND_LINE, - ARG_PROMPT, - ARG_PROMPT_LOCALE, - ARG_PROMPT_KEYMAP, - ARG_PROMPT_KEYMAP_AUTO, - ARG_PROMPT_TIMEZONE, - ARG_PROMPT_HOSTNAME, - ARG_PROMPT_ROOT_PASSWORD, - ARG_PROMPT_ROOT_SHELL, - ARG_COPY, - ARG_COPY_LOCALE, - ARG_COPY_KEYMAP, - ARG_COPY_TIMEZONE, - ARG_COPY_ROOT_PASSWORD, - ARG_COPY_ROOT_SHELL, - ARG_FORCE, - ARG_DELETE_ROOT_PASSWORD, - ARG_WELCOME, - ARG_CHROME, - ARG_RESET, - ARG_MUTE_CONSOLE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - { "image", required_argument, NULL, ARG_IMAGE }, - { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, - { "locale", required_argument, NULL, ARG_LOCALE }, - { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, - { "keymap", required_argument, NULL, ARG_KEYMAP }, - { "timezone", required_argument, NULL, ARG_TIMEZONE }, - { "hostname", required_argument, NULL, ARG_HOSTNAME }, - { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, - { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, - { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, - { "root-password-hashed", required_argument, NULL, ARG_ROOT_PASSWORD_HASHED }, - { "root-shell", required_argument, NULL, ARG_ROOT_SHELL }, - { "kernel-command-line", required_argument, NULL, ARG_KERNEL_COMMAND_LINE }, - { "prompt", no_argument, NULL, ARG_PROMPT }, - { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, - { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, - { "prompt-keymap-auto", no_argument, NULL, ARG_PROMPT_KEYMAP_AUTO }, - { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, - { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, - { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, - { "prompt-root-shell", no_argument, NULL, ARG_PROMPT_ROOT_SHELL }, - { "copy", no_argument, NULL, ARG_COPY }, - { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, - { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP }, - { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, - { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, - { "copy-root-shell", no_argument, NULL, ARG_COPY_ROOT_SHELL }, - { "force", no_argument, NULL, ARG_FORCE }, - { "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD }, - { "welcome", required_argument, NULL, ARG_WELCOME }, - { "chrome", required_argument, NULL, ARG_CHROME }, - { "reset", no_argument, NULL, ARG_RESET }, - { "mute-console", required_argument, NULL, ARG_MUTE_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_ROOT: - r = parse_path_argument(optarg, true, &arg_root); + OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"): + r = parse_path_argument(arg, true, &arg_root); if (r < 0) return r; break; - case ARG_IMAGE: - r = parse_path_argument(optarg, false, &arg_image); + OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"): + r = parse_path_argument(arg, false, &arg_image); if (r < 0) return r; break; - case ARG_IMAGE_POLICY: - r = parse_image_policy_argument(optarg, &arg_image_policy); + 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; - case ARG_LOCALE: - r = free_and_strdup(&arg_locale, optarg); + OPTION_LONG("locale", "LOCALE", "Set primary locale (LANG=)"): + r = free_and_strdup(&arg_locale, arg); if (r < 0) return log_oom(); - break; - case ARG_LOCALE_MESSAGES: - r = free_and_strdup(&arg_locale_messages, optarg); + OPTION_LONG("locale-messages", "LOCALE", "Set message locale (LC_MESSAGES=)"): + r = free_and_strdup(&arg_locale_messages, arg); if (r < 0) return log_oom(); - break; - case ARG_KEYMAP: - if (!keymap_is_valid(optarg)) + OPTION_LONG("keymap", "KEYMAP", "Set keymap"): + if (!keymap_is_valid(arg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Keymap %s is not valid.", optarg); + "Keymap %s is not valid.", arg); - r = free_and_strdup(&arg_keymap, optarg); + r = free_and_strdup(&arg_keymap, arg); if (r < 0) return log_oom(); - break; - case ARG_TIMEZONE: - if (!timezone_is_valid(optarg, LOG_ERR)) + OPTION_LONG("timezone", "TIMEZONE", "Set timezone"): + if (!timezone_is_valid(arg, LOG_ERR)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Timezone %s is not valid.", optarg); + "Timezone %s is not valid.", arg); - r = free_and_strdup(&arg_timezone, optarg); + r = free_and_strdup(&arg_timezone, arg); if (r < 0) return log_oom(); - break; - case ARG_HOSTNAME: - if (!hostname_is_valid(optarg, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK)) + OPTION_LONG("hostname", "NAME", "Set hostname"): + if (!hostname_is_valid(arg, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Host name %s is not valid.", optarg); + "Host name %s is not valid.", arg); - r = free_and_strdup(&arg_hostname, optarg); + r = free_and_strdup(&arg_hostname, arg); if (r < 0) return log_oom(); hostname_cleanup(arg_hostname); break; - case ARG_SETUP_MACHINE_ID: + OPTION_LONG("setup-machine-id", NULL, "Set a random machine ID"): r = sd_id128_randomize(&arg_machine_id); if (r < 0) return log_error_errno(r, "Failed to generate randomized machine ID: %m"); - break; - case ARG_MACHINE_ID: - r = sd_id128_from_string(optarg, &arg_machine_id); + OPTION_LONG("machine-id", "ID", "Set specified machine ID"): + r = sd_id128_from_string(arg, &arg_machine_id); if (r < 0) - return log_error_errno(r, "Failed to parse machine id %s.", optarg); - + return log_error_errno(r, "Failed to parse machine id %s.", arg); break; - case ARG_ROOT_PASSWORD: - r = free_and_strdup(&arg_root_password, optarg); + OPTION_LONG("root-password", "PASSWORD", "Set root password from plaintext password"): + r = free_and_strdup(&arg_root_password, arg); if (r < 0) return log_oom(); arg_root_password_is_hashed = false; break; - case ARG_ROOT_PASSWORD_FILE: + OPTION_LONG("root-password-file", "FILE", "Set root password from file"): arg_root_password = mfree(arg_root_password); - r = read_one_line_file(optarg, &arg_root_password); + r = read_one_line_file(arg, &arg_root_password); if (r < 0) - return log_error_errno(r, "Failed to read %s: %m", optarg); + return log_error_errno(r, "Failed to read %s: %m", arg); arg_root_password_is_hashed = false; break; - case ARG_ROOT_PASSWORD_HASHED: - r = free_and_strdup(&arg_root_password, optarg); + OPTION_LONG("root-password-hashed", "HASH", "Set root password from hashed password"): + r = free_and_strdup(&arg_root_password, arg); if (r < 0) return log_oom(); arg_root_password_is_hashed = true; break; - case ARG_ROOT_SHELL: - r = free_and_strdup(&arg_root_shell, optarg); + OPTION_LONG("root-shell", "SHELL", "Set root shell"): + r = free_and_strdup(&arg_root_shell, arg); if (r < 0) return log_oom(); - break; - case ARG_KERNEL_COMMAND_LINE: - r = free_and_strdup(&arg_kernel_cmdline, optarg); + OPTION_LONG("kernel-command-line", "CMDLINE", "Set kernel command line"): + r = free_and_strdup(&arg_kernel_cmdline, arg); if (r < 0) return log_oom(); - break; - case ARG_PROMPT_LOCALE: + OPTION_LONG("prompt-locale", NULL, "Prompt the user for locale settings"): arg_prompt_locale = true; break; - case ARG_PROMPT_KEYMAP: + OPTION_LONG("prompt-keymap", NULL, "Prompt the user for keymap settings"): arg_prompt_keymap = true; arg_prompt_keymap_auto = false; break; - case ARG_PROMPT_KEYMAP_AUTO: + OPTION_LONG("prompt-keymap-auto", NULL, + "Prompt the user for keymap settings if invoked on local console"): arg_prompt_keymap_auto = true; break; - case ARG_PROMPT_TIMEZONE: + OPTION_LONG("prompt-timezone", NULL, "Prompt the user for timezone"): arg_prompt_timezone = true; break; - case ARG_PROMPT_HOSTNAME: + OPTION_LONG("prompt-hostname", NULL, "Prompt the user for hostname"): arg_prompt_hostname = true; break; - case ARG_PROMPT_ROOT_PASSWORD: + OPTION_LONG("prompt-root-password", NULL, "Prompt the user for root password"): arg_prompt_root_password = true; break; - case ARG_PROMPT_ROOT_SHELL: + OPTION_LONG("prompt-root-shell", NULL, "Prompt the user for root shell"): arg_prompt_root_shell = true; break; - case ARG_PROMPT: + OPTION_LONG("prompt", NULL, "Prompt for all of the above"): arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = true; arg_prompt_keymap_auto = false; break; - case ARG_COPY_LOCALE: + OPTION_LONG("copy-locale", NULL, "Copy locale from host"): arg_copy_locale = true; break; - case ARG_COPY_KEYMAP: + OPTION_LONG("copy-keymap", NULL, "Copy keymap from host"): arg_copy_keymap = true; break; - case ARG_COPY_TIMEZONE: + OPTION_LONG("copy-timezone", NULL, "Copy timezone from host"): arg_copy_timezone = true; break; - case ARG_COPY_ROOT_PASSWORD: + OPTION_LONG("copy-root-password", NULL, "Copy root password from host"): arg_copy_root_password = true; break; - case ARG_COPY_ROOT_SHELL: + OPTION_LONG("copy-root-shell", NULL, "Copy root shell from host"): arg_copy_root_shell = true; break; - case ARG_COPY: + OPTION_LONG("copy", NULL, "Copy all of the above"): arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password = arg_copy_root_shell = true; break; - case ARG_FORCE: + OPTION_LONG("force", NULL, "Overwrite existing files"): arg_force = true; break; - case ARG_DELETE_ROOT_PASSWORD: + OPTION_LONG("delete-root-password", NULL, "Delete root password"): arg_delete_root_password = true; break; - case ARG_WELCOME: - r = parse_boolean(optarg); + OPTION_LONG("welcome", "BOOL", "Whether to show the welcome text"): + r = parse_boolean(arg); if (r < 0) - return log_error_errno(r, "Failed to parse --welcome= argument: %s", optarg); + return log_error_errno(r, "Failed to parse --welcome= argument: %s", arg); arg_welcome = r; break; - case ARG_CHROME: - r = parse_boolean_argument("--chrome=", optarg, &arg_chrome); + OPTION_LONG("chrome", "BOOL", + "Whether to show a color bar at top and bottom of terminal"): + r = parse_boolean_argument("--chrome=", arg, &arg_chrome); if (r < 0) return r; - break; - case ARG_MUTE_CONSOLE: - r = parse_boolean_argument("--mute-console=", optarg, &arg_mute_console); + OPTION_LONG("mute-console", "BOOL", + "Whether to disallow kernel/PID 1 writes to the console while running"): + r = parse_boolean_argument("--mute-console=", arg, &arg_mute_console); if (r < 0) return r; - break; - case ARG_RESET: + OPTION_LONG("reset", NULL, "Remove existing files"): arg_reset = true; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password))