common/exechelp: Provide a way to wait for multiple processes.
authorJustus Winter <justus@g10code.com>
Thu, 14 Jan 2016 17:20:14 +0000 (18:20 +0100)
committerJustus Winter <justus@g10code.com>
Tue, 23 Feb 2016 10:58:52 +0000 (11:58 +0100)
* common/exechelp-posix.c (gnupg_wait_process): Generalize to
'gnupg_wait_processes'.
* common/exechelp-w32.c (gnupg_wait_process): Likewise.
* common/exechelp-w32ce.c (gnupg_wait_process): New function stub.
* common/exechelp.h (gnupg_wait_process): New prototype.

Signed-off-by: Justus Winter <justus@g10code.com>
common/exechelp-posix.c
common/exechelp-w32.c
common/exechelp-w32ce.c
common/exechelp.h

index 37abf55..6614eb7 100644 (file)
@@ -522,60 +522,94 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
 }
 
 
-/* See exechelp.h for the description.  */
+/* See exechelp.h for a description.  */
 gpg_error_t
 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
 {
-  gpg_err_code_t ec;
-  int i, status;
+  return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
+}
 
-  if (r_exitcode)
-    *r_exitcode = -1;
+/* See exechelp.h for a description.  */
+gpg_error_t
+gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
+                      int hang, int *r_exitcodes)
+{
+  gpg_err_code_t ec = 0;
+  size_t i, left;
 
-  if (pid == (pid_t)(-1))
-    return gpg_error (GPG_ERR_INV_VALUE);
+  for (i = 0; i < count; i++)
+    {
+      if (r_exitcodes)
+        r_exitcodes[i] = -1;
+
+      if (pids[i] == (pid_t)(-1))
+        return gpg_error (GPG_ERR_INV_VALUE);
+    }
+
+  left = count;
+  while (left > 0)
+    {
+      pid_t pid;
+      int status;
 
 #ifdef USE_NPTH
-  i = npth_waitpid (pid, &status, hang? 0:WNOHANG);
+      pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG);
 #else
-  while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
-        && errno == EINTR);
+      while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1)
+             && errno == EINTR);
 #endif
 
