]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
sulogin: avoid shared memory area usemask but use waitid() for childs
authorWerner Fink <werner@suse.de>
Thu, 11 Feb 2016 12:35:26 +0000 (13:35 +0100)
committerKarel Zak <kzak@redhat.com>
Fri, 12 Feb 2016 10:19:21 +0000 (11:19 +0100)
This small patch improves the console detection code and also avoids not
existing device nodes due strdup() which is used in canonicalize_path().
Beside this now the code for emergeny mount does work if enabled at
configure time.

Signed-off-by: Werner Fink <werner@suse.de>
login-utils/sulogin-consoles.c
login-utils/sulogin.c

index bc55e9cb690a4be817459ef1210e19b4f6d85422..fe8eab15d76e78a7528bf4cd6fbf1ef5cfbf4572 100644 (file)
@@ -36,8 +36,9 @@
 # include <linux/serial.h>
 # include <linux/major.h>
 #endif
-#include <fcntl.h>
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #ifdef USE_SULOGIN_EMERGENCY_MOUNT
@@ -226,6 +227,8 @@ dev_t devattr(const char *tty)
 
 /*
  * Search below /dev for the character device in `dev_t comparedev' variable.
+ * Note that realpath(3) is used here to avoid not existent devices due the
+ * strdup(3) used in our canonicalize_path()!
  */
 static
 #ifdef __GNUC__
@@ -233,16 +236,28 @@ __attribute__((__nonnull__,__malloc__,__hot__))
 #endif
 char* scandev(DIR *dir, dev_t comparedev)
 {
+       char path[PATH_MAX];
        char *name = NULL;
        struct dirent *dent;
-       int fd;
+       int len, fd;
 
        DBG(dbgprint("scanning /dev for %u:%u", major(comparedev), minor(comparedev)));
 
+       /*
+        * Try udev links on character devices first.
+        */
+       if ((len = snprintf(path, sizeof(path),
+                           "/dev/char/%u:%u", major(comparedev), minor(comparedev))) > 0 &&
+           (size_t)len < sizeof(path)) {
+
+           name = realpath(path, NULL);
+           if (name)
+                   goto out;
+       }
+
        fd = dirfd(dir);
        rewinddir(dir);
        while ((dent = readdir(dir))) {
-               char path[PATH_MAX];
                struct stat st;
 
 #ifdef _DIRENT_HAVE_D_TYPE
@@ -255,17 +270,33 @@ char* scandev(DIR *dir, dev_t comparedev)
                        continue;
                if (comparedev != st.st_rdev)
                        continue;
-               if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path))
+               if ((len = snprintf(path, sizeof(path), "/dev/%s", dent->d_name)) < 0 ||
+                   (size_t)len >= sizeof(path))
                        continue;
-#ifdef USE_SULOGIN_EMERGENCY_MOUNT
-               if (emergency_flags & MNT_DEVTMPFS)
-                       mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev);
-#endif
 
-               name = canonicalize_path(path);
-               break;
+               name = realpath(path, NULL);
+               if (name)
+                       goto out;
        }
 
+#ifdef USE_SULOGIN_EMERGENCY_MOUNT
+       /*
+        * There was no /dev mounted hence and no device was found hence we create our own.
+        */
+       if (!name && (emergency_flags & MNT_DEVTMPFS)) {
+
+               if ((len = snprintf(path, sizeof(path),
+                                   "/dev/tmp-%u:%u", major(comparedev), minor(comparedev))) < 0 ||
+                   (size_t)len >= sizeof(path))
+                       goto out;
+
+               if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev) < 0 && errno != EEXIST)
+                       goto out;
+
+               name = realpath(path, NULL);
+       }
+#endif
+out:
        return name;
 }
 
@@ -307,7 +338,7 @@ int append_console(struct list_head *consoles, const char *name)
        tail->flags = 0;
        tail->fd = -1;
        tail->id = last ? last->id + 1 : 0;
-       tail->pid = 0;
+       tail->pid = -1;
        memset(&tail->tio, 0, sizeof(tail->tio));
 
        return 0;
index be52141c1604b3b5c14f5fc9c69abe16364dcbd1..4b1e44b070ac86684c2761439db5eb1dc4c4b75d 100644 (file)
@@ -66,7 +66,6 @@
 static unsigned int timeout;
 static int profile;
 static volatile uint32_t openfd;               /* Remember higher file descriptors */
