[systemd-devel] [PATCH 2/2] Adding binary to shutdown the system
Gustavo Sverzut Barbieri
barbieri at profusion.mobi
Tue Oct 5 18:21:24 PDT 2010
From: Fabiano Fidencio <fidencio at profusion.mobi>
This functions are working as follows:
- Send a SIGTERM to all processes that may be finished
- Send a SIGKILL to all processes that still live and may be finished
- Try to unmount all mount points
- Try to remount read-only all mount points that can't be umounted
- Umount all swap devices
- Umount and detach all loopback devices
- Call [poweroff|halt|reboot|kexec]
TODO:
- Umount device-mapper.
- Make log work. So far it is being useless as we do not parse
/etc/systemd/system.conf, kernel command line but just
environment, however we're executed by init and thus have no
useful variables. Forcing it to target=kmsg/console and
level=debug also does not produce any output, however writing to
/dev/console does work (hack used during debug).
---
Makefile.am | 15 +++
src/shutdown.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 319 insertions(+), 0 deletions(-)
create mode 100644 src/shutdown.c
diff --git a/Makefile.am b/Makefile.am
index a9d1857..72f998d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,7 @@ tmpfilesdir=$(sysconfdir)/tmpfiles.d
# And these are the special ones for /
rootdir=@rootdir@
rootbindir=$(rootdir)/bin
+rootsbindir=$(rootdir)/sbin
rootlibexecdir=$(rootdir)/lib/systemd
systemunitdir=$(rootdir)/lib/systemd/system
@@ -50,9 +51,11 @@ AM_CPPFLAGS = \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(rootbindir)/systemd\" \
+ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \
-DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \
-DRUNTIME_DIR=\"$(localstatedir)/run\" \
-DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \
+ -DKEXEC_BINARY_PATH=\"$(rootsbindir)/kexec\" \
-I $(top_srcdir)/src
if TARGET_GENTOO
@@ -89,6 +92,7 @@ rootlibexec_PROGRAMS = \
systemd-update-utmp \
systemd-random-seed \
systemd-shutdownd \
+ systemd-shutdown \
systemd-modules-load \
systemd-remount-api-vfs \
systemd-kmsg-syslogd \
@@ -633,6 +637,17 @@ systemd_shutdownd_CFLAGS = \
systemd_shutdownd_LDADD = \
libsystemd-basic.la
+systemd_shutdown_SOURCES = \
+ src/mount-setup.c \
+ src/umount.c \
+ src/shutdown.c
+
+systemd_shutdown_CFLAGS = \
+ $(AM_CFLAGS)
+
+systemd_shutdown_LDADD = \
+ libsystemd-basic.la
+
systemd_modules_load_SOURCES = \
src/modules-load.c
diff --git a/src/shutdown.c b/src/shutdown.c
new file mode 100644
index 0000000..96c68f9
--- /dev/null
+++ b/src/shutdown.c
@@ -0,0 +1,304 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <linux/reboot.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "umount.h"
+#include "util.h"
+
+#define TIMEOUT_USEC (5 * USEC_PER_SEC)
+#define FINALIZE_ATTEMPTS 50
+#define FINALIZE_CRITICAL_ATTEMPTS 10
+
+_noreturn_ static void freeze(void) {
+ for (;;)
+ pause();
+}
+
+static bool ignore_proc(pid_t pid) {
+ if (pid == 1)
+ return true;
+
+ /* TODO: add more ignore rules here: device-mapper, etc */
+
+ return false;
+}
+
+static int killall(int sign) {
+ DIR *dir;
+ struct dirent *d;
+ unsigned int processes = 0;
+
+ if ((dir = opendir("/proc")) == NULL)
+ return -errno;
+
+ while ((d = readdir(dir))) {
+ pid_t pid;
+ if (parse_pid(d->d_name, &pid) < 0)
+ continue;
+
+ if (ignore_proc(pid))
+ continue;
+
+ if (kill(pid, sign) == 0)
+ processes++;
+ else
+ log_warning("Could not kill %d: %m", pid);
+ }
+
+ closedir(dir);
+
+ return processes;
+}
+
+static int send_signal(int sign) {
+ sigset_t mask, oldmask;
+ usec_t until;
+ int processes;
+ struct timespec ts;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
+ return -errno;
+
+ if (kill(-1, SIGSTOP) < 0)
+ log_warning("kill(-1, SIGSTOP): %m");
+
+ processes = killall(sign);
+
+ if (kill(-1, SIGCONT) < 0)
+ log_warning("kill(-1, SIGCONT): %m");
+
+ if (processes <= 0)
+ goto finish;
+
+ until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n;
+
+ for (;;) {
+ n = now(CLOCK_MONOTONIC);
+ if (n >= until)
+ goto finish;
+
+ if (waitpid(-1, NULL, WNOHANG) <= 0)
+ break;
+
+ if (--processes == 0)
+ goto finish;
+ }
+
+ timespec_store(&ts, until - n);
+ if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
+ log_warning("sigtimedwait did not return SIGCHLD: %m");
+ }
+
+finish:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return processes;
+}
+
+static int rescue_send_signal(int sign) {
+ sigset_t mask, oldmask;
+ usec_t until;
+ struct timespec ts;
+ int r;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
+ return -errno;
+
+ if (kill(-1, SIGSTOP) < 0)
+ log_warning("kill(-1, SIGSTOP): %m");
+
+ r = kill(-1, sign);
+ if (r < 0)
+ log_warning("kill(-1, %d): %m", sign);
+
+ if (kill(-1, SIGCONT) < 0)
+ log_warning("kill(-1, SIGCONT): %m");
+
+ if (r < 0)
+ goto finish;
+
+ until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ for (;;) {
+ usec_t n;
+
+ for (;;) {
+ n = now(CLOCK_MONOTONIC);
+ if (n >= until)
+ goto finish;
+
+ if (waitpid(-1, NULL, WNOHANG) <= 0)
+ break;
+ }
+
+ timespec_store(&ts, until - n);
+ if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
+ log_warning("sigtimedwait did not return SIGCHLD: %m");
+ }
+
+finish:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return r;
+}
+
+
+int main(int argc, char *argv[]) {
+ int cmd, r, retries;
+ bool need_umount = true, need_swapoff = true, need_loop_detach = true;
+
+ log_parse_environment();
+ log_set_target(LOG_TARGET_KMSG); /* syslog will die if not gone yet */
+ log_open();
+
+ if (getpid() != 1) {
+ log_debug("only init may exec this binary");
+ r = -EPERM;
+ goto error;
+ }
+
+ if (argc != 2) {
+ log_debug("invalid number of arguments");
+ r = -EINVAL;
+ goto error;
+ }
+
+ if (streq(argv[1], "reboot"))
+ cmd = RB_AUTOBOOT;
+ else if (streq(argv[1], "poweroff"))
+ cmd = RB_POWER_OFF;
+ else if (streq(argv[1], "halt"))
+ cmd = RB_HALT_SYSTEM;
+ else if (streq(argv[1], "kexec"))
+ cmd = LINUX_REBOOT_CMD_KEXEC;
+ else {
+ log_debug("unknown action %s", argv[1]);
+ r = -EINVAL;
+ goto error;
+ }
+
+ /* lock us into memory */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
+ log_warning("cannot lock process memory: %m");
+
+ log_info("Sending SIGTERM To Processes");
+ r = send_signal(SIGTERM);
+ if (r < 0)
+ log_warning("cannot send SIGTERM to all process: %s", strerror(r));
+
+ log_info("Sending SIGKILL To Processes");
+ r = send_signal(SIGKILL);
+ if (r < 0)
+ log_warning("cannot send SIGKILL to all process: %s", strerror(r));
+
+
+ /* preventing that we won't block umounts */
+ if (chdir("/") != 0)
+ log_warning("cannot chdir(\"/\"). Unmounts likely to fail.");
+
+ /* umount all mountpoints, swaps, and loopback devices */
+ retries = FINALIZE_ATTEMPTS;
+ while (need_umount || need_swapoff || need_loop_detach) {
+ if (need_umount) {
+ log_info("Unmounting Filesystems");
+ r = umount_all();
+ if (r == 0)
+ need_umount = false;
+ else
+ log_warning("Not all filesystems unmounted");
+ }
+
+ if (need_swapoff) {
+ log_info("Disabling Swaps");
+ r = swapoff_all();
+ if (r == 0)
+ need_swapoff = false;
+ else
+ log_warning("Not all swaps are off ");
+ }
+
+ if (need_loop_detach) {
+ log_info("Umounting and Detaching Loopback Devices");
+ r = loopback_detach_all();
+ if (r == 0)
+ need_loop_detach = false;
+ else
+ log_warning("Not all loop devices detached");
+ }
+
+ if (need_umount || need_swapoff || need_loop_detach) {
+ retries--;
+
+ if (retries <= FINALIZE_CRITICAL_ATTEMPTS) {
+ log_warning("Approaching critical level to finalize filesystem and devices, try to kill all processes.");
+ rescue_send_signal(SIGTERM);
+ rescue_send_signal(SIGKILL);
+ }
+
+ if (retries > 0)
+ log_info("Action still required, %d tries left", retries);
+ else {
+ log_error("Tried enough but still action required need_umount=%d, need_swapoff=%d, need_loop_detach=%d", need_umount, need_swapoff, need_loop_detach);
+ r = -EBUSY;
+ goto error;
+ }
+ }
+ }
+
+ sync();
+
+ if (cmd == LINUX_REBOOT_CMD_KEXEC) {
+ const char *args[5] = {KEXEC_BINARY_PATH, "-e", "-x", "-f", NULL};
+ /* we cheat and exec kexec to avoid doing all its work */
+ execv(args[0], (char * const *) args);
+ log_warning("kexec not supported, falling back to reboot");
+ cmd = RB_AUTOBOOT;
+ }
+
+ reboot(cmd);
+ r = errno;
+
+ error:
+ sync();
+ if (r < 0)
+ r = -r;
+ log_error("Critical error while doing system shutdown: %s", strerror(r));
+ freeze();
+ return 0;
+}
--
1.7.2.2
More information about the systemd-devel
mailing list