-  if (i == (pid_t)(-1))
-    {
-      ec = gpg_err_code_from_errno (errno);
-      log_error (_("waiting for process %d to terminate failed: %s\n"),
-                 (int)pid, strerror (errno));
-    }
-  else if (!i)
-    {
-      ec = GPG_ERR_TIMEOUT; /* Still running.  */
-    }
-  else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
-    {
-      log_error (_("error running '%s': probably not installed\n"), pgmname);
-      ec = GPG_ERR_CONFIGURATION;
-    }
-  else if (WIFEXITED (status) && WEXITSTATUS (status))
-    {
-      if (!r_exitcode)
-        log_error (_("error running '%s': exit status %d\n"), pgmname,
-                   WEXITSTATUS (status));
+      if (pid == (pid_t)(-1))
+        {
+          ec = gpg_err_code_from_errno (errno);
+          log_error (_("waiting for processes to terminate failed: %s\n"),
+                     strerror (errno));
+          break;
+        }
+      else if (!pid)
+        {
+          ec = GPG_ERR_TIMEOUT; /* Still running.  */
+          break;
+        }
       else
-        *r_exitcode = WEXITSTATUS (status);
-      ec = GPG_ERR_GENERAL;
-    }
-  else if (!WIFEXITED (status))
-    {
-      log_error (_("error running '%s': terminated\n"), pgmname);
-      ec = GPG_ERR_GENERAL;
-    }
-  else
-    {
-      if (r_exitcode)
-        *r_exitcode = 0;
-      ec = 0;
+        {
+          for (i = 0; i < count; i++)
+            if (pid == pids[i])
+              break;
+
+          if (i == count)
+            /* No match, ignore this pid.  */
+            continue;
+
+          /* Process PIDS[i] died.  */
+          left -= 1;
+
+          if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
+            {
+              log_error (_("error running '%s': probably not installed\n"),
+                         pgmnames[i]);
+              ec = GPG_ERR_CONFIGURATION;
+            }
+          else if (WIFEXITED (status) && WEXITSTATUS (status))
+            {
+              if (!r_exitcodes)
+                log_error (_("error running '%s': exit status %d\n"),
+                           pgmnames[i], WEXITSTATUS (status));
+              else
+                r_exitcodes[i] = WEXITSTATUS (status);
+              ec = GPG_ERR_GENERAL;
+            }
+          else if (!WIFEXITED (status))
+            {
+              log_error (_("error running '%s': terminated\n"), pgmnames[i]);
+              ec = GPG_ERR_GENERAL;
+            }
+          else
+            {
+              if (r_exitcodes)
+                r_exitcodes[i] = 0;
+            }
+        }
     }
 
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
index 68846f8..3e407d2 100644 (file)
@@ -682,64 +682,86 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
 gpg_error_t
 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
 {
-  gpg_err_code_t ec;
-  HANDLE proc = fd_to_handle (pid);
+  return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
+}
+
+/* See exechelp.h for a description.  */
+gpg_error_t
+gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
+                      int hang, int *r_exitcodes)
+{
+  gpg_err_code_t ec = 0;
+  size_t i;
+  HANDLE *procs;
   int code;
-  DWORD exc;
 
-  if (r_exitcode)
-    *r_exitcode = -1;
+  procs = xtrycalloc (count, sizeof *procs);
+  if (procs == NULL)
+    return gpg_error_from_syserror ();
+
+  for (i = 0; i < count; i++)
+    {
+      if (r_exitcodes)
+        r_exitcodes[i] = -1;
+
+      if (pids[i] == (pid_t)(-1))
+        return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (pid == (pid_t)(-1))
-    return gpg_error (GPG_ERR_INV_VALUE);
+      procs[i] = fd_to_handle (pids[i]);
+    }
 
   /* FIXME: We should do a pth_waitpid here.  However this has not yet
      been implemented.  A special W32 pth system call would even be
      better.  */
-  code = WaitForSingleObject (proc, hang? INFINITE : 0);
+  code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
   switch (code)
     {
     case WAIT_TIMEOUT:
       ec = GPG_ERR_TIMEOUT;
-      break;
+      goto leave;
 
     case WAIT_FAILED:
-      log_error (_("waiting for process %d to terminate failed: %s\n"),
-                 (int)pid, w32_strerror (-1));
+      log_error (_("waiting for processes to terminate failed: %s\n"),
+                 w32_strerror (-1));
       ec = GPG_ERR_GENERAL;
-      break;
+      goto leave;
 
     case WAIT_OBJECT_0:
-      if (!GetExitCodeProcess (proc, &exc))
+      for (i = 0; i < count; i++)
         {
-          log_error (_("error getting exit code of process %d: %s\n"),
-                     (int)pid, w32_strerror (-1) );
-          ec = GPG_ERR_GENERAL;
-        }
-      else if (exc)
-        {
-          if (!r_exitcode)
-            log_error (_("error running '%s': exit status %d\n"),
-                       pgmname, (int)exc);
+          DWORD exc;
+
+          if (! GetExitCodeProcess (procs[i], &exc))
+            {
+              log_error (_("error getting exit code of process %d: %s\n"),
+                         (int) pids[i], w32_strerror (-1) );
+              ec = GPG_ERR_GENERAL;
+            }
+          else if (exc)
+            {
+              if (!r_exitcodes)
+                log_error (_("error running '%s': exit status %d\n"),
+                           pgmnames[i], (int)exc);
+              else
+                r_exitcodes[i] = (int)exc;
+              ec = GPG_ERR_GENERAL;
+            }
           else
-            *r_exitcode = (int)exc;
-          ec = GPG_ERR_GENERAL;
-        }
-      else
-        {
-          if (r_exitcode)
-            *r_exitcode = 0;
-          ec = 0;
+            {
+              if (r_exitcodes)
+                r_exitcodes[i] = 0;
+            }
         }
       break;
 
     default:
-      log_error ("WaitForSingleObject returned unexpected "
-                 "code %d for pid %d\n", code, (int)pid );
+      log_error ("WaitForMultipleObjects returned unexpected "
+                 "code %d\n", code);
       ec = GPG_ERR_GENERAL;
       break;
     }
 
+ leave:
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
 }
 
index 710c828..e208f6e 100644 (file)
@@ -796,6 +796,15 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
 }
 
 
+/* See exechelp.h for a description.  */
+gpg_error_t
+gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
+                      int hang, int *r_exitcodes)
+{
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
+
+
 void
 gnupg_release_process (pid_t pid)
 {
index cdee300..82224fd 100644 (file)
@@ -161,6 +161,10 @@ gpg_error_t gnupg_spawn_process_fd (const char *pgmname,
 gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang,
                                 int *r_exitcode);
 
+/* Like gnupg_wait_process, but for COUNT processes.  */
+gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids,
+                                 size_t count, int hang, int *r_exitcodes);
+
 
 /* Kill a process; that is send an appropriate signal to the process.
    gnupg_wait_process must be called to actually remove the process