common: Correctly handle modules relying on npth.
[gnupg.git] / common / exechelp-w32ce.c
index 208eeb9..9e72cef 100644 (file)
@@ -4,12 +4,22 @@
  *
  * 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.
 #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
 # include <sys/stat.h>
 #endif
 
+#include <assuan.h>
 
 #include "util.h"
 #include "i18n.h"
 #define pid_to_handle(a) ((HANDLE)(a))
 #define handle_to_pid(a) ((int)(a))
 
+\f
+#ifdef USE_NPTH
+/* The data passed to the feeder_thread.  */
+struct feeder_thread_parms
+{
+  estream_t stream;
+  volatile int stream_valid;
+  HANDLE hd;
+  int direction;
+};
+
+
+/* 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 = 0;
+      DWORD nwritten;
+
+      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
+            {
+              pth_enter ();
+              rc = WriteFile (parm->hd, buffer, nread, &nwritten, NULL);
+              pth_leave ();
+              if (!rc)
+                {
+                  log_debug ("feeder(%p): WriteFile error: rc=%d\n",
+                             parm->hd, (int)GetLastError ());
+                  goto leave;
+                }
+              nread -= nwritten;
+            }
+          while (nread);
+        }
+      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 = 0;
+      size_t nwritten;
+
+      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)
+        {
+          log_debug ("feeder_thread pipe->estream: read %d bytes\n",
+                     (int)nread);
+          do
+            {
+              if (parm->stream_valid
+                  && es_write (parm->stream, buffer, nread, &nwritten))
+                {
+                  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 && parm->stream_valid);
+        }
+      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(%p): eof\n", parm->hd);
+    }
+
+leave:
+  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_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, HANDLE hd, int direction)
+{
+#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->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_debug ("spawning new feeder(%p, %p, %d)\n", stream, hd, direction);
+  if(!pth_spawn (tattr, feeder_thread, parm))
+    {
+      err = gpg_error_from_syserror ();
+      es_onclose (stream, 0, feeder_onclose_notification, parm);
+      xfree (parm);
+    }
+  else
+    err = 0;
+  pth_attr_destroy (tattr);
 
+  return err;
+#else
+  (void)stream;
+  (void)hd;
+  (void)direction;
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);  /* No Pth.  */
+#endif
+}
+
+
+\f
 /* Return the maximum number of currently allowed open file
    descriptors.  Only useful on POSIX systems but returns a value on
    other systems too.  */
@@ -92,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 *
@@ -156,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))
@@ -182,20 +327,18 @@ get_all_open_fds (void)
 }
 
 
-/* Helper function to build_w32_commandline. */
+
 static char *
