Merge branch 'STABLE-BRANCH-2-2'
[gnupg.git] / common / exechelp-w32ce.c
index 5a84c9f..ec9f014 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>
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
-#include <unistd.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
 #define handle_to_pid(a) ((int)(a))
 
 \f
-#ifdef USE_GNU_PTH      
-/* The data passed to the feeder_thread.  */ 
+#ifdef USE_NPTH
+/* The data passed to the feeder_thread.  */
 struct feeder_thread_parms
 {
   estream_t stream;
-  int fd;
+  volatile int stream_valid;
+  HANDLE hd;
   int direction;
 };
 
 
-/* The thread started by start_feeded.  */
+/* The thread started by start_feede3.  */
 static void *
 feeder_thread (void *arg)
 {
   struct feeder_thread_parms *parm = arg;
   char buffer[4096];
+  int rc;
 
   if (parm->direction)
     {
-      size_t nread;
+      size_t nread = 0;
       DWORD nwritten;
 
-      while (!es_read (parm->stream, buffer, sizeof buffer, &nread))
+      log_debug ("feeder_thread estream->pipe: stream=%p pipe=%p\n",
+                 parm->stream, parm->hd);
+      while (parm->stream_valid
+             && !es_read (parm->stream, buffer, sizeof buffer, &nread))
         {
           do
             {
-              if (!WriteFile (fd_to_handle (parm->fd), 
-                              buffer, nread, &nwritten, NULL))
+              pth_enter ();
+              rc = WriteFile (parm->hd, buffer, nread, &nwritten, NULL);
+              pth_leave ();
+              if (!rc)
                 {
-                  log_debug ("feeder(%d): WriteFile error: rc=%d\n",
-                             parm->fd, (int)GetLastError ());
+                  log_debug ("feeder(%p): WriteFile error: rc=%d\n",
+                             parm->hd, (int)GetLastError ());
                   goto leave;
                 }
               nread -= nwritten;
             }
           while (nread);
         }
-      if (nread)
-        log_debug ("feeder(%d): es_read error: %s\n",
-                   parm->fd, strerror (errno));
+      if (!parm->stream_valid)
+        log_debug ("feeder(%p): closed by other thread\n", parm->hd);
+      else if (nread)
+        log_debug ("feeder(%p): es_read error: %s\n",
+                   parm->hd, strerror (errno));
     }
   else
     {
-      DWORD nread;
+      DWORD nread = 0;
       size_t nwritten;
 
-      while (ReadFile (fd_to_handle (parm->fd),
-                       buffer, sizeof buffer, &nread, NULL) && nread)
+      log_debug ("feeder_thread pipe->estream: stream=%p pipe=%p\n",
+                 parm->stream, parm->hd);
+      while ( (pth_enter (),
+               (rc = ReadFile (parm->hd, buffer, sizeof buffer, &nread, NULL)),
+               pth_leave (),
+               rc) && nread)
         {
-          do 
+          log_debug ("feeder_thread pipe->estream: read %d bytes\n",
+                     (int)nread);
+          do
             {
-              if (es_write (parm->stream, buffer, nread, &nwritten))
+              if (parm->stream_valid
+                  && es_write (parm->stream, buffer, nread, &nwritten))
                 {
-                  log_debug ("feeder(%d): es_write error: %s\n",
-                             parm->fd, strerror (errno));
+                  log_debug ("feeder(%p): es_write error: %s\n",
+                             parm->hd, strerror (errno));
                   goto leave;
                 }
+              log_debug ("feeder_thread pipe->estream: es_wrote %d bytes\n",
+                         (int)nwritten);
               nread -= nwritten;
             }
-          while (nread);
+          while (nread && parm->stream_valid);
         }
-      if (nread)
-        log_debug ("feeder(%d): ReadFile error: rc=%d\n",
-                   parm->fd, (int)GetLastError ());
+      if (!parm->stream_valid)
+        log_debug ("feeder(%p): closed by other thread\n", parm->hd);
+      else if (nread)
+        log_debug ("feeder(%p): ReadFile error: rc=%d\n",
+                   parm->hd, (int)GetLastError ());
       else
-        log_debug ("feeder(%d): eof\n", parm->fd);
+        log_debug ("feeder(%p): eof\n", parm->hd);
     }
 
 leave:
-  CloseHandle (fd_to_handle (parm->fd));
+  log_debug ("feeder(%p): waiting for es_fclose\n", parm->hd);
+  while (parm->stream_valid)
+    pth_yield (NULL);
+  log_debug ("feeder(%p): about to close the pipe handle\n", parm->hd);
+  CloseHandle (parm->hd);
+  log_debug ("feeder(%p): pipe handle closed\n", parm->hd);
   xfree (parm);
   return NULL;
 }
