Merge branch 'wk/test-gpgrt-estream'
[gnupg.git] / common / exechelp-w32ce.c
index ac7602d..cca55c8 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
 #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))
@@ -334,8 +357,8 @@ copy_quoted (char *p, const char *string)
 /* Build a command line for use with W32's CreateProcess.  On success
    CMDLINE gets the address of a newly allocated string.  */
 static int
-build_w32_commandline (const char *pgmname, const char * const *argv,
-                      int fd0, int fd1, int fd2, int fd2_isnull,
+build_w32_commandline (const char * const *argv,
+                      int rvid0, int rvid1, int rvid2,
                        char **cmdline)
 {
   int i, n;
@@ -345,28 +368,27 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
 
   p = fdbuf;
   *p = 0;
-  if (fd0)
-    {
-      snprintf (p, 25, "-&S0=%d ", fd0);
-      p += strlen (p);
-    }
-  if (fd1)
-    {
-      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);
-  n += strlen (pgmname) + 1 + 2; /* (1 space, 2 quoting) */
   for (i=0; (s = argv[i]); i++)
     {
       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
@@ -381,8 +403,7 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
     return -1;
 
   p = stpcpy (p, fdbuf);
-  p = copy_quoted (p, pgmname);
-  for (i = 0; argv[i]; i++) 
+  for (i = 0; argv[i]; i++)
     {
       *p++ = ' ';
       p = copy_quoted (p, argv[i]);
@@ -479,112 +500,173 @@ 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,
+                     gpg_err_source_t errsource,
                      void (*preexec)(void), unsigned int flags,
-                     estream_t *statusfile, pid_t *pid)
+                     estream_t 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;
 
   (void)preexec;
   (void)flags;
-  
-  /* Setup return values.  */
-  *statusfile = NULL;
-  *pid = (pid_t)(-1);
-
-  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 (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
-                               0, &cmdline);
+  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;
-
 }
 
 
@@ -601,78 +683,48 @@ gpg_error_t
 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
                         int infd, int outfd, int errfd, pid_t *pid)
 {
-  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-#if 0
   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;
-#endif
 }
 
-/* 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);
@@ -688,50 +740,65 @@ 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);
 }
 
 
+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
    the program to exec is PGMNAME and its arguments are in ARGV (the
    programname is automatically passed as first argument).
@@ -743,42 +810,20 @@ gpg_error_t
 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
                               const char *envp[] )
 {
-  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-#if 0
   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);
@@ -787,17 +832,19 @@ 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;
-#endif
 }
 
+
 /* Kill a process; that is send an appropriate signal to the process.
    gnupg_wait_process must be called to actually remove the process
    from the system.  An invalid PID is ignored.  */
@@ -807,7 +854,7 @@ gnupg_kill_process (pid_t pid)
   if (pid != (pid_t) INVALID_HANDLE_VALUE)
     {
       HANDLE process = (HANDLE) pid;
-      
+
       /* Arbitrary error code.  */
       TerminateProcess (process, 1);
     }