diff --git a/man/systemctl.xml b/man/systemctl.xml
index e0f499c41e..f930034cb1 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 068f54e18b..fd8ca09de8 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";
+ pretty_action = "Reboot";
+ else /* If we don't recognize the action string, we'll show it as-is */
+ pretty_action = action;
- /* 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));
+ 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 1c50adff6e..93432953b0 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)) {
+ ACTION_HALT)) {
- 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 */
+ 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 862edada08..1a5beabc72 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);