]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
feature to honor first shutdown request to completion
authorJay Burger <jay.burger@fujitsu.com>
Fri, 10 Apr 2020 21:38:42 +0000 (16:38 -0500)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 24 Jun 2020 07:42:01 +0000 (09:42 +0200)
Create unit tests per established norm at position 52

check in_set first before getting unit

src/core/emergency-action.c
test/TEST-52-HONORFIRSTSHUTDOWN/Makefile [new file with mode: 0644]
test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh [new file with mode: 0755]
test/TEST-52-HONORFIRSTSHUTDOWN/test.sh [new file with mode: 0755]
test/meson.build
test/testsuite-52.units/testsuite-52.service [new file with mode: 0755]
test/testsuite-52.units/testsuite-52.sh [new file with mode: 0755]
test/units/test-honor-first-shutdown.service [new file with mode: 0644]
test/units/test-honor-first-shutdown.sh [new file with mode: 0755]

index 4330c0f2c1cd25e367d9f33f57ccdacce14433fd..1565a799270c01c2d988dfb648d4fb024a1c486e 100644 (file)
 #include "terminal-util.h"
 #include "virt.h"
 
+static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
+        [EMERGENCY_ACTION_NONE] =               "none",
+        [EMERGENCY_ACTION_REBOOT] =             "reboot",
+        [EMERGENCY_ACTION_REBOOT_FORCE] =       "reboot-force",
+        [EMERGENCY_ACTION_REBOOT_IMMEDIATE] =   "reboot-immediate",
+        [EMERGENCY_ACTION_POWEROFF] =           "poweroff",
+        [EMERGENCY_ACTION_POWEROFF_FORCE] =     "poweroff-force",
+        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
+        [EMERGENCY_ACTION_EXIT] =               "exit",
+        [EMERGENCY_ACTION_EXIT_FORCE] =         "exit-force",
+};
+
 static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
         log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
         if (warn)
@@ -28,10 +40,22 @@ void emergency_action(
                 int exit_status,
                 const char *reason) {
 
+        Unit *u;
+
         assert(m);
         assert(action >= 0);
         assert(action < _EMERGENCY_ACTION_MAX);
 
+        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
+        if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) {
+                u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
+                if (u && unit_active_or_pending(u)) {
+                        log_notice("Shutdown is already active. Skipping emergency action request %s.",
+                                   emergency_action_table[action]);
+                        return;
+                }
+        }
+
         if (action == EMERGENCY_ACTION_NONE)
                 return;
 
@@ -126,17 +150,6 @@ void emergency_action(
         }
 }
 
-static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
-        [EMERGENCY_ACTION_NONE] = "none",
-        [EMERGENCY_ACTION_REBOOT] = "reboot",
-        [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force",
-        [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
-        [EMERGENCY_ACTION_POWEROFF] = "poweroff",
-        [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
-        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
-        [EMERGENCY_ACTION_EXIT] = "exit",
-        [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
-};
 DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
 
 int parse_emergency_action(
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile b/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile
new file mode 100644 (file)
index 0000000..a48af97
--- /dev/null
@@ -0,0 +1,16 @@
+BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
+
+all setup run clean clean-again:
+       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+
+# finish option is used to run checks that can only be run outside of
+# the test execution. Example case, honor first shutdown, proof is obtained
+# from the console output as the image shuts down. This does not show up in
+# the journal so the output from the do_test is captured in a file in /tmp.
+# Without the use of finish the test will still pass because if it fails
+# the test will loop and will be terminated via a command timeout.
+# This just provides concrete confirmation.
+finish:
+       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./fini.sh --$@
+
+.PHONY: all setup run clean clean-again
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh
new file mode 100755 (executable)
index 0000000..993ada0
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+TEST_DESCRIPTION="test honor first shutdown"
+
+if grep -q "Shutdown is already active. Skipping emergency action request" /tmp/honorfirstshutdown.log; then
+    echo "$TEST_DESCRIPTION [pass]"
+    exit 0
+else
+    echo "$TEST_DESCRIPTION [fail]"
+    exit 1
+fi
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh
new file mode 100755 (executable)
index 0000000..a0848ef
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+. $TEST_BASE_DIR/test-functions
+TEST_REQUIRE_INSTALL_TESTS=0
+TEST_DESCRIPTION="testing honor first shutdown"
+#INTERACTIVE_DEBUG=1
+TEST_NO_QEMU=1
+
+#Using timeout because if the test fails it can loop.
+# The reason is because the poweroff executed by end.service
+# could turn into a reboot if the test fails.
+NSPAWN_TIMEOUT=20
+
+#Remove this file if it exists. this is used along with
+# the make target "finish". Since concrete confirmaion is
+# only found from the console during the poweroff.
+rm -f /tmp/honorfirstshutdown.log >/dev/null
+
+do_test "$@" 52 > /tmp/honorfirstshutdown.log
index 404b923467058569d2b9e1e946d1a5fadac9f04f..8bda38a2e27d1b169dfa9397ed443c34d1d9afcb 100644 (file)
@@ -28,6 +28,8 @@ install_subdir('testsuite-28.units',
                install_dir : testdata_dir)
 install_subdir('testsuite-30.units',
                install_dir : testdata_dir)
+install_subdir('testsuite-52.units',
+               install_dir : testdata_dir)
 
 testsuite08_dir = testdata_dir + '/testsuite-08.units'
 install_data('testsuite-08.units/-.mount',
diff --git a/test/testsuite-52.units/testsuite-52.service b/test/testsuite-52.units/testsuite-52.service
new file mode 100755 (executable)
index 0000000..93f847f
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/%N.units/%N.sh
+Type=oneshot
diff --git a/test/testsuite-52.units/testsuite-52.sh b/test/testsuite-52.units/testsuite-52.sh
new file mode 100755 (executable)
index 0000000..9cccf1b
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -ex
+set -o pipefail
+
+if ! test -x /usr/lib/systemd/tests/testdata/units/test-honor-first-shutdown.sh ; then
+        echo "honor-first-shutdown script not found - FAIL" > /testok
+        exit 0
+fi
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl enable test-honor-first-shutdown.service
+systemctl start test-honor-first-shutdown.service
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/test-honor-first-shutdown.service b/test/units/test-honor-first-shutdown.service
new file mode 100644 (file)
index 0000000..374f1e6
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Honor First Shutdown feature
+After=multi-user.target
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStop=sh -c 'kill -SIGKILL $MAINPID'
+FailureAction=reboot
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/test/units/test-honor-first-shutdown.sh b/test/units/test-honor-first-shutdown.sh
new file mode 100755 (executable)
index 0000000..17c1ec9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+echo "Honor first shutdown test script"
+sleep infinity;