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
f649325ba7.
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+84
-209
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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))
|
||||
|
||||
Reference in New Issue
Block a user