summaryrefslogtreecommitdiff
path: root/src/systemctl
diff options
context:
space:
mode:
authorMike Yuan <me@yhndnzj.com>2023-03-05 23:27:44 +0800
committerMike Yuan <me@yhndnzj.com>2023-03-14 19:21:11 +0800
commit1433e1f998465b7acf472c73d58c14e7e2eb3f13 (patch)
tree89ffc9ccc34b5a6b8f56069106e78b4e1bd9b5d6 /src/systemctl
parent92b00e867844948bdf559758739343c4308570c0 (diff)
downloadsystemd-1433e1f998465b7acf472c73d58c14e7e2eb3f13.tar.gz
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
Diffstat (limited to 'src/systemctl')
-rw-r--r--src/systemctl/systemctl-logind.c26
-rw-r--r--src/systemctl/systemctl-start-special.c33
-rw-r--r--src/systemctl/systemctl.c28
3 files changed, 66 insertions, 21 deletions
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";
-
- /* 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 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)) {
-
- 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 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);