-#endif /*USE_GNU_PTH*/
+#endif /*USE_NPTH*/
+
+#ifdef USE_NPTH
+static void
+feeder_onclose_notification (estream_t stream, void *opaque)
+{
+  struct feeder_thread_parms *parm = opaque;
+  (void)stream;
+  log_debug ("feeder(%p): received onclose note\n", parm->hd);
+  parm->stream_valid = 0;
+}
+#endif /*USE_NPTH*/
 
 /* Fire up a thread to copy data between STREAM and a pipe's
    descriptor FD.  With DIRECTION set to true the copy takes place
    from the stream to the pipe, otherwise from the pipe to the
    stream.  */
 static gpg_error_t
-start_feeder (estream_t stream, int fd, int direction)
+start_feeder (estream_t stream, HANDLE hd, int direction)
 {
-#ifdef USE_GNU_PTH      
+#ifdef USE_NPTH
   gpg_error_t err;
   struct feeder_thread_parms *parm;
   pth_attr_t tattr;
-  
+
   parm = xtrymalloc (sizeof *parm);
   if (!parm)
     return gpg_error_from_syserror ();
   parm->stream = stream;
-  parm->fd = fd;
+  parm->stream_valid = 1;
+  parm->hd = hd;
   parm->direction = direction;
-  
+
+  if (es_onclose (stream, 1, feeder_onclose_notification, parm))
+    {
+      err = gpg_error_from_syserror ();
+      xfree (parm);
+      return err;
+    }
+
   tattr = pth_attr_new ();
   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
   pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
   pth_attr_set (tattr, PTH_ATTR_NAME, "exec-feeder");
-  
-  log_error ("spawning new feeder(%p, %d, %d)\n", stream, fd, direction);
+
+  log_debug ("spawning new feeder(%p, %p, %d)\n", stream, hd, direction);
   if(!pth_spawn (tattr, feeder_thread, parm))
     {
       err = gpg_error_from_syserror ();
-      log_error ("error spawning feeder: %s\n", gpg_strerror (err));
+      es_onclose (stream, 0, feeder_onclose_notification, parm);
       xfree (parm);
     }
   else
@@ -186,7 +240,7 @@ start_feeder (estream_t stream, int fd, int direction)
   return err;
 #else
   (void)stream;
-  (void)fd;
+  (void)hd;
   (void)direction;
   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);  /* No Pth.  */
 #endif
@@ -214,50 +268,19 @@ get_max_fds (void)
 }
 
 
