tests: Fix t-gettime for a time_t of 64 and a long of 32 bit.
[gnupg.git] / common / exechelp-w32.c
index a8fbd15..e79ee5b 100644 (file)
@@ -4,18 +4,28 @@
  *
  * This file is part of GnuPG.
  *
- * GnuPG 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 3 of the License, or
- * (at your option) any later version.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
  *
- * GnuPG is distributed in the hope that it will be useful,
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - 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.
+ *
+ * or both in parallel, as here.
+ *
+ * This file 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <unistd.h>
 #include <fcntl.h>
 
-#ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth.  */
-#undef HAVE_PTH
-#undef USE_GNU_PTH
+#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth.  */
+#undef HAVE_NPTH
+#undef USE_NPTH
 #endif
 
-#ifdef USE_GNU_PTH
-#include <pth.h>
+#ifdef HAVE_NPTH
+#include <npth.h>
 #endif
 
 #ifdef HAVE_STAT
@@ -55,7 +65,7 @@
 #include "exechelp.h"
 
 /* Define to 1 do enable debugging.  */
-#define DEBUG_W32_SPAWN 1
+#define DEBUG_W32_SPAWN 0
 
 
 /* It seems Vista doesn't grok X_OK and so fails access() tests.
 # define handle_to_pid(a) ((int)(a))
 
 
+/* Helper */
+static inline gpg_error_t
+my_error_from_syserror (void)
+{
+  return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+}
+
+static inline gpg_error_t
+my_error (int errcode)
+{
+  return gpg_err_make (default_errsource, errcode);
+}
+
+
 /* Return the maximum number of currently allowed open file
    descriptors.  Only useful on POSIX systems but returns a value on
    other systems too.  */
@@ -104,11 +128,14 @@ close_all_fds (int first, int *except)
 
 
 /* Returns an array with all currently open file descriptors.  The end
-   of the array is marked by -1.  The caller needs to release this
-   array using the *standard free* and not with xfree.  This allow the
-   use of this fucntion right at startup even before libgcrypt has
-   been initialized.  Returns NULL on error and sets ERRNO
-   accordingly.  */
+ * of the array is marked by -1.  The caller needs to release this
+ * array using the *standard free* and not with xfree.  This allow the
+ * use of this function right at startup even before libgcrypt has
+ * been initialized.  Returns NULL on error and sets ERRNO
+ * accordingly.  Note that fstat prints a warning to DebugView for all
+ * invalid fds which is a bit annoying.  We actually do not need this
+ * function in real code (close_all_fds is a dummy anyway) but we keep
+ * it for use by t-exechelp.c.  */
 int *
 get_all_open_fds (void)
 {
@@ -209,7 +236,7 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
 
   buf = p = xtrymalloc (n);
   if (!buf)
-    return gpg_error_from_syserror ();
+    return my_error_from_syserror ();
 
   p = build_w32_commandline_copy (p, pgmname);
   for (i=0; argv[i]; i++)
@@ -223,45 +250,41 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
 }
 
 
-/* Create pipe where one end is inheritable: With an INHERIT_IDX of 0
-   the read end is inheritable, with 1 the write end is inheritable.  */
+#define INHERIT_READ   1
+#define INHERIT_WRITE  2
+#define INHERIT_BOTH   (INHERIT_READ|INHERIT_WRITE)
+
+/* Create pipe.  FLAGS indicates which ends are inheritable.  */
 static int
-create_inheritable_pipe (HANDLE filedes[2], int inherit_idx)
+create_inheritable_pipe (HANDLE filedes[2], int flags)
 {
-  HANDLE r, w, h;
+  HANDLE r, w;
   SECURITY_ATTRIBUTES sec_attr;
 
   memset (&sec_attr, 0, sizeof sec_attr );
   sec_attr.nLength = sizeof sec_attr;
-  sec_attr.bInheritHandle = FALSE;
+  sec_attr.bInheritHandle = TRUE;
 
   if (!CreatePipe (&r, &w, &sec_attr, 0))
     return -1;
 
-  if (!DuplicateHandle (GetCurrentProcess(), inherit_idx? w : r,
-                        GetCurrentProcess(), &h, 0,
-                        TRUE, DUPLICATE_SAME_ACCESS ))
-    {
-      log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
-      CloseHandle (r);
-      CloseHandle (w);
-      return -1;
-    }
+  if ((flags & INHERIT_READ) == 0)
+    if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0))
+      goto fail;
 
-  if (inherit_idx)
-    {
-      CloseHandle (w);
-      w = h;
-    }
-  else
-    {
-      CloseHandle (r);
-      r = h;
-    }
+  if ((flags & INHERIT_WRITE) == 0)
+    if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0))
+      goto fail;
 
   filedes[0] = r;
   filedes[1] = w;
   return 0;
