diff --git a/TODO.md b/TODO.md index 3c5ec2884d..8d623eb0de 100644 --- a/TODO.md +++ b/TODO.md @@ -3031,6 +3031,9 @@ SPDX-License-Identifier: LGPL-2.1-or-later - whenever we receive fds via SCM_RIGHTS make sure none got dropped due to the reception limit the kernel silently enforces. +- after option+verb introspection is added, add a test to verify that the + list in proc-cmdline.c matches the actual option list in systemd and shutdown. + - write a document explaining how to write correct udev rules. Mention things such as: 1. do not do lists of vid/pid matches, use hwdb for that diff --git a/src/basic/argv-util.c b/src/basic/argv-util.c index c9d610b3a0..0ff2fd9bcb 100644 --- a/src/basic/argv-util.c +++ b/src/basic/argv-util.c @@ -39,27 +39,32 @@ bool invoked_as(char *argv[], const char *token) { } bool invoked_by_systemd(void) { + static int cached = -1; int r; + if (cached >= 0) + return cached; + /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd, * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */ const char *e = getenv("SYSTEMD_EXEC_PID"); if (!e) - return false; + return (cached = false); if (streq(e, "*")) /* For testing. */ - return true; + return (cached = true); pid_t p; r = parse_pid(e, &p); if (r < 0) { /* We know that systemd sets the variable correctly. Something else must have set it. */ log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e); - return false; + return (cached = false); } - return getpid_cached() == p; + cached = getpid_cached() == p; + return cached; } bool argv_looks_like_help(int argc, char **argv) { diff --git a/src/basic/argv-util.h b/src/basic/argv-util.h index 650b00353d..02901ebc49 100644 --- a/src/basic/argv-util.h +++ b/src/basic/argv-util.h @@ -9,7 +9,7 @@ extern char **saved_argv; void save_argc_argv(int argc, char **argv); bool invoked_as(char *argv[], const char *token); -bool invoked_by_systemd(void); +bool invoked_by_systemd(void) _const_; bool argv_looks_like_help(int argc, char **argv); int rename_process_full(const char *comm, const char *invocation); diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h deleted file mode 100644 index 9abef6f156..0000000000 --- a/src/basic/getopt-defs.h +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include - -#define SYSTEMD_GETOPT_SHORT_OPTIONS "hDbsz:" - -#define COMMON_GETOPT_ARGS \ - ARG_LOG_LEVEL = 0x100, \ - ARG_LOG_TARGET, \ - ARG_LOG_COLOR, \ - ARG_LOG_LOCATION, \ - ARG_LOG_TIME - -#define SYSTEMD_GETOPT_ARGS \ - ARG_UNIT, \ - ARG_SYSTEM, \ - ARG_USER, \ - ARG_TEST, \ - ARG_NO_PAGER, \ - ARG_VERSION, \ - ARG_DUMP_CONFIGURATION_ITEMS, \ - ARG_DUMP_BUS_PROPERTIES, \ - ARG_BUS_INTROSPECT, \ - ARG_DUMP_CORE, \ - ARG_CRASH_CHVT, \ - ARG_CRASH_SHELL, \ - ARG_CRASH_REBOOT, \ - ARG_CRASH_ACTION, \ - ARG_CONFIRM_SPAWN, \ - ARG_SHOW_STATUS, \ - ARG_DESERIALIZE, \ - ARG_SWITCHED_ROOT, \ - ARG_DEFAULT_STD_OUTPUT, \ - ARG_DEFAULT_STD_ERROR, \ - ARG_MACHINE_ID, \ - ARG_SERVICE_WATCHDOGS - -#define SHUTDOWN_GETOPT_ARGS \ - ARG_EXIT_CODE, \ - ARG_TIMEOUT - -#define COMMON_GETOPT_OPTIONS \ - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, \ - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, \ - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, \ - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, \ - { "log-time", optional_argument, NULL, ARG_LOG_TIME } - -#define SYSTEMD_GETOPT_OPTIONS \ - { "unit", required_argument, NULL, ARG_UNIT }, \ - { "system", no_argument, NULL, ARG_SYSTEM }, \ - { "user", no_argument, NULL, ARG_USER }, \ - { "test", no_argument, NULL, ARG_TEST }, \ - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, \ - { "help", no_argument, NULL, 'h' }, \ - { "version", no_argument, NULL, ARG_VERSION }, \ - { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, \ - { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES }, \ - { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT }, \ - { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, \ - { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, \ - { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, \ - { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, \ - { "crash-action", required_argument, NULL, ARG_CRASH_ACTION }, \ - { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, \ - { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, \ - { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, \ - { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT }, \ - { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, \ - { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, \ - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, \ - { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS } - -#define SHUTDOWN_GETOPT_OPTIONS \ - { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, \ - { "timeout", required_argument, NULL, ARG_TIMEOUT } diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index eb61b0bc55..4de3480ff0 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -5,7 +5,6 @@ #include "alloc-util.h" #include "extract-word.h" #include "fileio.h" -#include "getopt-defs.h" #include "initrd-util.h" #include "log.h" #include "parse-util.h" @@ -15,18 +14,50 @@ #include "strv.h" #include "virt.h" +typedef enum ArgType { + no_argument, + required_argument, + optional_argument, +} ArgType; + int proc_cmdline_filter_pid1_args(char **argv, char ***ret) { - enum { - COMMON_GETOPT_ARGS, - SYSTEMD_GETOPT_ARGS, - SHUTDOWN_GETOPT_ARGS, + const struct { + const char *name; + ArgType has_arg; + } options[] = { + { "log-level", required_argument }, + { "log-target", required_argument }, + { "log-color", optional_argument }, + { "log-location", optional_argument }, + { "log-time", optional_argument }, + { "unit", required_argument }, + { "system", no_argument, }, + { "user", no_argument, }, + { "test", no_argument, }, + { "no-pager", no_argument, }, + { "help", no_argument, }, + { "version", no_argument, }, + { "dump-configuration-items", no_argument, }, + { "dump-bus-properties", no_argument, }, + { "bus-introspect", required_argument }, + { "dump-core", optional_argument }, + { "crash-chvt", required_argument }, + { "crash-vt", required_argument }, + { "crash-shell", optional_argument }, + { "crash-reboot", optional_argument }, + { "crash-action", required_argument }, + { "confirm-spawn", optional_argument }, + { "show-status", optional_argument }, + { "deserialize", required_argument }, + { "switched-root", no_argument, }, + { "default-standard-output", required_argument }, + { "default-standard-error", required_argument }, + { "machine-id", required_argument }, + { "service-watchdogs", required_argument }, + { "exit-code", required_argument }, + { "timeout", required_argument }, }; - static const struct option options[] = { - COMMON_GETOPT_OPTIONS, - SYSTEMD_GETOPT_OPTIONS, - SHUTDOWN_GETOPT_OPTIONS, - }; - static const char *short_options = SYSTEMD_GETOPT_SHORT_OPTIONS; + const char *short_options = "hDbsz:"; _cleanup_strv_free_ char **filtered = NULL; int state, r; @@ -108,12 +139,10 @@ int proc_cmdline_filter_pid1_args(char **argv, char ***ret) { } int proc_cmdline(char **ret) { - const char *e; - assert(ret); /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */ - e = secure_getenv("SYSTEMD_PROC_CMDLINE"); + const char *e = secure_getenv("SYSTEMD_PROC_CMDLINE"); if (e) return strdup_to(ret, e); @@ -124,13 +153,12 @@ int proc_cmdline(char **ret) { } static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) { - const char *e; int r; assert(ret); /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */ - e = secure_getenv("SYSTEMD_PROC_CMDLINE"); + const char *e = secure_getenv("SYSTEMD_PROC_CMDLINE"); if (e) return strv_split_full(ret, e, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE); diff --git a/src/core/main.c b/src/core/main.c index 2ce73a8624..c6d66c9ec7 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include #include @@ -19,6 +18,7 @@ #include "sd-messages.h" #include "alloc-util.h" +#include "ansi-color.h" #include "apparmor-setup.h" #include "architecture.h" #include "argv-util.h" @@ -47,8 +47,10 @@ #include "fd-util.h" #include "fdset.h" #include "fileio.h" +#include "format-table.h" #include "format-util.h" -#include "getopt-defs.h" +#include "glyph-util.h" +#include "help-util.h" #include "hexdecoct.h" #include "hostname-setup.h" #include "id128-util.h" @@ -73,13 +75,13 @@ #include "mkdir-label.h" #include "mount-setup.h" #include "mount-util.h" +#include "options.h" #include "os-util.h" #include "osc-context.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" #include "path-util.h" -#include "pretty-print.h" #include "proc-cmdline.h" #include "process-util.h" #include "random-util.h" @@ -994,249 +996,236 @@ static void set_manager_settings(Manager *m) { } static int parse_argv(int argc, char *argv[]) { - enum { - COMMON_GETOPT_ARGS, - SYSTEMD_GETOPT_ARGS, - }; - - static const struct option options[] = { - COMMON_GETOPT_OPTIONS, - SYSTEMD_GETOPT_OPTIONS, - {} - }; - - int c, r; bool user_arg_seen = false; + int r; assert(argc >= 1); assert(argv); - if (getpid_cached() == 1) - opterr = 0; + int log_level_shift = getpid_cached() == 1 ? LOG_DEBUG - LOG_ERR : 0; + OptionParser opts = { argc, argv, .log_level_shift = log_level_shift }; - while ((c = getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL)) >= 0) + /* Note: when new options are added here, also add them to the exclusion list in proc-cmdline.c! */ + FOREACH_OPTION(c, &opts) switch (c) { - case ARG_LOG_LEVEL: - r = log_set_max_level_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse log level \"%s\": %m", optarg); - + OPTION_COMMON_HELP: + arg_action = ACTION_HELP; break; - case ARG_LOG_TARGET: - r = log_set_target_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse log target \"%s\": %m", optarg); - + OPTION_COMMON_VERSION: + arg_action = ACTION_VERSION; break; - case ARG_LOG_COLOR: - - if (optarg) { - r = log_show_color_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse log color setting \"%s\": %m", - optarg); - } else - log_show_color(true); - + OPTION_LONG("test", NULL, "Determine initial transaction, dump it and exit"): + arg_action = ACTION_TEST; break; - case ARG_LOG_LOCATION: - if (optarg) { - r = log_show_location_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse log location setting \"%s\": %m", - optarg); - } else - log_show_location(true); - - break; - - case ARG_LOG_TIME: - - if (optarg) { - r = log_show_time_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse log time setting \"%s\": %m", - optarg); - } else - log_show_time(true); - - break; - - case ARG_DEFAULT_STD_OUTPUT: - r = exec_output_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse default standard output setting \"%s\": %m", - optarg); - arg_defaults.std_output = r; - break; - - case ARG_DEFAULT_STD_ERROR: - r = exec_output_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse default standard error output setting \"%s\": %m", - optarg); - arg_defaults.std_error = r; - break; - - case ARG_UNIT: - r = free_and_strdup(&arg_default_unit, optarg); - if (r < 0) - return log_error_errno(r, "Failed to set default unit \"%s\": %m", optarg); - - break; - - case ARG_SYSTEM: + OPTION_LONG("system", NULL, "Combined with --test: operate in system mode"): arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; break; - case ARG_USER: + OPTION_LONG("user", NULL, "Combined with --test: operate in user mode"): arg_runtime_scope = RUNTIME_SCOPE_USER; user_arg_seen = true; break; - case ARG_TEST: - arg_action = ACTION_TEST; - break; - - case ARG_NO_PAGER: - arg_pager_flags |= PAGER_DISABLE; - break; - - case ARG_VERSION: - arg_action = ACTION_VERSION; - break; - - case ARG_DUMP_CONFIGURATION_ITEMS: + OPTION_LONG("dump-configuration-items", NULL, "Dump understood unit configuration items"): arg_action = ACTION_DUMP_CONFIGURATION_ITEMS; break; - case ARG_DUMP_BUS_PROPERTIES: + OPTION_LONG("dump-bus-properties", NULL, "Dump exposed bus properties"): arg_action = ACTION_DUMP_BUS_PROPERTIES; break; - case ARG_BUS_INTROSPECT: - arg_bus_introspect = optarg; + OPTION_LONG("bus-introspect", "PATH", "Write XML introspection data"): + arg_bus_introspect = opts.arg; arg_action = ACTION_BUS_INTROSPECT; break; - case ARG_DUMP_CORE: - r = parse_boolean_argument("--dump-core", optarg, &arg_dump_core); + OPTION_LONG("unit", "UNIT", "Set default unit"): + r = free_and_strdup(&arg_default_unit, opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to set default unit \"%s\": %m", opts.arg); + break; + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "dump-core", "BOOL", "Dump core on crash"): + r = parse_boolean_argument("--dump-core", opts.arg, &arg_dump_core); if (r < 0) return r; break; - case ARG_CRASH_CHVT: - r = parse_crash_chvt(optarg, &arg_crash_chvt); + OPTION_LONG("crash-chvt", "NR", "Change to specified VT on crash"): {} + OPTION_LONG("crash-vt", "NR", /* help= */ NULL): /* compat option that was previously documented in --help */ + r = parse_crash_chvt(opts.arg, &arg_crash_chvt); if (r < 0) - return log_error_errno(r, "Failed to parse crash virtual terminal index: \"%s\": %m", - optarg); + return log_error_errno(r, "Failed to parse crash virtual terminal index '%s': %m", + opts.arg); break; - case ARG_CRASH_SHELL: - r = parse_boolean_argument("--crash-shell", optarg, &arg_crash_shell); + OPTION_LONG("crash-action", "ACTION", "Specify what to do on crash"): + r = crash_action_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse crash action \"%s\": %m", opts.arg); + arg_crash_action = r; + break; + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "crash-shell", "BOOL", "Run shell on crash"): + r = parse_boolean_argument("--crash-shell", opts.arg, &arg_crash_shell); if (r < 0) return r; break; - case ARG_CRASH_REBOOT: - r = parse_boolean_argument("--crash-reboot", optarg, NULL); + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "confirm-spawn", "BOOL", + "Ask for confirmation when spawning processes"): + arg_confirm_spawn = mfree(arg_confirm_spawn); + + r = parse_confirm_spawn(opts.arg, &arg_confirm_spawn); + if (r < 0) + return log_error_errno(r, "Failed to parse confirm spawn option: \"%s\": %m", + opts.arg); + break; + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "show-status", "BOOL", + "Show status updates on the console during boot"): + if (opts.arg) { + r = parse_show_status(opts.arg, &arg_show_status); + if (r < 0) + return log_error_errno(r, "Failed to parse show status boolean: \"%s\": %m", + opts.arg); + } else + arg_show_status = SHOW_STATUS_YES; + break; + + OPTION_LONG("log-target", "TARGET", + "Set log target (console, journal, kmsg, journal-or-kmsg, null)"): + r = log_set_target_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse log target \"%s\": %m", opts.arg); + break; + + OPTION_LONG("log-level", "LEVEL", + "Set log level (debug, info, notice, warning, err, crit, alert, emerg)"): + r = log_set_max_level_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse log level \"%s\": %m", opts.arg); + break; + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "log-color", "BOOL", "Highlight important log messages"): + if (opts.arg) { + r = log_show_color_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse log color setting \"%s\": %m", + opts.arg); + } else + log_show_color(true); + break; + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "log-location", "BOOL", + "Include code location in log messages"): + if (opts.arg) { + r = log_show_location_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse log location setting \"%s\": %m", + opts.arg); + } else + log_show_location(true); + break; + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "log-time", "BOOL", "Prefix log messages with current time"): + if (opts.arg) { + r = log_show_time_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse log time setting \"%s\": %m", + opts.arg); + } else + log_show_time(true); + break; + + OPTION_LONG("default-standard-output", "…", "Set default standard output for services"): + r = exec_output_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse default standard output setting \"%s\": %m", + opts.arg); + arg_defaults.std_output = r; + break; + + OPTION_LONG("default-standard-error", "…", "Set default standard error output for services"): + r = exec_output_from_string(opts.arg); + if (r < 0) + return log_error_errno(r, "Failed to parse default standard error output setting \"%s\": %m", + opts.arg); + arg_defaults.std_error = r; + break; + + OPTION_COMMON_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + + /* Options not shown in --help. */ + + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "crash-reboot", "BOOL", /* help= */ NULL): + r = parse_boolean_argument("--crash-reboot", opts.arg, NULL); if (r < 0) return r; arg_crash_action = r > 0 ? CRASH_REBOOT : CRASH_FREEZE; break; - case ARG_CRASH_ACTION: - r = crash_action_from_string(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse crash action \"%s\": %m", optarg); - arg_crash_action = r; - break; - - case ARG_CONFIRM_SPAWN: - arg_confirm_spawn = mfree(arg_confirm_spawn); - - r = parse_confirm_spawn(optarg, &arg_confirm_spawn); - if (r < 0) - return log_error_errno(r, "Failed to parse confirm spawn option: \"%s\": %m", - optarg); - break; - - case ARG_SERVICE_WATCHDOGS: - r = parse_boolean_argument("--service-watchdogs=", optarg, &arg_service_watchdogs); + OPTION_LONG("service-watchdogs", "BOOL", /* help= */ NULL): + r = parse_boolean_argument("--service-watchdogs=", opts.arg, &arg_service_watchdogs); if (r < 0) return r; break; - case ARG_SHOW_STATUS: - if (optarg) { - r = parse_show_status(optarg, &arg_show_status); - if (r < 0) - return log_error_errno(r, "Failed to parse show status boolean: \"%s\": %m", - optarg); - } else - arg_show_status = SHOW_STATUS_YES; - break; - - case ARG_DESERIALIZE: { - int fd; - FILE *f; - - fd = parse_fd(optarg); + OPTION_LONG("deserialize", "FD", /* help= */ NULL): { + int fd = parse_fd(opts.arg); if (fd < 0) - return log_error_errno(fd, "Failed to parse serialization fd \"%s\": %m", optarg); + return log_error_errno(fd, "Failed to parse serialization fd \"%s\": %m", opts.arg); (void) fd_cloexec(fd, true); - f = fdopen(fd, "r"); + FILE *f = fdopen(fd, "r"); if (!f) return log_error_errno(errno, "Failed to open serialization fd %d: %m", fd); safe_fclose(arg_serialization); arg_serialization = f; - break; } - case ARG_SWITCHED_ROOT: + OPTION_LONG("switched-root", NULL, /* help= */ NULL): arg_switched_root = true; break; - case ARG_MACHINE_ID: - r = id128_from_string_nonzero(optarg, &arg_machine_id); + OPTION_LONG("machine-id", "ID", /* help= */ NULL): + r = id128_from_string_nonzero(opts.arg, &arg_machine_id); if (r < 0) - return log_error_errno(r, "MachineID '%s' is not valid: %m", optarg); + return log_error_errno(r, "MachineID '%s' is not valid: %m", opts.arg); break; - case 'h': - arg_action = ACTION_HELP; - break; - - case 'D': + OPTION_SHORT('D', NULL, /* help= */ NULL): log_set_max_level(LOG_DEBUG); break; - case 'b': - case 's': - case 'z': - /* Just to eat away the sysvinit kernel cmdline args that we'll parse in - * parse_proc_cmdline_item() or ignore, without any getopt() error messages. - */ - case '?': + /* Eat the sysvinit kernel cmdline args that we'll parse in + * parse_proc_cmdline_item() or ignore. */ + OPTION_SHORT('b', NULL, /* help= */ NULL): {} + OPTION_SHORT('s', NULL, /* help= */ NULL): {} + OPTION_SHORT('z', "ARG", /* help= */ NULL): + if (getpid_cached() != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Switch -%c is only supported when running as PID 1.", opts.opt->short_code); + /* In PID1, do nothing and continue with option parsing. */ + break; + OPTION_ERROR: if (getpid_cached() != 1) return -EINVAL; - else - return 0; - - default: - assert_not_reached(); + /* In PID1, silently terminate option parsing. */ + return 0; } - if (optind < argc && getpid_cached() != 1) + if (option_parser_get_n_args(&opts) > 0 && getpid_cached() != 1) /* Hmm, when we aren't run as init system let's complain about excess arguments */ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Excess arguments."); @@ -1248,50 +1237,24 @@ static int parse_argv(int argc, char *argv[]) { } static int help(void) { - _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; - r = terminal_urlify_man("systemd", "1", &link); + r = option_parser_get_help_table(&options); if (r < 0) - return log_oom(); + return r; - printf("%s [OPTIONS...]\n\n" - "%sStarts and monitors system and user services.%s\n\n" - "This program takes no positional arguments.\n\n" - "%sOptions%s:\n" - " -h --help Show this help\n" - " --version Show version\n" - " --test Determine initial transaction, dump it and exit\n" - " --system Combined with --test: operate in system mode\n" - " --user Combined with --test: operate in user mode\n" - " --dump-configuration-items Dump understood unit configuration items\n" - " --dump-bus-properties Dump exposed bus properties\n" - " --bus-introspect=PATH Write XML introspection data\n" - " --unit=UNIT Set default unit\n" - " --dump-core[=BOOL] Dump core on crash\n" - " --crash-vt=NR Change to specified VT on crash\n" - " --crash-action=ACTION Specify what to do on crash\n" - " --crash-shell[=BOOL] Run shell on crash\n" - " --confirm-spawn[=BOOL] Ask for confirmation when spawning processes\n" - " --show-status[=BOOL] Show status updates on the console during boot\n" - " --log-target=TARGET Set log target (console, journal, kmsg,\n" - " journal-or-kmsg, null)\n" - " --log-level=LEVEL Set log level (debug, info, notice, warning,\n" - " err, crit, alert, emerg)\n" - " --log-color[=BOOL] Highlight important log messages\n" - " --log-location[=BOOL] Include code location in log messages\n" - " --log-time[=BOOL] Prefix log messages with current time\n" - " --default-standard-output= Set default standard output for services\n" - " --default-standard-error= Set default standard error output for services\n" - " --no-pager Do not pipe output into a pager\n" - "\nSee the %s for details.\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - ansi_underline(), - ansi_normal(), - link); + help_cmdline("[OPTIONS...]"); + help_abstract("Starts and monitors system and user services."); + printf("\nThis program takes no positional arguments.\n"); + + help_section("Options"); + r = table_print_or_warn(options); + if (r < 0) + return r; + + help_man_page_reference("systemd", "1"); return 0; } @@ -2228,8 +2191,8 @@ static int do_reexecute( } /* Try the fallback, if there is any, without any serialization. We pass the original argv[] and - * envp[]. (Well, modulo the ordering changes due to getopt() in argv[], and some cleanups in envp[], - * but let's hope that doesn't matter.) */ + * envp[]. (Well, modulo the ordering changes due to option parsing in argv[], and some cleanups in + * envp[], but let's hope that doesn't matter.) */ arg_serialization = safe_fclose(arg_serialization); fds = fdset_free(fds); @@ -3341,15 +3304,19 @@ int main(int argc, char *argv[]) { /* The efivarfs is now mounted, let's lock down the system token. */ lock_down_efi_variables(); + } else { /* Running as user instance */ arg_runtime_scope = RUNTIME_SCOPE_USER; - log_set_always_reopen_console(true); - log_set_target_and_open(LOG_TARGET_AUTO); /* clear the kernel timestamp, because we are not PID 1 */ kernel_timestamp = DUAL_TIMESTAMP_NULL; + if (invoked_by_systemd()) { + log_set_always_reopen_console(true); + log_set_target_and_open(LOG_TARGET_AUTO); + } + r = mac_init(); if (r < 0) { error_message = "Failed to initialize MAC support"; @@ -3361,9 +3328,11 @@ int main(int argc, char *argv[]) { * transitioning from the initrd to the main systemd or suchlike. */ save_rlimits(&saved_rlimit_nofile, &saved_rlimit_memlock); - /* Reset all signal handlers. */ + /* Reset all signal handlers to clean up after reexec. */ (void) reset_all_signal_handlers(); - (void) ignore_signals(SIGNALS_IGNORE); + + if (getpid_cached() == 1 || invoked_by_systemd()) + (void) ignore_signals(SIGNALS_IGNORE); (void) parse_configuration(&saved_rlimit_nofile, &saved_rlimit_memlock); diff --git a/src/shared/options.h b/src/shared/options.h index 5ba276eaee..c2283dc406 100644 --- a/src/shared/options.h +++ b/src/shared/options.h @@ -80,6 +80,10 @@ typedef struct Option { #define OPTION_POSITIONAL OPTION_FULL(OPTION_POSITIONAL_ENTRY, /* sc= */ 0, "(positional)", /* mv= */ NULL, /* h= */ NULL) #define OPTION_HELP_VERBATIM(lc, h) OPTION_FULL(OPTION_HELP_ENTRY_VERBATIM, /* sc= */ 0, lc, /* mv= */ NULL, h) +/* This can be used when custom error handling is needed. */ +#define OPTION_ERROR \ + case INT_MIN ... -1 + #define OPTION_COMMON_HELP \ OPTION('h', "help", NULL, "Show this help") diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 131550c46b..c3c114a667 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -61,7 +61,9 @@ static int parse_argv(int argc, char *argv[]) { assert(argv); /* The interface is: the verb must stay in argv[1]. Any extra positional arguments - * are warned about and ignored. See 4b5d8d0f22ae61ceb45a25391354ba53b43ee992. */ + * are warned about and ignored. See 4b5d8d0f22ae61ceb45a25391354ba53b43ee992. + * + * Note: when new options are added here, also add them to the exclusion list in proc-cmdline.c! */ OptionParser opts = { argc, argv, OPTION_PARSER_RETURN_POSITIONAL_ARGS }; int r;