-/* Close all file descriptors starting with descriptor FIRST.  If
-   EXCEPT is not NULL, it is expected to be a list of file descriptors
-   which shall not be closed.  This list shall be sorted in ascending
-   order with the end marked by -1.  */
+/* Under Windows this is a dummy function.  */
 void
 close_all_fds (int first, int *except)
 {
-  int max_fd = get_max_fds ();
-  int fd, i, except_start;
-
-  if (except)
-    {
-      except_start = 0;
-      for (fd=first; fd < max_fd; fd++)
-        {
-          for (i=except_start; except[i] != -1; i++)
-            {
-              if (except[i] == fd)
-                {
-                  /* If we found the descriptor in the exception list
-                     we can start the next compare run at the next
-                     index because the exception list is ordered.  */
-                except_start = i + 1;
-                break;
-                }
-            }
-          if (except[i] == -1)
-            close (fd);
-        }
-    }
-  else
-    {
-      for (fd=first; fd < max_fd; fd++)
-        close (fd);
-    }
-
-  gpg_err_set_errno (0);
+  (void)first;
+  (void)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
+   use of this function right at startup even before libgcrypt has
    been initialized.  Returns NULL on error and sets ERRNO
    accordingly.  */
 int *
@@ -278,7 +301,7 @@ get_all_open_fds (void)
   array = calloc (narray, sizeof *array);
   if (!array)
     return NULL;
-  
+
   /* Note:  The list we return is ordered.  */
   for (idx=0, fd=0; fd < max_fd; fd++)
     if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
@@ -335,9 +358,7 @@ copy_quoted (char *p, const char *string)
    CMDLINE gets the address of a newly allocated string.  */
 static int
 build_w32_commandline (const char * const *argv,
-                      int fd0, int fd0_isnull,
-                       int fd1, int fd1_isnull,
-                       int fd2, int fd2_isnull,
+                      int rvid0, int rvid1, int rvid2,
                        char **cmdline)
 {
   int i, n;
@@ -347,31 +368,25 @@ build_w32_commandline (const char * const *argv,
 
   p = fdbuf;
   *p = 0;
-  if (fd0)
-    {
-      if (fd0_isnull)
-        strcpy (p, "-&S0=null ");
-      else
-        snprintf (p, 25, "-&S0=%d ", fd0);
-      p += strlen (p);
-    }
-  if (fd1)
-    {
-      if (fd1_isnull)
-        strcpy (p, "-&S1=null ");
-      else
-        snprintf (p, 25, "-&S1=%d ", fd1);
-      p += strlen (p);
-    }
-  if (fd2)
-    {
-      if (fd2_isnull)
-        strcpy (p, "-&S2=null ");
-      else
-        snprintf (p, 25, "-&S2=%d ", fd2);
-      p += strlen (p);
-    }
-  
+
+  if (rvid0)
+    snprintf (p, 25, "-&S0=%d ", rvid0);
+  else
+    strcpy (p, "-&S0=null ");
+  p += strlen (p);
+
+  if (rvid1)
+    snprintf (p, 25, "-&S1=%d ", rvid1);
+  else
+    strcpy (p, "-&S1=null ");
+  p += strlen (p);
+
+  if (rvid2)
+    snprintf (p, 25, "-&S2=%d ", rvid2);
+  else
+    strcpy (p, "-&S2=null ");
+  p += strlen (p);
+
   *cmdline = NULL;
   n = strlen (fdbuf);
   for (i=0; (s = argv[i]); i++)
@@ -388,7 +403,7 @@ build_w32_commandline (const char * const *argv,
     return -1;
 
   p = stpcpy (p, fdbuf);
-  for (i = 0; argv[i]; i++) 
+  for (i = 0; argv[i]; i++)
     {
       *p++ = ' ';
       p = copy_quoted (p, argv[i]);
@@ -435,18 +450,33 @@ create_inheritable_pipe (int filedes[2], int inherit_idx)
 /* Portable function to create a pipe.  Under Windows the write end is
    inheritable (i.e. an rendezvous id).  */
 gpg_error_t
-gnupg_create_inbound_pipe (int filedes[2])
+gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
 {
-  return create_inheritable_pipe (filedes, 1);
+  if (r_fp)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+  else
+    return create_inheritable_pipe (filedes, 1);
 }
 
 
 /* Portable function to create a pipe.  Under Windows the read end is
    inheritable (i.e. an rendezvous id).  */
 gpg_error_t
-gnupg_create_outbound_pipe (int filedes[2])
+gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
 {
-  return create_inheritable_pipe (filedes, 0);
+  if (r_fp)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+  else
+    return create_inheritable_pipe (filedes, 0);
+}
+
+
+/* Portable function to create a pipe.  Under Windows both ends are
+   inheritable.  */
+gpg_error_t
+gnupg_create_pipe (int filedes[2])
+{
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 }
 
 
@@ -485,120 +515,174 @@ create_process (const char *pgmname, const char *cmdline,
 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
 gpg_error_t
 gnupg_spawn_process (const char *pgmname, const char *argv[],
-                     estream_t infile, estream_t outfile,
-                     void (*preexec)(void), unsigned int flags,
-                     estream_t *statusfile, pid_t *pid)
+                     int *except, void (*preexec)(void), unsigned int flags,
+                     estream_t *r_infp,
+                     estream_t *r_outfp,
+                     estream_t *r_errfp,
+                     pid_t *pid)
 {
   gpg_error_t err;
   PROCESS_INFORMATION pi = {NULL };
   char *cmdline;
-  int inpipe[2], outpipe[2], errpipe[2];
-
+  es_syshd_t syshd;
+  struct {
+    HANDLE hd;
+    int rvid;
+  } inpipe = {INVALID_HANDLE_VALUE, 0};
+  struct {
+    HANDLE hd;
+    int rvid;
+  } outpipe = {INVALID_HANDLE_VALUE, 0};
+  struct {
+    HANDLE hd;
+    int rvid;
+  } errpipe = {INVALID_HANDLE_VALUE, 0};
+  estream_t outfp = NULL;
+  estream_t errfp = NULL;
+  gpg_err_source_t errsource = default_errsource;
+
+  (void)except; /* Not yet used.  */
   (void)preexec;
   (void)flags;
-  
-  /* Setup return values.  */
-  *statusfile = NULL;
-  *pid = (pid_t)(-1);
 
-  /* A NULL INFILE or OUTFILE is only used by gpgtar thus we don't
-     need to implement this for CE.  */
-  if (!infile || !outfile)
-    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-
-  es_fflush (infile);
-  es_rewind (infile);
-
-  /* Create a pipe to copy our infile to the stdin of the child
-     process.  On success inpipe[1] is owned by the feeder.  */
-  err = create_inheritable_pipe (inpipe, 0);
-  if (err)
-    {
-      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
-      return err;
-    }
-  err = start_feeder (infile, inpipe[1], 1);
-  if (err)
+  /* Setup return values.  */
+  if (r_outfp)
+    *r_outfp = NULL;
+  if (r_errfp)
+    *r_errfp = NULL;
+  *pid = (pid_t)(-1); /* Always required.  */
+
+  log_debug ("%s: enter\n", __func__);
+  if (infp)
     {
-      log_error (_("error spawning feeder: %s\n"), gpg_strerror (err));
-      CloseHandle (fd_to_handle (inpipe[1]));
-      return err;
+      es_fflush (infp);
+      es_rewind (infp);
+
+      /* Create a pipe to copy our infile to the stdin of the child
+         process.  On success inpipe.hd is owned by the feeder.  */
+      inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1);
+      if (inpipe.hd == INVALID_HANDLE_VALUE)
+        {
+          log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
+                     w32_strerror (-1));
+          gpg_err_set_errno (EIO);
+          return gpg_error_from_syserror ();
+        }
+      log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__,
+                 infp, inpipe.hd, inpipe.rvid);
+      err = start_feeder (infp, inpipe.hd, 1);
+      if (err)
+        {
+          log_error ("error spawning feeder: %s\n", gpg_strerror (err));
+          CloseHandle (inpipe.hd);
+          return err;
+        }
+      inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder.  */
+      log_debug ("%s: inpipe %p created; feeder started\n", __func__,
+                 infp);
     }
 
-  /* Create a pipe to copy stdout of the child process to our
-     outfile. On success outpipe[0] is owned by the feeded.  */
-  err = create_inheritable_pipe (outpipe, 1);
-  if (err)
+  if (r_outfp)
     {
-      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
-      return err;
+      /* Create a pipe to make the stdout of the child process
+         available as a stream.  */
+      outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0);
+      if (outpipe.hd == INVALID_HANDLE_VALUE)
+        {
+          log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
+                     w32_strerror (-1));
+          gpg_err_set_errno (EIO);
+          /* Fixme release other stuff/kill feeder.  */
+          return gpg_error_from_syserror ();
+        }
+      syshd.type = ES_SYSHD_HANDLE;
+      syshd.u.handle = outpipe.hd;
+      err = 0;
+      outfp = es_sysopen (&syshd, "r");
+      if (!outfp)
+        {
+          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+          log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
+          CloseHandle (outpipe.hd);
+          return err;
+        }
+      log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__,
+                 outfp, outpipe.hd, outpipe.rvid);
+      outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP.  */
     }