+
+ fail:
+  log_error ("SetHandleInformation failed: %s\n", w32_strerror (-1));
+  CloseHandle (r);
+  CloseHandle (w);
+  return -1;
 }
 
 
@@ -275,22 +298,23 @@ w32_open_null (int for_write)
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL, OPEN_EXISTING, 0, NULL);
   if (hfile == INVALID_HANDLE_VALUE)
-    log_debug ("can't open `nul': %s\n", w32_strerror (-1));
+    log_debug ("can't open 'nul': %s\n", w32_strerror (-1));
   return hfile;
 }
 
 
 static gpg_error_t
-do_create_pipe (int filedes[2], int inherit_idx)
+create_pipe_and_estream (int filedes[2], int flags,
+                         estream_t *r_fp, int outbound, int nonblock)
 {
   gpg_error_t err = 0;
   HANDLE fds[2];
 
   filedes[0] = filedes[1] = -1;
-  err = gpg_error (GPG_ERR_GENERAL);
-  if (!create_inheritable_pipe (fds, inherit_idx))
+  err = my_error (GPG_ERR_GENERAL);
+  if (!create_inheritable_pipe (fds, flags))
     {
-      filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), 0);
+      filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
       if (filedes[0] == -1)
         {
           log_error ("failed to translate osfhandle %p\n", fds[0]);
@@ -298,7 +322,7 @@ do_create_pipe (int filedes[2], int inherit_idx)
         }
       else
         {
-          filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), 1);
+          filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND);
           if (filedes[1] == -1)
             {
               log_error ("failed to translate osfhandle %p\n", fds[1]);
@@ -310,33 +334,65 @@ do_create_pipe (int filedes[2], int inherit_idx)
             err = 0;
         }
     }
+
+  if (! err && r_fp)
+    {
+      if (!outbound)
+        *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
+      else
+        *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
+      if (!*r_fp)
+        {
+          err = my_error_from_syserror ();
+          log_error (_("error creating a stream for a pipe: %s\n"),
+                     gpg_strerror (err));
+          close (filedes[0]);
+          close (filedes[1]);
+          filedes[0] = filedes[1] = -1;
+          return err;
+        }
+    }
+
   return err;
 }
 
 /* Portable function to create a pipe.  Under Windows the write end is
-   inheritable.  */
+   inheritable.  If R_FP is not NULL, an estream is created for the
+   read end and stored at R_FP.  */
 gpg_error_t
-gnupg_create_inbound_pipe (int filedes[2])
+gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
 {
-  return do_create_pipe (filedes, 1);
+  return create_pipe_and_estream (filedes, INHERIT_WRITE,
+                                  r_fp, 0, nonblock);
 }
 
 
 /* Portable function to create a pipe.  Under Windows the read end is
+   inheritable.  If R_FP is not NULL, an estream is created for the
+   write end and stored at R_FP.  */
+gpg_error_t
+gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
+{
+  return create_pipe_and_estream (filedes, INHERIT_READ,
+                                  r_fp, 1, nonblock);
+}
+
+
+/* Portable function to create a pipe.  Under Windows both ends are
    inheritable.  */
 gpg_error_t
-gnupg_create_outbound_pipe (int filedes[2])
+gnupg_create_pipe (int filedes[2])
 {
-  return do_create_pipe (filedes, 0);
+  return create_pipe_and_estream (filedes, INHERIT_BOTH,
+                                  NULL, 0, 0);
 }
 
 
 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
 gpg_error_t
 gnupg_spawn_process (const char *pgmname, const char *argv[],
-                     gpg_err_source_t errsource,
-                     void (*preexec)(void), unsigned int flags,
-                     estream_t infp,
+                     int *except, void (*preexec)(void), unsigned int flags,
+                     estream_t *r_infp,
                      estream_t *r_outfp,
                      estream_t *r_errfp,
                      pid_t *pid)
@@ -353,9 +409,10 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   STARTUPINFO si;
   int cr_flags;
   char *cmdline;
-  HANDLE inhandle = INVALID_HANDLE_VALUE;
+  HANDLE inpipe[2]  = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
   HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
   HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
+  estream_t infp = NULL;
   estream_t outfp = NULL;
   estream_t errfp = NULL;
   HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
@@ -363,43 +420,46 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
                       INVALID_HANDLE_VALUE};
   int i;
   es_syshd_t syshd;
+  gpg_err_source_t errsource = default_errsource;
+  int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+
+  (void)except; /* Not yet used.  */
 