-build_w32_commandline_copy (char *buffer, const char *string)
+copy_quoted (char *p, const char *string)
 {
-  char *p = buffer;
   const char *s;
 
   if (!*string) /* Empty string. */
     p = stpcpy (p, "\"\"");
-  else if (strpbrk (string, " \t\n\v\f\""))
+  else if (strpbrk (string, " \t\n\v\f\"")) /* Need quotes.  */
     {
-      /* Need to do some kind of quoting.  */
       p = stpcpy (p, "\"");
-      for (s=string; *s; s++)
+      for (s = string; *s; s++)
         {
           *p++ = *s;
           if (*s == '\"')
@@ -204,32 +347,51 @@ build_w32_commandline_copy (char *buffer, const char *string)
       *p++ = '\"';
       *p = 0;
     }
-  else
+  else /* Copy verbatim.  */
     p = stpcpy (p, string);
 
   return p;
 }
 
+
 /* Build a command line for use with W32's CreateProcess.  On success
    CMDLINE gets the address of a newly allocated string.  */
-static gpg_error_t
-build_w32_commandline (const char *pgmname, const char * const *argv, 
+static int
+build_w32_commandline (const char * const *argv,
+                      int rvid0, int rvid1, int rvid2,
                        char **cmdline)
 {
   int i, n;
   const char *s;
   char *buf, *p;
+  char fdbuf[3*30];
+
+  p = fdbuf;
+  *p = 0;
+
+  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 = 0;
-  s = pgmname;
-  n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
-  for (; *s; s++)
-    if (*s == '\"')
-      n++;  /* Need to double inner quotes.  */
-  for (i=0; (s=argv[i]); i++)
+  n = strlen (fdbuf);
+  for (i=0; (s = argv[i]); i++)
     {
-      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
+      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
       for (; *s; s++)
         if (*s == '\"')
           n++;  /* Need to double inner quotes.  */
@@ -237,240 +399,290 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
   n++;
 
   buf = p = xtrymalloc (n);
-  if (!buf)
-    return gpg_error_from_syserror ();
+  if (! buf)
+    return -1;
 
-  p = build_w32_commandline_copy (p, pgmname);
-  for (i=0; argv[i]; i++) 
+  p = stpcpy (p, fdbuf);
+  for (i = 0; argv[i]; i++)
     {
       *p++ = ' ';
-      p = build_w32_commandline_copy (p, argv[i]);
+      p = copy_quoted (p, argv[i]);
     }
 
-  *cmdline= buf;
+  *cmdline = buf;
   return 0;
 }
 
 
 /* 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.  */
-static int
+   the read end is inheritable, with 1 the write end is inheritable.
+   Note that the inheritable ends are rendezvous ids and no file
+   descriptors or handles. */
+static gpg_error_t
 create_inheritable_pipe (int filedes[2], int inherit_idx)
 {
-  HANDLE r, w, h;
-
-  if (!CreatePipe (&r, &w, NULL, 0))
-    return -1;
+  HANDLE hd;
+  int rvid;
 
-  if (!DuplicateHandle (GetCurrentProcess(), inherit_idx? w : r,
-                        GetCurrentProcess(), &h, 0,
-                        TRUE, DUPLICATE_SAME_ACCESS ))
+  filedes[0] = filedes[1] = -1;
+  hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
+  if (hd == INVALID_HANDLE_VALUE)
     {
-      log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
-      CloseHandle (r);
-      CloseHandle (w);
-      return -1;
+      log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1));
+      gpg_err_set_errno (EIO);
+      return gpg_error_from_syserror ();
     }
 
   if (inherit_idx)
     {
-      CloseHandle (w);
-      w = h;
+      filedes[0] = handle_to_fd (hd);
+      filedes[1] = rvid;
     }
   else
     {
-      CloseHandle (r);
-      r = h;
+      filedes[0] = rvid;
+      filedes[1] = handle_to_fd (hd);
     }
-
-  filedes[0] = handle_to_fd (r);
-  filedes[1] = handle_to_fd (w);
   return 0;
 }
 
 
-static gpg_error_t
-do_create_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], estream_t *r_fp, int nonblock)
 {
-  gpg_error_t err = 0;
-  int fds[2];
-
-  filedes[0] = filedes[1] = -1;
-  err = gpg_error (GPG_ERR_GENERAL);
-  if (!create_inheritable_pipe (fds, inherit_idx))
-    {
-      filedes[0] = _open_osfhandle (fds[0], 0);
-      if (filedes[0] == -1)
-        {
-          log_error ("failed to translate osfhandle %p\n", (void*)fds[0]);
-          CloseHandle (fd_to_handle (fds[1]));
-        }
-      else 
-        {
-          filedes[1] = _open_osfhandle (fds[1], 1);
-          if (filedes[1] == -1)
-            {
-              log_error ("failed to translate osfhandle %p\n", (void*)fds[1]);
-              close (filedes[0]);
-              filedes[0] = -1;
-              CloseHandle (fd_to_handle (fds[1]));
-            }
-          else
-            err = 0;
-        }
-    }
-  return err;
+  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 write end is
-   inheritable.  */
+
+/* Portable function to create a pipe.  Under Windows the read end is
+   inheritable (i.e. an rendezvous id).  */
 gpg_error_t
-gnupg_create_inbound_pipe (int filedes[2])
+gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
 {
-  return do_create_pipe (filedes, 1);
+  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 the read end is
+/* 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 gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 }
 
 
-/* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
-   stdin, write the output to OUTFILE, return a new stream in
-   STATUSFILE for stderr and the pid of the process in PID. The
-   arguments for the process are expected in the NULL terminated array
-   ARGV.  The program name itself should not be included there.  If
-   PREEXEC is not NULL, that function will be called right before the
-   exec.  Calling gnupg_wait_process is required.
-
-   FLAGS is a bit vector with just one bit defined for now:
+static int
+create_process (const char *pgmname, const char *cmdline,
+                PROCESS_INFORMATION *pi)
+{
+  int res;
+  wchar_t *wpgmname, *wcmdline;
+
+  wpgmname = utf8_to_wchar (pgmname);
+  if (!wpgmname)
+    return 0;
+  wcmdline = utf8_to_wchar (cmdline);
+  if (!wcmdline)
+    {
+      xfree (wpgmname);
+      return 0;
+    }
+  res = CreateProcess (wpgmname,      /* Program to start.  */
+                       wcmdline,      /* Command line arguments.  */
+                       NULL,          /* Process security attributes.  */
+                       NULL,          /* Thread security attributes.  */
+                       FALSE,          /* Inherit handles.  */
+                       CREATE_SUSPENDED, /* Creation flags.  */
+                       NULL,          /* Environment.  */
+                       NULL,          /* Use current drive/directory.  */
+                       NULL,          /* Startup information. */
+                       pi);           /* Returns process information.  */
+  xfree (wcmdline);
+  xfree (wpgmname);
+  return res;
+}
 