-  err = start_feeder (outfile, outpipe[0], 0);
-  if (err)
+
+  if (r_errfp)
     {
-      log_error (_("error spawning feeder: %s\n"), gpg_strerror (err));
-      CloseHandle (fd_to_handle (outpipe[0]));
-      return err;
+      /* Create a pipe to make the stderr of the child process
+         available as a stream.  */
+      errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0);
+      if (errpipe.hd == INVALID_HANDLE_VALUE)
+        {
+          log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
+                     w32_strerror (-1));
+          gpg_err_set_errno (EIO);
+          /* Fixme release other stuff/kill feeder.  */
+          return gpg_error_from_syserror ();
+        }
+      syshd.type = ES_SYSHD_HANDLE;
+      syshd.u.handle = errpipe.hd;
+      err = 0;
+      errfp = es_sysopen (&syshd, "r");
+      if (!errfp)
+        {
+          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+          log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
+          CloseHandle (errpipe.hd);
+          return err;
+        }
+      log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__,
+                 errfp, errpipe.hd, errpipe.rvid);
+      errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP.  */
     }
 
 
-  /* Create a pipe for use with stderr of the child process.  */
-  err = create_inheritable_pipe (errpipe, 1);
-  if (err)
-    {
-      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
-      return err;
-    }
 
   /* Build the command line.  */
-  err = build_w32_commandline (argv,
-                               inpipe[0], 0,
-                               outpipe[1], 0,
-                               errpipe[1], 0,
+  err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid,
                                &cmdline);
   if (err)
     {
-      CloseHandle (fd_to_handle (errpipe[0]));
-      return err; 
+      /* Fixme release other stuff/kill feeder.  */
+      CloseHandle (errpipe.hd);
+      return err;
     }
 