+  if (r_infp)
+    *r_infp = NULL;
   if (r_outfp)
     *r_outfp = NULL;
   if (r_errfp)
     *r_errfp = NULL;
   *pid = (pid_t)(-1); /* Always required.  */
 
-  if (infp)
+  if (r_infp)
     {
-      es_fflush (infp);
-      es_rewind (infp);
-      es_syshd (infp, &syshd);
-      switch (syshd.type)
+      if (create_inheritable_pipe (inpipe, INHERIT_READ))
         {
-        case ES_SYSHD_FD:
-          inhandle = (HANDLE)_get_osfhandle (syshd.u.fd);
-          break;
-        case ES_SYSHD_SOCK:
-          inhandle = (HANDLE)_get_osfhandle (syshd.u.sock);
-          break;
-        case ES_SYSHD_HANDLE:
-          inhandle = syshd.u.handle;
-          break;
-        default:
-          inhandle = INVALID_HANDLE_VALUE;
-          break;
+          err = gpg_err_make (errsource, GPG_ERR_GENERAL);
+          log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
+          return err;
+        }
+
+      syshd.type = ES_SYSHD_HANDLE;
+      syshd.u.handle = inpipe[1];
+      infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
+      if (!infp)
+        {
+          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+          log_error (_("error creating a stream for a pipe: %s\n"),
+                     gpg_strerror (err));
+          CloseHandle (inpipe[0]);
+          CloseHandle (inpipe[1]);
+          inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
+          return err;
         }
-      if (inhandle == INVALID_HANDLE_VALUE)
-        return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
-      /* FIXME: In case we can't get a system handle (e.g. due to
-         es_fopencookie we should create a piper and a feeder
-         thread.  */
     }
 
   if (r_outfp)
     {
-      if (create_inheritable_pipe (outpipe, 1))
+      if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
         {
           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -408,7 +468,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
       syshd.type = ES_SYSHD_HANDLE;
       syshd.u.handle = outpipe[0];
-      outfp = es_sysopen (&syshd, "r");
+      outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
       if (!outfp)
         {
           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
@@ -417,13 +477,19 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
           CloseHandle (outpipe[0]);
           CloseHandle (outpipe[1]);
           outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
+          if (infp)
+            es_fclose (infp);
+          else if (inpipe[1] != INVALID_HANDLE_VALUE)
+            CloseHandle (inpipe[1]);
+          if (inpipe[0] != INVALID_HANDLE_VALUE)
+            CloseHandle (inpipe[0]);
           return err;
         }
     }
 
   if (r_errfp)
     {
-      if (create_inheritable_pipe (errpipe, 1))
+      if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
         {
           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -432,7 +498,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
       syshd.type = ES_SYSHD_HANDLE;
       syshd.u.handle = errpipe[0];
-      errfp = es_sysopen (&syshd, "r");
+      errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
       if (!errfp)
         {
           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
@@ -447,6 +513,12 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
             CloseHandle (outpipe[0]);
           if (outpipe[1] != INVALID_HANDLE_VALUE)
             CloseHandle (outpipe[1]);
+          if (infp)
+            es_fclose (infp);
+          else if (inpipe[1] != INVALID_HANDLE_VALUE)
+            CloseHandle (inpipe[1]);
+          if (inpipe[0] != INVALID_HANDLE_VALUE)
+            CloseHandle (inpipe[0]);
           return err;
         }
     }
@@ -461,12 +533,12 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   if (err)
     return err;
 
-  if (inhandle != INVALID_HANDLE_VALUE)
+  if (inpipe[0] == INVALID_HANDLE_VALUE)
     nullhd[0] = w32_open_null (0);
-  if (outpipe[1] != INVALID_HANDLE_VALUE)
-    nullhd[1] = w32_open_null (0);
-  if (errpipe[1] != INVALID_HANDLE_VALUE)
-    nullhd[2] = w32_open_null (0);
+  if (outpipe[1] == INVALID_HANDLE_VALUE)
+    nullhd[1] = w32_open_null (1);
+  if (errpipe[1] == INVALID_HANDLE_VALUE)
+    nullhd[2] = w32_open_null (1);
 
   /* Start the process.  Note that we can't run the PREEXEC function
      because this might change our own environment. */
@@ -476,15 +548,15 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   si.cb = sizeof (si);
   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
-  si.hStdInput  =   inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle;
+  si.hStdInput  = inpipe[0]  == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
   si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
   si.hStdError  = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
 
   cr_flags = (CREATE_DEFAULT_ERROR_MODE
-              | ((flags & 128)? DETACHED_PROCESS : 0)
+              | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
               | GetPriorityClass (GetCurrentProcess ())
               | CREATE_SUSPENDED);
-/*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
+/*   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
   if (!CreateProcess (pgmname,       /* Program to start.  */
                       cmdline,       /* Command line arguments.  */
                       &sec_attr,     /* Process security attributes.  */
@@ -499,6 +571,12 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
     {
       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
       xfree (cmdline);
+      if (infp)
+        es_fclose (infp);
+      else if (inpipe[1] != INVALID_HANDLE_VALUE)
+        CloseHandle (outpipe[1]);
+      if (inpipe[0] != INVALID_HANDLE_VALUE)
+        CloseHandle (inpipe[0]);
       if (outfp)
         es_fclose (outfp);
       else if (outpipe[0] != INVALID_HANDLE_VALUE)
@@ -522,6 +600,8 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
       CloseHandle (nullhd[i]);
 
   /* Close the inherited ends of the pipes.  */
+  if (inpipe[0] != INVALID_HANDLE_VALUE)
+    CloseHandle (inpipe[0]);
   if (outpipe[1] != INVALID_HANDLE_VALUE)
     CloseHandle (outpipe[1]);
   if (errpipe[1] != INVALID_HANDLE_VALUE)
@@ -536,13 +616,15 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
      invalid argument error if we pass it the correct processID.  As a
      workaround we use -1 (ASFW_ANY).  */
-  if ( (flags & 64) )
+  if ((flags & GNUPG_SPAWN_RUN_ASFW))
     gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
 
   /* Process has been created suspended; resume it now. */
   ResumeThread (pi.hThread);
   CloseHandle (pi.hThread);
 
+  if (r_infp)
+    *r_infp = infp;
   if (r_outfp)
     *r_outfp = outfp;
   if (r_errfp)
@@ -599,7 +681,7 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
   si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
   si.hStdError  = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
 
-/*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
+/*   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
   if (!CreateProcess (pgmname,       /* Program to start.  */
                       cmdline,       /* Command line arguments.  */
                       &sec_attr,     /* Process security attributes.  */
@@ -615,7 +697,7 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
                       ))
     {
       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
-      err = gpg_error (GPG_ERR_GENERAL);
+      err = my_error (GPG_ERR_GENERAL);
     }
   else
     err = 0;
@@ -645,63 +727,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 my_error_from_syserror ();
 
-  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 my_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))
-        {
-          log_error (_("error getting exit code of process %d: %s\n"),
-                     (int)pid, w32_strerror (-1) );
-          ec = GPG_ERR_GENERAL;
-        }
-      else if (exc)
+      for (i = 0; i < count; i++)
         {
-          log_error (_("error running `%s': exit status %d\n"),
-                     pgmname, (int)exc );
-          if (r_exitcode)
-            *r_exitcode = (int)exc;
-          ec = GPG_ERR_GENERAL;
-        }
-      else
-        {
-          if (r_exitcode)
-            *r_exitcode = 0;
-          ec = 0;
+          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
+            {
+              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);
 }
 