-   Bit 7: If set the process will be started as a background process.
-          This flag is only useful under W32 systems, so that no new
-          console is created and pops up a console window when
-          starting the server.  Does not work on W32CE.
-   Bit 6: On W32 run AllowSetForegroundWindow for the child.  Due to
-          error problems this actually allows SetForegroundWindow for
-          childs of this process.
 
-   Returns 0 on success or an error code. */
+/* Fork and exec the PGMNAME, see exechelp.h for details.  */
 gpg_error_t
 gnupg_spawn_process (const char *pgmname, const char *argv[],
-                     FILE *infile, estream_t outfile,
-                     void (*preexec)(void), unsigned int flags,
-                     FILE **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,      /* Returns process handle.  */
-      0,         /* Returns primary thread handle.  */
-      0,         /* Returns pid.  */
-      0          /* Returns tid.  */
-    };
-  STARTUPINFO si;
+  PROCESS_INFORMATION pi = {NULL };
   char *cmdline;
-  int fd, fdout, rp[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);
-  fflush (infile);
-  rewind (infile);
-  fd = _get_osfhandle (fileno (infile));
-  fdout = _get_osfhandle (es_fileno (outfile));
-  if (fd == -1 || fdout == -1)
-    log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
+  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)
+    {
+      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);
+    }
+
+  if (r_outfp)
+    {
+      /* 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.  */
+    }
+
+  if (r_errfp)
+    {
+      /* 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.  */
+    }
+
+
 
   /* Build the command line.  */
-  err = build_w32_commandline (pgmname, argv, &cmdline);
+  err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid,
+                               &cmdline);
   if (err)
-    return err; 
-
-  /* Create a pipe.  */
-  if (create_inheritable_pipe (rp, 1))
     {
-      err = gpg_error (GPG_ERR_GENERAL);
-      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
-      xfree (cmdline);
+      /* Fixme release other stuff/kill feeder.  */
+      CloseHandle (errpipe.hd);
       return err;
     }