-  
-  log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
+  log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
   if (!create_process (pgmname, cmdline, &pi))
     {
       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
       xfree (cmdline);
-      CloseHandle (fd_to_handle (errpipe[0]));
+      /* Fixme release other stuff/kill feeder.  */
+      CloseHandle (errpipe.hd);
       return gpg_error (GPG_ERR_GENERAL);
     }
   xfree (cmdline);
   cmdline = NULL;
 
   /* Note: The other end of the pipe is a rendezvous id and thus there
-     is no need to close.  */
+     is no need for a close.  */
 
   log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
              " dwProcessID=%d dwThreadId=%d\n",
              pi.hProcess, pi.hThread,
              (int) pi.dwProcessId, (int) pi.dwThreadId);
-  
+
 
   /* Process has been created suspended; resume it now. */
   ResumeThread (pi.hThread);
-  CloseHandle (pi.hThread); 
-
-  *statusfile = es_fdopen (handle_to_fd (errpipe[0]), "r");
-  if (!*statusfile)
-    {
-      err = gpg_error_from_syserror ();
-      log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err));
-      CloseHandle (pi.hProcess);
-      return err;
-    }
+  CloseHandle (pi.hThread);
 
+  if (r_outfp)
+    *r_outfp = outfp;
+  if (r_errfp)
+    *r_errfp = errfp;
   *pid = handle_to_pid (pi.hProcess);
   return 0;
-
 }
 
 
@@ -626,11 +710,11 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
     return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
   /* Build the command line.  */
-  err = build_w32_commandline (argv, -1, 1, -1, 1, -1, 1, &cmdline);
+  err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
   if (err)
-    return err; 
+    return err;
 
-  log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
+  log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
   if (!create_process (pgmname, cmdline, &pi))
     {
       log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1));
@@ -644,10 +728,10 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
              " dwProcessID=%d dwThreadId=%d\n",
              pi.hProcess, pi.hThread,
              (int) pi.dwProcessId, (int) pi.dwThreadId);
-  
+
   /* Process has been created suspended; resume it now. */
   ResumeThread (pi.hThread);
-  CloseHandle (pi.hThread); 
+  CloseHandle (pi.hThread);
 
   *pid = handle_to_pid (pi.hProcess);
   return 0;
@@ -673,12 +757,12 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
      been implemented.  A special W32 pth system call would even be
      better.  */
   code = WaitForSingleObject (proc, hang? INFINITE : 0);
-  switch (code) 
+  switch (code)
     {
     case WAIT_TIMEOUT:
       ec = GPG_ERR_TIMEOUT;
       break;
-      
+
     case WAIT_FAILED:
       log_error (_("waiting for process %d to terminate failed: %s\n"),
                  (int)pid, w32_strerror (-1));
@@ -694,7 +778,7 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
           }
       else if (exc)
         {
-          log_error (_("error running `%s': exit status %d\n"),
+          log_error (_("error running '%s': exit status %d\n"),
                        pgmname, (int)exc );
           if (exitcode)
             *exitcode = (int)exc;
@@ -707,7 +791,7 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
           ec = 0;
         }
       break;
-      
+
     default:
       log_error ("WaitForSingleObject returned unexpected "
                  "code %d for pid %d\n", code, (int)pid );
@@ -719,19 +803,28 @@ 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)
 {
   if (pid != (pid_t)INVALID_HANDLE_VALUE)
     {
       HANDLE process = (HANDLE)pid;
-      
+
       CloseHandle (process);
     }
 }
 
 
-/* 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
@@ -747,14 +840,14 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
   PROCESS_INFORMATION pi = {NULL };
 
   (void)envp;
-  
+
   /* Build the command line.  */
-  err = build_w32_commandline (argv, -1, 1, -1, 1, -1, 1, &cmdline);
+  err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
   if (err)
-    return err; 
+    return err;
 
   /* Note: There is no detached flag under CE.  */
-  log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
+  log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
   if (!create_process (pgmname, cmdline, &pi))
     {
       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
@@ -768,10 +861,10 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
              " dwProcessID=%d dwThreadId=%d\n",
              pi.hProcess, pi.hThread,
              (int) pi.dwProcessId, (int) pi.dwThreadId);
-  
+
   /* Process has been created suspended; resume it now. */
   ResumeThread (pi.hThread);
-  CloseHandle (pi.hThread); 
+  CloseHandle (pi.hThread);
 
   return 0;
 }
@@ -786,7 +879,7 @@ gnupg_kill_process (pid_t pid)
   if (pid != (pid_t) INVALID_HANDLE_VALUE)
     {
       HANDLE process = (HANDLE) pid;
-      
+
       /* Arbitrary error code.  */
       TerminateProcess (process, 1);
     }