]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemctl: add option --when for scheduled shutdown
authorMike Yuan <me@yhndnzj.com>
Sun, 5 Mar 2023 15:27:44 +0000 (23:27 +0800)
committerMike Yuan <me@yhndnzj.com>
Tue, 14 Mar 2023 11:21:11 +0000 (19:21 +0800)
Pass an empty string or "cancel" will cancel the action.
Pass "show" will show the scheduled actions.

Replaces #17258

man/systemctl.xml
src/systemctl/systemctl-logind.c
src/systemctl/systemctl-start-special.c
src/systemctl/systemctl.c

index e0f499c41ea2a4cec99c331ad0d7b604fe6f9e99..f930034cb1db6fd64ff62c03af5e8c459652fd22 100644 (file)
@@ -1486,6 +1486,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <option>--force</option> is specified twice the halt operation is executed by <command>systemctl</command>
             itself, and the system manager is not contacted. This means the command should succeed even when the system
             manager has crashed.</para>
+
+            <para>If combined with <option>--when=</option>, shutdown will be scheduled after the given timestamp.
+            And <option>--when=cancel</option> will cancel the shutdown.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -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.</para>
 
-            <para>If combined with <option>--force</option>, 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 <option>--force</option> 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
-            <option>--force</option> is specified twice the power-off operation is executed by
-            <command>systemctl</command> itself, and the system manager is not contacted. This means the command should
-            succeed even when the system manager has crashed.</para>
+            <para>This command honors <option>--force</option> and <option>--when=</option> in a similar way
+            as <command>halt</command>.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -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.</para>
 
-            <para>If combined with <option>--force</option>, 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 <option>--force</option> 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
-            <option>--force</option> is specified twice the reboot operation is executed by
-            <command>systemctl</command> itself, and the system manager is not contacted. This means the command should
-            succeed even when the system manager has crashed.</para>
-
             <para>If the switch <option>--reboot-argument=</option> is given, it will be passed as the optional
             argument to the <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
             system call.</para>
@@ -1532,6 +1522,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <para>Options <option>--boot-loader-entry=</option>, <option>--boot-loader-menu=</option>, and
             <option>--firmware-setup</option> can be used to select what to do <emphasis>after</emphasis> the
             reboot. See the descriptions of those options for details.</para>
+
+            <para>This command honors <option>--force</option> and <option>--when=</option> in a similar way
+            as <command>halt</command>.</para>
           </listitem>
         </varlistentry>
 
@@ -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.</para>
 
-            <para>If combined with <option>--force</option>, 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.</para>
+            <para>This command honors <option>--force</option> and <option>--when=</option> in a similar way
+            as <command>halt</command>.</para>
           </listitem>
         </varlistentry>
 
@@ -2477,6 +2469,19 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--when=</option></term>
+
+        <listitem>
+          <para>When used with <command>halt</command>, <command>poweroff</command>, <command>reboot</command>
+          or <command>kexec</command>, schedule the action to be performed at the given timestamp,
+          which should adhere to the syntax documented in <citerefentry
+          project='man-pages'><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+          section "PARSING TIMESTAMPS". Specially, if <literal>show</literal> is given, the currently scheduled
+          action will be shown, which can be canceled by passing an empty string or <literal>cancel</literal>.</para>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
index 068f54e18b6221b87e6ee7f875af848fa9c758e9..fd8ca09de88fb02864d86193cc61b117ef2432c1 100644 (file)
@@ -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
index 1c50adff6ed803bebcaf718092a9654a301d7594..93432953b04d444eb577503d7e4e14148abc7b31 100644 (file)
@@ -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;
 
index 862edada089eb153463066d27c72af4f4bf2a1fd..1a5beabc7221c2650a8f63442574f770bd814285 100644 (file)
@@ -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);