-  
-  /* Start the process.  Note that we can't run the PREEXEC function
-     because this would change our own environment. */
-  /* si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; */
-  /* si.hStdInput  = fd_to_handle (fd); */
-  /* si.hStdOutput = fd_to_handle (fdout); */
-  /* si.hStdError  = fd_to_handle (rp[1]); */
-
-/*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
-  if (!CreateProcess (pgmname,       /* Program to start.  */
-                      cmdline,       /* Command line arguments.  */
-                      NULL,          /* Process security attributes.  */
-                      NULL,          /* Thread security attributes.  */
-                      FALSE,          /* Inherit handles.  */
-                      CREATE_SUSPENDED, /* Creation flags.  */
-                      NULL,          /* Environment.  */
-                      NULL,          /* Use current drive/directory.  */
-                      NULL,           /* Startup information. */
-                      &pi            /* Returns process information.  */
-                      ))
+
+  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 (rp[0]));
-      CloseHandle (fd_to_handle (rp[1]));
+      /* Fixme release other stuff/kill feeder.  */
+      CloseHandle (errpipe.hd);
       return gpg_error (GPG_ERR_GENERAL);
     }
   xfree (cmdline);
   cmdline = NULL;
 
-  /* Close the other end of the pipe.  */
-  CloseHandle (fd_to_handle (rp[1]));
-  
-/*   log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
-/*              " dwProcessID=%d dwThreadId=%d\n", */
-/*              pi.hProcess, pi.hThread, */
-/*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
-  
-  /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
-     invalid argument error if we pass the correct processID to
-     it.  As a workaround we use -1 (ASFW_ANY).  */
-  if ( (flags & 64) )
-    gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
+  /* Note: The other end of the pipe is a rendezvous id and thus there
+     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); 
-
-  {
-    int x;
-
-    x = _open_osfhandle (rp[0], 0);
-    if (x == -1)
-      log_error ("failed to translate osfhandle %p\n", (void*)rp[0] );
-    else 
-      *statusfile = fdopen (x, "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;
-
 }
 
 
@@ -488,76 +700,47 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
                         int infd, int outfd, int errfd, pid_t *pid)
 {
   gpg_error_t err;
-  PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
-  STARTUPINFO si;
+  PROCESS_INFORMATION pi = {NULL};
   char *cmdline;
-  int i;
-  HANDLE stdhd[3];
 
   /* Setup return values.  */
   *pid = (pid_t)(-1);
 
+  if (infd != -1 || outfd != -1 || errfd != -1)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
   /* Build the command line.  */
-  err = build_w32_commandline (pgmname, argv, &cmdline);
+  err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
   if (err)
-    return err; 
-
-  /* si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; */
-  /* stdhd[0] = infd  == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; */
-  /* stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; */
-  /* stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; */
-  /* si.hStdInput  = infd  == -1? stdhd[0] : (void*)_get_osfhandle (infd); */
-  /* 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); */
-  if (!CreateProcess (pgmname,       /* Program to start.  */
-                      cmdline,       /* Command line arguments.  */
-                      NULL,          /* Process security attributes.  */
-                      NULL,          /* Thread security attributes.  */
-                      FALSE,          /* Inherit handles.  */
-                      CREATE_SUSPENDED,
-                      NULL,          /* Environment.  */
-                      NULL,          /* Use current drive/directory.  */
-                      NULL,           /* Startup information. */
-                      &pi            /* Returns process information.  */
-                      ))
+    return err;
+
+  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));
-      err = gpg_error (GPG_ERR_GENERAL);
+      log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1));
+      xfree (cmdline);
+      return gpg_error (GPG_ERR_GENERAL);
     }
-  else
-    err = 0;
   xfree (cmdline);
-  for (i=0; i < 3; i++)
-    if (stdhd[i] != INVALID_HANDLE_VALUE)
-      CloseHandle (stdhd[i]);
-  if (err)
-    return err;
+  cmdline = NULL;
 
-/*   log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
-/*              " dwProcessID=%d dwThreadId=%d\n", */
-/*              pi.hProcess, pi.hThread, */
-/*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
+  log_debug ("CreateProcess(fd) 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); 
+  CloseHandle (pi.hThread);
 
   *pid = handle_to_pid (pi.hProcess);
   return 0;
-
 }
 
 
-/* Wait for the process identified by PID to terminate. PGMNAME should
-   be the same as supplied to the spawn function and is only used for
-   diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
-   for any failures of the spawned program or other error codes.  If
-   EXITCODE is not NULL the exit code of the process is stored at this
-   address or -1 if it could not be retrieved. */
+/* See exechelp.h for a description.  */
 gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
+gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
 {
   gpg_err_code_t ec;
   HANDLE proc = fd_to_handle (pid);
@@ -573,51 +756,75 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
   /* 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, INFINITE);
-  switch (code) 
+  code = WaitForSingleObject (proc, hang? INFINITE : 0);
+  switch (code)
     {
-      case WAIT_FAILED:
-        log_error (_("waiting for process %d to terminate failed: %s\n"),
-                   (int)pid, w32_strerror (-1));
-        ec = GPG_ERR_GENERAL;
-        break;
-
-      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;
+    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));
+      ec = GPG_ERR_GENERAL;
+      break;
+
+    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)
-          {
-            log_error (_("error running `%s': exit status %d\n"),
+      else if (exc)
+        {
+          log_error (_("error running '%s': exit status %d\n"),
                        pgmname, (int)exc );
-            if (exitcode)
-              *exitcode = (int)exc;
-            ec = GPG_ERR_GENERAL;
-          }
-        else
-          {
-            if (exitcode)
-              *exitcode = 0;
-            ec = 0;
-          }
-        CloseHandle (proc);
-        break;
-
-      default:
-        log_error ("WaitForSingleObject returned unexpected "
-                   "code %d for pid %d\n", code, (int)pid );
-        ec = GPG_ERR_GENERAL;
-        break;
+          if (exitcode)
+            *exitcode = (int)exc;
+          ec = GPG_ERR_GENERAL;
+        }
+      else
+        {
+          if (exitcode)
+            *exitcode = 0;
+          ec = 0;
+        }
+      break;
+
+    default:
+      log_error ("WaitForSingleObject returned unexpected "
+                 "code %d for pid %d\n", code, (int)pid );
+      ec = GPG_ERR_GENERAL;
+      break;
     }
 
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
 }
 
 
-/* Spawn a new process and immediatley detach from it.  The name of
+/* 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 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
@@ -629,39 +836,19 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
                               const char *envp[] )
 {
   gpg_error_t err;
-  PROCESS_INFORMATION pi = 
-    {
-      NULL,      /* Returns process handle.  */
-      0,         /* Returns primary thread handle.  */
-      0,         /* Returns pid.  */
-      0          /* Returns tid.  */
-    };
-  STARTUPINFO si;
   char *cmdline;
+  PROCESS_INFORMATION pi = {NULL };
 
   (void)envp;
 
-  if (access (pgmname, X_OK))
-    return gpg_error_from_syserror ();
-
   /* Build the command line.  */
-  err = build_w32_commandline (pgmname, argv, &cmdline);
+  err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
   if (err)
-    return err; 
-
-/*   log_debug ("CreateProcess(detached), path=`%s' cmdline=`%s'\n", */
-/*              pgmname, cmdline); */
-  if (!CreateProcess (pgmname,       /* Program to start.  */
-                      cmdline,       /* Command line arguments.  */
-                      NULL,          /* Process security attributes.  */
-                      NULL,          /* Thread security attributes.  */
-                      FALSE,         /* Inherit handles.  */
-                      0,             /* Creation flags.  */
-                      NULL,          /* Environment.  */
-                      NULL,          /* Use current drive/directory.  */
-                      NULL,           /* Startup information. */
-                      &pi            /* Returns process information.  */
-                      ))
+    return err;
+
+  /* Note: There is no detached flag under CE.  */
+  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));
       xfree (cmdline);
@@ -670,12 +857,14 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
   xfree (cmdline);
   cmdline = NULL;
 
-/*   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
-/*              " dwProcessID=%d dwThreadId=%d\n", */
-/*              pi.hProcess, pi.hThread, */
-/*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
+  log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
+             " dwProcessID=%d dwThreadId=%d\n",
+             pi.hProcess, pi.hThread,
+             (int) pi.dwProcessId, (int) pi.dwThreadId);
 
-  CloseHandle (pi.hThread); 
+  /* Process has been created suspended; resume it now. */
+  ResumeThread (pi.hThread);
+  CloseHandle (pi.hThread);
 
   return 0;
 }
@@ -690,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);
     }