@@ -719,7 +824,7 @@ gnupg_release_process (pid_t pid)
 }
 
 
-/* Spawn a new process and immediatley detach from it.  The name of
+/* Spawn a new process and immediately detach from it.  The name of
    the program to exec is PGMNAME and its arguments are in ARGV (the
    programname is automatically passed as first argument).
    Environment strings in ENVP are set.  An error is returned if
@@ -744,14 +849,11 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
   char *cmdline;
 
 
-  /* FIXME: We don't make use of ENVP yet.  It is currently only used
-     to pass the GPG_AGENT_INFO variable to gpg-agent.  As the default
-     on windows is to use a standard socket, this does not really
-     matter.  */
+  /* We don't use ENVP.  */
   (void)envp;
 
   if (access (pgmname, X_OK))
-    return gpg_error_from_syserror ();
+    return my_error_from_syserror ();
 
   /* Prepare security attributes.  */
   memset (&sec_attr, 0, sizeof sec_attr );
@@ -773,7 +875,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
               | GetPriorityClass (GetCurrentProcess ())
               | CREATE_NEW_PROCESS_GROUP
               | DETACHED_PROCESS);
-/*   log_debug ("CreateProcess(detached), path=`%s' cmdline=`%s'\n", */
+/*   log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
 /*              pgmname, cmdline); */
   if (!CreateProcess (pgmname,       /* Program to start.  */
                       cmdline,       /* Command line arguments.  */
@@ -789,7 +891,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
     {
       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
       xfree (cmdline);
-      return gpg_error (GPG_ERR_GENERAL);
+      return my_error (GPG_ERR_GENERAL);
     }
   xfree (cmdline);
   cmdline = NULL;
@@ -800,6 +902,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
 
   CloseHandle (pi.hThread);
+  CloseHandle (pi.hProcess);
 
   return 0;
 }