From a4145b8ca3f2e0b3c9f67e9863a1e7c1d4ea4129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 25 Aug 2026 11:13:56 +0200 Subject: [PATCH] shutdown: convert to VERB_COMMAND and add --introspect-cli systemd-shutdown parsed a bunch of options, but had no way to print them. Add the standard --help and --version options and the CLI introspection. parse_argv() is moved before the PID 1 check, so that the new options also work when the binary is invoked manually. As PID 1 we can never return from main() (the kernel would panic), so in that case parse errors still freeze(), and the help output is followed by a freeze() too. Ctrl-Alt-Delete and console reopening are re-enabled before freezing, so that the freeze can be escaped through the kernel and the error is visible, as before. The --help/--version/introspect-cli option names are already listed in the kernel command line exclusion list in proc-cmdline.c. --- src/shutdown/shutdown.c | 44 ++++++++++++++++--- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 7130a996a4..44fddbde7b 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "async.h" #include "binfmt-util.h" +#include "build.h" #include "cgroup-setup.h" #include "cgroup-util.h" #include "constants.h" @@ -49,6 +50,7 @@ #include "terminal-util.h" #include "time-util.h" #include "umount.h" +#include "verbs.h" #include "virt.h" #include "watchdog.h" @@ -60,6 +62,13 @@ static const char *arg_verb = NULL; static uint8_t arg_exit_code = 0; static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; +COMMAND( + "systemd-shutdown\0", + "Execute the final phase of system shutdown.", + .argspec = "reboot|poweroff|halt|kexec|exit\0", + .man_pages = "systemd-shutdown.8\0", +); + static int parse_argv(int argc, char *argv[]) { assert(argc >= 1); assert(argv); @@ -75,6 +84,15 @@ static int parse_argv(int argc, char *argv[]) { FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { + OPTION_COMMON_HELP: + return command_print_help(); + + OPTION_COMMON_VERSION: + return version(); + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); + OPTION_COMMON_LOG_LEVEL: r = log_set_max_level_from_string(opts.arg); if (r < 0) @@ -149,7 +167,7 @@ static int parse_argv(int argc, char *argv[]) { if (!arg_verb) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Verb argument missing."); - return 0; + return 1; /* Further action */ } static int switch_root_initramfs(void) { @@ -403,9 +421,17 @@ static int run(int argc, char *argv[]) { log_set_prohibit_ipc(true); log_parse_environment(); - if (getpid_cached() != 1) + r = parse_argv(argc, argv); + if (getpid_cached() != 1) { + /* If we're a normal user space process, our work is done. Either we hit an error, or were + * called with --help, --version, or another option that was handled by parse_argv(), or it + * looks like we're being asked to do some real work, but we should do that only as part of + * the real shutdown sequence. */ + if (r <= 0) + return r; return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not executed by init (PID 1). Refusing to operate."); + } log_set_always_reopen_console(true); @@ -414,8 +440,7 @@ static int run(int argc, char *argv[]) { * through kernel. */ (void) reboot(RB_ENABLE_CAD); - r = parse_argv(argc, argv); - if (r < 0) + if (r <= 0) goto error; log_open(); @@ -711,9 +736,14 @@ static int run(int argc, char *argv[]) { r = log_error_errno(errno, "Failed to invoke reboot(): %m"); error: - log_struct_errno(LOG_EMERG, r, - LOG_MESSAGE("Critical error while doing system shutdown: %m"), - LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN_ERROR_STR)); + if (r == 0) + log_struct(LOG_EMERG, + LOG_MESSAGE("Non-shutdown operation requested, cannot operate."), + LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN_ERROR_STR)); + else + log_struct_errno(LOG_EMERG, r, + LOG_MESSAGE("Critical error while doing system shutdown: %m"), + LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN_ERROR_STR)); freeze(); } diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 78d329bfc5..cfdbe5c5ef 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -115,6 +115,7 @@ INTROSPECTABLE=( systemd-resolved systemd-run systemd-sbsign + systemd-shutdown systemd-sleep systemd-socket-activate systemd-socket-proxyd