From: Mike Yuan Date: Sun, 5 Mar 2023 15:27:44 +0000 (+0800) Subject: systemctl: add option --when for scheduled shutdown X-Git-Tag: v254-rc1~1028^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1433e1f998465b7acf472c73d58c14e7e2eb3f13;p=thirdparty%2Fsystemd.git systemctl: add option --when for scheduled shutdown Pass an empty string or "cancel" will cancel the action. Pass "show" will show the scheduled actions. Replaces #17258 --- diff --git a/man/systemctl.xml b/man/systemctl.xml index e0f499c41ea..f930034cb1d 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1486,6 +1486,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err is specified twice the halt operation is executed by systemctl itself, and the system manager is not contacted. This means the command should succeed even when the system manager has crashed. + + If combined with , shutdown will be scheduled after the given timestamp. + And will cancel the shutdown. @@ -1497,13 +1500,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err users. This command is asynchronous; it will return after the power-off operation is enqueued, without waiting for it to complete. - If combined with , shutdown of all running services is skipped, however all - processes are killed and all file systems are unmounted or mounted read-only, immediately followed by the - powering off. If is specified twice, the operation is immediately executed without - terminating any processes or unmounting any file systems. This may result in data loss. Note that when - is specified twice the power-off operation is executed by - systemctl itself, and the system manager is not contacted. This means the command should - succeed even when the system manager has crashed. + This command honors and in a similar way + as halt. @@ -1517,14 +1515,6 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err users. This command is asynchronous; it will return after the reboot operation is enqueued, without waiting for it to complete. - If combined with , shutdown of all running services is skipped, however all - processes are killed and all file systems are unmounted or mounted read-only, immediately followed by the - reboot. If is specified twice, the operation is immediately executed without - terminating any processes or unmounting any file systems. This may result in data loss. Note that when - is specified twice the reboot operation is executed by - systemctl itself, and the system manager is not contacted. This means the command should - succeed even when the system manager has crashed. - If the switch is given, it will be passed as the optional argument to the reboot2 system call. @@ -1532,6 +1522,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err Options , , and can be used to select what to do after the reboot. See the descriptions of those options for details. + + This command honors and in a similar way + as halt. @@ -1544,9 +1537,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err asynchronous; it will return after the reboot operation is enqueued, without waiting for it to complete. - If combined with , shutdown of all running services is skipped, however all - processes are killed and all file systems are unmounted or mounted read-only, immediately followed by the - reboot. + This command honors and in a similar way + as halt. @@ -2477,6 +2469,19 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + + When used with halt, poweroff, reboot + or kexec, schedule the action to be performed at the given timestamp, + which should adhere to the syntax documented in systemd.time7 + section "PARSING TIMESTAMPS". Specially, if show is given, the currently scheduled + action will be shown, which can be canceled by passing an empty string or cancel. + + + diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c index 068f54e18b6..fd8ca09de88 100644 --- a/src/systemctl/systemctl-logind.c +++ b/src/systemctl/systemctl-logind.c @@ -356,7 +356,7 @@ int logind_show_shutdown(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; sd_bus *bus; - const char *action = NULL; + const char *action, *pretty_action; uint64_t elapse; int r; @@ -376,17 +376,23 @@ int logind_show_shutdown(void) { return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown."); if (STR_IN_SET(action, "halt", "poweroff", "exit")) - action = "Shutdown"; + pretty_action = "Shutdown"; else if (streq(action, "kexec")) - action = "Reboot via kexec"; + pretty_action = "Reboot via kexec"; else if (streq(action, "reboot")) - action = "Reboot"; - - /* If we don't recognize the action string, we'll show it as-is */ - - log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", - action, - FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style)); + pretty_action = "Reboot"; + else /* If we don't recognize the action string, we'll show it as-is */ + pretty_action = action; + + if (arg_action == ACTION_SYSTEMCTL) + log_info("%s scheduled for %s, use 'systemctl %s --when=cancel' to cancel.", + pretty_action, + FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style), + action); + else + log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", + pretty_action, + FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style)); return 0; #else diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index 1c50adff6ed..93432953b04 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -197,22 +197,33 @@ int verb_start_special(int argc, char *argv[], void *userdata) { ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC, - ACTION_HALT, - ACTION_SUSPEND, - ACTION_HIBERNATE, - ACTION_HYBRID_SLEEP, - ACTION_SUSPEND_THEN_HIBERNATE)) { - - r = logind_reboot(a); - if (r >= 0) - return r; - if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) - /* Requested operation requires auth, is not supported or already in progress */ + ACTION_HALT)) { + + if (arg_when == 0) + r = logind_reboot(a); + else if (arg_when != USEC_INFINITY) + r = logind_schedule_shutdown(a); + else /* arg_when == USEC_INFINITY */ + r = logind_cancel_shutdown(); + if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) + /* The latter indicates that the requested operation requires auth, + * is not supported or already in progress, in which cases we ignore the error. */ return r; /* On all other errors, try low-level operation. In order to minimize the difference * between operation with and without logind, we explicitly enable non-blocking mode * for this, as logind's shutdown operations are always non-blocking. */ + arg_no_block = true; + + } else if (IN_SET(a, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, + ACTION_SUSPEND_THEN_HIBERNATE)) { + + r = logind_reboot(a); + if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) + return r; arg_no_block = true; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 862edada089..1a5beabc722 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -323,6 +323,8 @@ static int systemctl_help(void) { " --mkdir Create directory before mounting, if missing\n" " --marked Restart/reload previously marked units\n" " --drop-in=NAME Edit unit files using the specified drop-in file name\n" + " --when=TIME Schedule halt/power-off/reboot/kexec action after\n" + " a certain timestamp\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -447,6 +449,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_MARKED, ARG_NO_WARN, ARG_DROP_IN, + ARG_WHEN, }; static const struct option options[] = { @@ -511,6 +514,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "mkdir", no_argument, NULL, ARG_MKDIR }, { "marked", no_argument, NULL, ARG_MARKED }, { "drop-in", required_argument, NULL, ARG_DROP_IN }, + { "when", required_argument, NULL, ARG_WHEN }, {} }; @@ -975,6 +979,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_drop_in = optarg; break; + case ARG_WHEN: + if (streq(optarg, "show")) { + r = logind_show_shutdown(); + if (r < 0 && r != -ENODATA) + return r; + + return 0; + } + + if (STR_IN_SET(optarg, "", "cancel")) { + arg_when = USEC_INFINITY; + break; + } + + r = parse_timestamp(optarg, &arg_when); + if (r < 0) + return log_error_errno(r, "Failed to parse --when= argument '%s': %m", optarg); + + if (!timestamp_is_set(arg_when)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid timestamp '%s' specified for --when=.", optarg); + + break; + case '.': /* Output an error mimicking getopt, and print a hint afterwards */ log_error("%s: invalid option -- '.'", program_invocation_name);