-static volatile uint32_t *usemask;
 
 struct sigaction saved_sigint;
 struct sigaction saved_sigtstp;
@@ -109,7 +108,8 @@ static int plymouth_command(const char* arg)
                dup2(fd, 0);
                dup2(fd, 1);
                dup2(fd, 2);
-               close(fd);
+               if (fd > 2)
+                       close(fd);
                execl(cmd, cmd, arg, (char *) NULL);
                exit(127);
        } else if (pid > 0) {
@@ -857,9 +857,12 @@ int main(int argc, char **argv)
        struct console *con;
        char *tty = NULL;
        struct passwd *pwd;
-       int c, status = 0;
-       int reconnect = 0;
+       struct timespec sigwait = {0, 50000000};
+       siginfo_t status = {};
+       sigset_t set = {};
+       int c, reconnect = 0;
        int opt_e = 0;
+       int wait = 0;
        pid_t pid;
 
        static const struct option longopts[] = {
@@ -985,9 +988,6 @@ int main(int argc, char **argv)
                tcinit(con);
        }
        ptr = (&consoles)->next;
-       usemask = (uint32_t*) mmap(NULL, sizeof(uint32_t),
-                                       PROT_READ|PROT_WRITE,
-                                       MAP_ANONYMOUS|MAP_SHARED, -1, 0);
 
        if (ptr->next == &consoles) {
                con = list_entry(ptr, struct console, entry);
@@ -1033,9 +1033,7 @@ int main(int argc, char **argv)
                                }
 
                                if (doshell) {
-                                       *usemask |= (1<<con->id);
                                        sushell(pwd);
-                                       *usemask &= ~(1<<con->id);
                                        failed++;
                                }
 
@@ -1068,28 +1066,82 @@ int main(int argc, char **argv)
 
        } while (ptr != &consoles);
 
-       while ((pid = wait(&status))) {
-               if (errno == ECHILD)
+       do {
+               int ret;
+
+               status.si_pid = 0;
+               ret = waitid(P_ALL, 0, &status, WEXITED);
+
+               if (ret == 0)
                        break;
-               if (pid < 0)
-                       continue;
-               list_for_each(ptr, &consoles) {
-                       con = list_entry(ptr, struct console, entry);
-                       if (con->pid == pid) {
-                               *usemask &= ~(1<<con->id);
+               if (ret < 0) {
+                       if (errno == ECHILD)
+                               break;
+                       if (errno == EINTR)
                                continue;
-                       }
-                       if (kill(con->pid, 0) < 0) {
-                               *usemask &= ~(1<<con->id);
+               }
+
+               errx(EXIT_FAILURE, _("Can not wait on su shell\n\n"));
+
+       } while (1);
+
+       list_for_each(ptr, &consoles) {
+               con = list_entry(ptr, struct console, entry);
+
+               if (con->fd < 0)
+                       continue;
+               if (con->pid < 0)
+                       continue;
+               if (con->pid == status.si_pid)
+                       con->pid = -1;
+               else {
+                       kill(con->pid, SIGTERM);
+                       wait++;
+               }
+       }
+
+       sigemptyset(&set);
+       sigaddset(&set, SIGCHLD);
+
+       do {
+               int signum, ret;
+
+               if (!wait)
+                       break;
+
+               status.si_pid = 0;
+               ret = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
+
+               if (ret < 0) {
+                       if (errno == ECHILD)
+                               break;
+                       if (errno == EINTR)
                                continue;
+               }
+
+               if (!ret && status.si_pid > 0) {
+                       list_for_each(ptr, &consoles) {
+                               con = list_entry(ptr, struct console, entry);
+
+                               if (con->fd < 0)
+                                       continue;
+                               if (con->pid < 0)
+                                       continue;
+                               if (con->pid == status.si_pid) {
+                                       con->pid = -1;
+                                       wait--;
+                               }
                        }
-                       if (*usemask & (1<<con->id))
-                               continue;
-                       kill(con->pid, SIGHUP);
-                       xusleep(50000);
-                       kill(con->pid, SIGKILL);
+                       continue;
                }
-       }
+
+               signum = sigtimedwait(&set, NULL, &sigwait);
+               if (signum != SIGCHLD) {
+                       if (signum < 0 && errno == EAGAIN)
+                               break;
+               }
+
+       } while (1);
 
        mask_signal(SIGCHLD, SIG_DFL, NULL);
        return EXIT_SUCCESS;