chiark / gitweb /
udev: set errno = ENOSYS for removed interfaces
[elogind.git] / src / shutdown.c
index 0905ef5b3a96ab5523d9aa5a26db1814e1f64085..9f65b1dab0b78aeed203717b75ba3852cc3ceb1e 100644 (file)
 #include "log.h"
 #include "umount.h"
 #include "util.h"
+#include "virt.h"
+#include "watchdog.h"
 
 #define TIMEOUT_USEC (5 * USEC_PER_SEC)
 #define FINALIZE_ATTEMPTS 50
 
 static bool ignore_proc(pid_t pid) {
+        char buf[PATH_MAX];
+        FILE *f;
+        char c;
+        size_t count;
+        uid_t uid;
+        int r;
+
+        /* We are PID 1, let's not commit suicide */
         if (pid == 1)
                 return true;
 
-        /* TODO: add more ignore rules here: device-mapper, etc */
+        r = get_process_uid(pid, &uid);
+        if (r < 0)
+                return true; /* not really, but better safe than sorry */
 
-        return false;
-}
+        /* Non-root processes otherwise are always subject to be killed */
+        if (uid != 0)
+                return false;
 
-static bool is_kernel_thread(pid_t pid)
-{
-        char buf[PATH_MAX];
-        FILE *f;
-        char c;
-        size_t count;
+        snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid);
+        char_array_0(buf);
 
-        snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long)pid);
         f = fopen(buf, "re");
         if (!f)
                 return true; /* not really, but has the desired effect */
 
         count = fread(&c, 1, 1, f);
         fclose(f);
-        return count != 1;
+
+        /* Kernel threads have an empty cmdline */
+        if (count <= 0)
+                return true;
+
+        /* Processes with argv[0][0] = '@' we ignore from the killing
+         * spree.
+         *
+         * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
+        if (count == 1 && c == '@')
+                return true;
+
+        return false;
 }
 
 static int killall(int sign) {
@@ -76,7 +96,8 @@ static int killall(int sign) {
         struct dirent *d;
         unsigned int n_processes = 0;
 
-        if ((dir = opendir("/proc")) == NULL)
+        dir = opendir("/proc");
+        if (!dir)
                 return -errno;
 
         while ((d = readdir(dir))) {
@@ -85,9 +106,6 @@ static int killall(int sign) {
                 if (parse_pid(d->d_name, &pid) < 0)
                         continue;
 
-                if (is_kernel_thread(pid))
-                        continue;
-
                 if (ignore_proc(pid))
                         continue;
 
@@ -276,6 +294,10 @@ static int pivot_to_new_root(void) {
                 log_error("Failed to open /dev/console: %m");
         else {
                 make_stdio(fd);
+
+                /* Initialize the controlling terminal */
+                setsid();
+                ioctl(STDIN_FILENO, TIOCSCTTY, NULL);
         }
 
         return 0;
@@ -285,12 +307,14 @@ int main(int argc, char *argv[]) {
         int cmd, r;
         unsigned retries;
         bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
-        bool killed_everbody = false, in_container;
+        bool killed_everbody = false, in_container, use_watchdog = false;
 
         log_parse_environment();
         log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
         log_open();
 
+        umask(0022);
+
         if (getpid() != 1) {
                 log_error("Not executed by init (pid 1).");
                 r = -EPERM;
@@ -319,6 +343,8 @@ int main(int argc, char *argv[]) {
                 goto error;
         }
 
+        use_watchdog = !!getenv("WATCHDOG_USEC");
+
         /* lock us into memory */
         if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
                 log_warning("Cannot lock process memory: %m");
@@ -336,6 +362,9 @@ int main(int argc, char *argv[]) {
         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
                 bool changed = false;
 
+                if (use_watchdog)
+                        watchdog_ping();
+
                 if (need_umount) {
                         log_info("Unmounting file systems.");
                         r = umount_all(&changed);
@@ -380,9 +409,12 @@ int main(int argc, char *argv[]) {
                                 log_error("Failed to detach DM devices: %s", strerror(-r));
                 }
 
-                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach)
+                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
+                        if (retries > 0)
+                                log_info("All filesystems, swaps, loop devices, DM devices detached.");
                         /* Yay, done */
                         break;
+                }
 
                 /* If in this iteration we didn't manage to
                  * unmount/deactivate anything, we either kill more