orphanripper: Race fix.
[nethome.git] / src / orphanripper.c
index 4893e8f..ffccabf 100644 (file)
 #include <assert.h>
 #include <pty.h>
 #include <poll.h>
 #include <assert.h>
 #include <pty.h>
 #include <poll.h>
+#include <sys/stat.h>
 
 #define LENGTH(x) (sizeof (x) / sizeof (*(x)))
 
 static const char *progname;
 
 
 #define LENGTH(x) (sizeof (x) / sizeof (*(x)))
 
 static const char *progname;
 
-static volatile int signal_chld_hit = 0;
+static volatile pid_t child;
 
 static void signal_chld (int signo)
 {
 
 static void signal_chld (int signo)
 {
-  signal_chld_hit = 1;
 }
 
 static volatile int signal_alrm_hit = 0;
 }
 
 static volatile int signal_alrm_hit = 0;
@@ -61,7 +61,6 @@ static void signal_alrm (int signo)
 }
 
 static char childptyname[LINE_MAX];
 }
 
 static char childptyname[LINE_MAX];
-static pid_t child;
 
 static void print_child_error (const char *reason, char **argv)
 {
 
 static void print_child_error (const char *reason, char **argv)
 {
@@ -87,6 +86,8 @@ static int read_out (int amaster)
   /* Weird but at least after POLLHUP we get EIO instead of just EOF.  */
   if (buf_got == -1 && errno == EIO)
     return 0;
   /* Weird but at least after POLLHUP we get EIO instead of just EOF.  */
   if (buf_got == -1 && errno == EIO)
     return 0;
+  if (buf_got == -1 && errno == EAGAIN)
+    return 0;
   if (buf_got < 0)
     {
       perror ("read (amaster)");
   if (buf_got < 0)
     {
       perror ("read (amaster)");
@@ -100,6 +101,44 @@ static int read_out (int amaster)
   return 1;
 }
 
   return 1;
 }
 
+/* kill (child, 0) ==0 sometimes even when CHILD's state is already Z.  */
+
+static int child_exited (void)
+{
+  char buf[200];
+  int fd, i, retval;
+  ssize_t got;
+  char *state;
+
+  snprintf (buf, sizeof (buf), "/proc/%ld/stat", (long) child);
+  fd = open (buf, O_RDONLY);
+  if (fd == -1)
+    {
+      perror ("open (/proc/CHILD/stat)");
+      exit (EXIT_FAILURE);
+    }
+  got = read (fd, buf, sizeof(buf));
+  if (got <= 0)
+    {
+      perror ("read (/proc/CHILD/stat)");
+      exit (EXIT_FAILURE);
+    }
+  if (close (fd) != 0)
+    {
+      perror ("close (/proc/CHILD/stat)");
+      exit (EXIT_FAILURE);
+    }
+  i = sscanf (buf, "%*d%*s%ms", &state);
+  if (i != 1)
+    {
+      perror ("sscanf (/proc/CHILD/stat)");
+      exit (EXIT_FAILURE);
+    }
+  retval = strcmp (state, "Z") == 0;
+  free (state);
+  return retval;
+}
+
 static int spawn (char **argv, int timeout)
 {
   pid_t child_got;
 static int spawn (char **argv, int timeout)
 {
   pid_t child_got;
@@ -153,6 +192,11 @@ static int spawn (char **argv, int timeout)
        assert (i == STDIN_FILENO);
 #endif
 
        assert (i == STDIN_FILENO);
 #endif
 
+       i = sigemptyset (&set);
+       assert (i == 0);
+       i = sigprocmask (SIG_SETMASK, &set, NULL);
+       assert (i == 0);
+
        /* Do not setpgrp(2) in the parent process as the process-group
           is shared for the whole sh(1) pipeline we could be a part
           of.  The process-group is set according to PID of the first
        /* Do not setpgrp(2) in the parent process as the process-group
           is shared for the whole sh(1) pipeline we could be a part
           of.  The process-group is set according to PID of the first
@@ -200,8 +244,13 @@ static int spawn (char **argv, int timeout)
       pollfd.fd = amaster;
       pollfd.events = POLLIN;
       i = ppoll (&pollfd, 1, NULL, &set);
       pollfd.fd = amaster;
       pollfd.events = POLLIN;
       i = ppoll (&pollfd, 1, NULL, &set);
-      if (i == -1 && errno == EINTR && signal_chld_hit)
-       break;
+      if (i == -1 && errno == EINTR)
+       {
+         if (child_exited ())
+           break;
+         /* Non-CHILD child may have exited.  */
+         continue;
+       }
       assert (i == 1);
       /* Data available?  Process it first.  */
       if (pollfd.revents & POLLIN)
       assert (i == 1);
       /* Data available?  Process it first.  */
       if (pollfd.revents & POLLIN)
@@ -221,7 +270,7 @@ static int spawn (char **argv, int timeout)
          exit (EXIT_FAILURE);
        }
       /* Child exited?  */
          exit (EXIT_FAILURE);
        }
       /* Child exited?  */
-      if (signal_chld_hit)
+      if (child_exited ())
        break;
     }
 
        break;
     }
 
@@ -270,12 +319,10 @@ static int spawn (char **argv, int timeout)
       exit (EXIT_FAILURE);
     }
 
       exit (EXIT_FAILURE);
     }
 
-  /* In the POLLHUP case we may not have seen SIGCHLD so far.  */
+  /* Not used in fact.  */
   i = sigprocmask (SIG_SETMASK, &set, NULL);
   assert (i == 0);
 
   i = sigprocmask (SIG_SETMASK, &set, NULL);
   assert (i == 0);
 
-  assert (signal_chld_hit != 0);
-
   /* Do not unset O_NONBLOCK as a stale child (the whole purpose of this
      program) having open its output pty would block us in read_out.  */
 #if 0
   /* Do not unset O_NONBLOCK as a stale child (the whole purpose of this
      program) having open its output pty would block us in read_out.  */
 #if 0