common: Include <gpg-error.h>.
[gnupg.git] / common / exechelp-w32ce.c
1 /* exechelp-w32.c - Fork and exec helpers for W32CE.
2  * Copyright (C) 2004, 2007, 2008, 2009,
3  *               2010 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * This file is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, see <http://www.gnu.org/licenses/>.
29  */
30
31 #include <config.h>
32
33 #if !defined(HAVE_W32_SYSTEM) && !defined (HAVE_W32CE_SYSTEM)
34 #error This code is only used on W32CE.
35 #endif
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <assert.h>
42 #ifdef HAVE_SIGNAL_H
43 # include <signal.h>
44 #endif
45 #include <unistd.h>
46 #include <fcntl.h>
47
48 #ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth.  */
49 #undef HAVE_NPTH
50 #undef USE_NPTH
51 #endif
52
53 #ifdef HAVE_NPTH
54 #include <npth.h>
55 #endif
56
57 #ifdef HAVE_STAT
58 # include <sys/stat.h>
59 #endif
60
61 #include <assuan.h>
62
63 #include "util.h"
64 #include "i18n.h"
65 #include "sysutils.h"
66 #include "exechelp.h"
67
68
69 /* It seems Vista doesn't grok X_OK and so fails access() tests.
70    Previous versions interpreted X_OK as F_OK anyway, so we'll just
71    use F_OK directly. */
72 #undef X_OK
73 #define X_OK F_OK
74
75
76 /* We assume that a HANDLE can be represented by an int which should
77    be true for all i386 systems (HANDLE is defined as void *) and
78    these are the only systems for which Windows is available.  Further
79    we assume that -1 denotes an invalid handle.  */
80 #define fd_to_handle(a)  ((HANDLE)(a))
81 #define handle_to_fd(a)  ((int)(a))
82 #define pid_to_handle(a) ((HANDLE)(a))
83 #define handle_to_pid(a) ((int)(a))
84
85 \f
86 #ifdef USE_NPTH
87 /* The data passed to the feeder_thread.  */
88 struct feeder_thread_parms
89 {
90   estream_t stream;
91   volatile int stream_valid;
92   HANDLE hd;
93   int direction;
94 };
95
96
97 /* The thread started by start_feede3.  */
98 static void *
99 feeder_thread (void *arg)
100 {
101   struct feeder_thread_parms *parm = arg;
102   char buffer[4096];
103   int rc;
104
105   if (parm->direction)
106     {
107       size_t nread = 0;
108       DWORD nwritten;
109
110       log_debug ("feeder_thread estream->pipe: stream=%p pipe=%p\n",
111                  parm->stream, parm->hd);
112       while (parm->stream_valid
113              && !es_read (parm->stream, buffer, sizeof buffer, &nread))
114         {
115           do
116             {
117               pth_enter ();
118               rc = WriteFile (parm->hd, buffer, nread, &nwritten, NULL);
119               pth_leave ();
120               if (!rc)
121                 {
122                   log_debug ("feeder(%p): WriteFile error: rc=%d\n",
123                              parm->hd, (int)GetLastError ());
124                   goto leave;
125                 }
126               nread -= nwritten;
127             }
128           while (nread);
129         }
130       if (!parm->stream_valid)
131         log_debug ("feeder(%p): closed by other thread\n", parm->hd);
132       else if (nread)
133         log_debug ("feeder(%p): es_read error: %s\n",
134                    parm->hd, strerror (errno));
135     }
136   else
137     {
138       DWORD nread = 0;
139       size_t nwritten;
140
141       log_debug ("feeder_thread pipe->estream: stream=%p pipe=%p\n",
142                  parm->stream, parm->hd);
143       while ( (pth_enter (),
144                (rc = ReadFile (parm->hd, buffer, sizeof buffer, &nread, NULL)),
145                pth_leave (),
146                rc) && nread)
147         {
148           log_debug ("feeder_thread pipe->estream: read %d bytes\n",
149                      (int)nread);
150           do
151             {
152               if (parm->stream_valid
153                   && es_write (parm->stream, buffer, nread, &nwritten))
154                 {
155                   log_debug ("feeder(%p): es_write error: %s\n",
156                              parm->hd, strerror (errno));
157                   goto leave;
158                 }
159               log_debug ("feeder_thread pipe->estream: es_wrote %d bytes\n",
160                          (int)nwritten);
161               nread -= nwritten;
162             }
163           while (nread && parm->stream_valid);
164         }
165       if (!parm->stream_valid)
166         log_debug ("feeder(%p): closed by other thread\n", parm->hd);
167       else if (nread)
168         log_debug ("feeder(%p): ReadFile error: rc=%d\n",
169                    parm->hd, (int)GetLastError ());
170       else
171         log_debug ("feeder(%p): eof\n", parm->hd);
172     }
173
174 leave:
175   log_debug ("feeder(%p): waiting for es_fclose\n", parm->hd);
176   while (parm->stream_valid)
177     pth_yield (NULL);
178   log_debug ("feeder(%p): about to close the pipe handle\n", parm->hd);
179   CloseHandle (parm->hd);
180   log_debug ("feeder(%p): pipe handle closed\n", parm->hd);
181   xfree (parm);
182   return NULL;
183 }
184 #endif /*USE_NPTH*/
185
186 #ifdef USE_NPTH
187 static void
188 feeder_onclose_notification (estream_t stream, void *opaque)
189 {
190   struct feeder_thread_parms *parm = opaque;
191   (void)stream;
192   log_debug ("feeder(%p): received onclose note\n", parm->hd);
193   parm->stream_valid = 0;
194 }
195 #endif /*USE_NPTH*/
196
197 /* Fire up a thread to copy data between STREAM and a pipe's
198    descriptor FD.  With DIRECTION set to true the copy takes place
199    from the stream to the pipe, otherwise from the pipe to the
200    stream.  */
201 static gpg_error_t
202 start_feeder (estream_t stream, HANDLE hd, int direction)
203 {
204 #ifdef USE_NPTH
205   gpg_error_t err;
206   struct feeder_thread_parms *parm;
207   pth_attr_t tattr;
208
209   parm = xtrymalloc (sizeof *parm);
210   if (!parm)
211     return gpg_error_from_syserror ();
212   parm->stream = stream;
213   parm->stream_valid = 1;
214   parm->hd = hd;
215   parm->direction = direction;
216
217   if (es_onclose (stream, 1, feeder_onclose_notification, parm))
218     {
219       err = gpg_error_from_syserror ();
220       xfree (parm);
221       return err;
222     }
223
224   tattr = pth_attr_new ();
225   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
226   pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
227   pth_attr_set (tattr, PTH_ATTR_NAME, "exec-feeder");
228
229   log_debug ("spawning new feeder(%p, %p, %d)\n", stream, hd, direction);
230   if(!pth_spawn (tattr, feeder_thread, parm))
231     {
232       err = gpg_error_from_syserror ();
233       es_onclose (stream, 0, feeder_onclose_notification, parm);
234       xfree (parm);
235     }
236   else
237     err = 0;
238   pth_attr_destroy (tattr);
239
240   return err;
241 #else
242   (void)stream;
243   (void)hd;
244   (void)direction;
245   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);  /* No Pth.  */
246 #endif
247 }
248
249
250 \f
251 /* Return the maximum number of currently allowed open file
252    descriptors.  Only useful on POSIX systems but returns a value on
253    other systems too.  */
254 int
255 get_max_fds (void)
256 {
257   int max_fds = -1;
258
259 #ifdef OPEN_MAX
260   if (max_fds == -1)
261     max_fds = OPEN_MAX;
262 #endif
263
264   if (max_fds == -1)
265     max_fds = 256;  /* Arbitrary limit.  */
266
267   return max_fds;
268 }
269
270
271 /* Under Windows this is a dummy function.  */
272 void
273 close_all_fds (int first, int *except)
274 {
275   (void)first;
276   (void)except;
277 }
278
279
280 /* Returns an array with all currently open file descriptors.  The end
281    of the array is marked by -1.  The caller needs to release this
282    array using the *standard free* and not with xfree.  This allow the
283    use of this function right at startup even before libgcrypt has
284    been initialized.  Returns NULL on error and sets ERRNO
285    accordingly.  */
286 int *
287 get_all_open_fds (void)
288 {
289   int *array;
290   size_t narray;
291   int fd, max_fd, idx;
292 #ifndef HAVE_STAT
293   array = calloc (1, sizeof *array);
294   if (array)
295     array[0] = -1;
296 #else /*HAVE_STAT*/
297   struct stat statbuf;
298
299   max_fd = get_max_fds ();
300   narray = 32;  /* If you change this change also t-exechelp.c.  */
301   array = calloc (narray, sizeof *array);
302   if (!array)
303     return NULL;
304
305   /* Note:  The list we return is ordered.  */
306   for (idx=0, fd=0; fd < max_fd; fd++)
307     if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
308       {
309         if (idx+1 >= narray)
310           {
311             int *tmp;
312
313             narray += (narray < 256)? 32:256;
314             tmp = realloc (array, narray * sizeof *array);
315             if (!tmp)
316               {
317                 free (array);
318                 return NULL;
319               }
320             array = tmp;
321           }
322         array[idx++] = fd;
323       }
324   array[idx] = -1;
325 #endif /*HAVE_STAT*/
326   return array;
327 }
328
329
330
331 static char *
332 copy_quoted (char *p, const char *string)
333 {
334   const char *s;
335
336   if (!*string) /* Empty string. */
337     p = stpcpy (p, "\"\"");
338   else if (strpbrk (string, " \t\n\v\f\"")) /* Need quotes.  */
339     {
340       p = stpcpy (p, "\"");
341       for (s = string; *s; s++)
342         {
343           *p++ = *s;
344           if (*s == '\"')
345             *p++ = *s;
346         }
347       *p++ = '\"';
348       *p = 0;
349     }
350   else /* Copy verbatim.  */
351     p = stpcpy (p, string);
352
353   return p;
354 }
355
356
357 /* Build a command line for use with W32's CreateProcess.  On success
358    CMDLINE gets the address of a newly allocated string.  */
359 static int
360 build_w32_commandline (const char * const *argv,
361                        int rvid0, int rvid1, int rvid2,
362                        char **cmdline)
363 {
364   int i, n;
365   const char *s;
366   char *buf, *p;
367   char fdbuf[3*30];
368
369   p = fdbuf;
370   *p = 0;
371
372   if (rvid0)
373     snprintf (p, 25, "-&S0=%d ", rvid0);
374   else
375     strcpy (p, "-&S0=null ");
376   p += strlen (p);
377
378   if (rvid1)
379     snprintf (p, 25, "-&S1=%d ", rvid1);
380   else
381     strcpy (p, "-&S1=null ");
382   p += strlen (p);
383
384   if (rvid2)
385     snprintf (p, 25, "-&S2=%d ", rvid2);
386   else
387     strcpy (p, "-&S2=null ");
388   p += strlen (p);
389
390   *cmdline = NULL;
391   n = strlen (fdbuf);
392   for (i=0; (s = argv[i]); i++)
393     {
394       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
395       for (; *s; s++)
396         if (*s == '\"')
397           n++;  /* Need to double inner quotes.  */
398     }
399   n++;
400
401   buf = p = xtrymalloc (n);
402   if (! buf)
403     return -1;
404
405   p = stpcpy (p, fdbuf);
406   for (i = 0; argv[i]; i++)
407     {
408       *p++ = ' ';
409       p = copy_quoted (p, argv[i]);
410     }
411
412   *cmdline = buf;
413   return 0;
414 }
415
416
417 /* Create pipe where one end is inheritable: With an INHERIT_IDX of 0
418    the read end is inheritable, with 1 the write end is inheritable.
419    Note that the inheritable ends are rendezvous ids and no file
420    descriptors or handles. */
421 static gpg_error_t
422 create_inheritable_pipe (int filedes[2], int inherit_idx)
423 {
424   HANDLE hd;
425   int rvid;
426
427   filedes[0] = filedes[1] = -1;
428   hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
429   if (hd == INVALID_HANDLE_VALUE)
430     {
431       log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1));
432       gpg_err_set_errno (EIO);
433       return gpg_error_from_syserror ();
434     }
435
436   if (inherit_idx)
437     {
438       filedes[0] = handle_to_fd (hd);
439       filedes[1] = rvid;
440     }
441   else
442     {
443       filedes[0] = rvid;
444       filedes[1] = handle_to_fd (hd);
445     }
446   return 0;
447 }
448
449
450 /* Portable function to create a pipe.  Under Windows the write end is
451    inheritable (i.e. an rendezvous id).  */
452 gpg_error_t
453 gnupg_create_inbound_pipe (int filedes[2])
454 {
455   return create_inheritable_pipe (filedes, 1);
456 }
457
458
459 /* Portable function to create a pipe.  Under Windows the read end is
460    inheritable (i.e. an rendezvous id).  */
461 gpg_error_t
462 gnupg_create_outbound_pipe (int filedes[2])
463 {
464   return create_inheritable_pipe (filedes, 0);
465 }
466
467
468 static int
469 create_process (const char *pgmname, const char *cmdline,
470                 PROCESS_INFORMATION *pi)
471 {
472   int res;
473   wchar_t *wpgmname, *wcmdline;
474
475   wpgmname = utf8_to_wchar (pgmname);
476   if (!wpgmname)
477     return 0;
478   wcmdline = utf8_to_wchar (cmdline);
479   if (!wcmdline)
480     {
481       xfree (wpgmname);
482       return 0;
483     }
484   res = CreateProcess (wpgmname,      /* Program to start.  */
485                        wcmdline,      /* Command line arguments.  */
486                        NULL,          /* Process security attributes.  */
487                        NULL,          /* Thread security attributes.  */
488                        FALSE,          /* Inherit handles.  */
489                        CREATE_SUSPENDED, /* Creation flags.  */
490                        NULL,          /* Environment.  */
491                        NULL,          /* Use current drive/directory.  */
492                        NULL,          /* Startup information. */
493                        pi);           /* Returns process information.  */
494   xfree (wcmdline);
495   xfree (wpgmname);
496   return res;
497 }
498
499
500 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
501 gpg_error_t
502 gnupg_spawn_process (const char *pgmname, const char *argv[],
503                      gpg_err_source_t errsource,
504                      void (*preexec)(void), unsigned int flags,
505                      estream_t *r_infp,
506                      estream_t *r_outfp,
507                      estream_t *r_errfp,
508                      pid_t *pid)
509 {
510   gpg_error_t err;
511   PROCESS_INFORMATION pi = {NULL };
512   char *cmdline;
513   es_syshd_t syshd;
514   struct {
515     HANDLE hd;
516     int rvid;
517   } inpipe = {INVALID_HANDLE_VALUE, 0};
518   struct {
519     HANDLE hd;
520     int rvid;
521   } outpipe = {INVALID_HANDLE_VALUE, 0};
522   struct {
523     HANDLE hd;
524     int rvid;
525   } errpipe = {INVALID_HANDLE_VALUE, 0};
526   estream_t outfp = NULL;
527   estream_t errfp = NULL;
528
529   (void)preexec;
530   (void)flags;
531
532   /* Setup return values.  */
533   if (r_outfp)
534     *r_outfp = NULL;
535   if (r_errfp)
536     *r_errfp = NULL;
537   *pid = (pid_t)(-1); /* Always required.  */
538
539   log_debug ("%s: enter\n", __func__);
540   if (infp)
541     {
542       es_fflush (infp);
543       es_rewind (infp);
544
545       /* Create a pipe to copy our infile to the stdin of the child
546          process.  On success inpipe.hd is owned by the feeder.  */
547       inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1);
548       if (inpipe.hd == INVALID_HANDLE_VALUE)
549         {
550           log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
551                      w32_strerror (-1));
552           gpg_err_set_errno (EIO);
553           return gpg_error_from_syserror ();
554         }
555       log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__,
556                  infp, inpipe.hd, inpipe.rvid);
557       err = start_feeder (infp, inpipe.hd, 1);
558       if (err)
559         {
560           log_error ("error spawning feeder: %s\n", gpg_strerror (err));
561           CloseHandle (inpipe.hd);
562           return err;
563         }
564       inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder.  */
565       log_debug ("%s: inpipe %p created; feeder started\n", __func__,
566                  infp);
567     }
568
569   if (r_outfp)
570     {
571       /* Create a pipe to make the stdout of the child process
572          available as a stream.  */
573       outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0);
574       if (outpipe.hd == INVALID_HANDLE_VALUE)
575         {
576           log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
577                      w32_strerror (-1));
578           gpg_err_set_errno (EIO);
579           /* Fixme release other stuff/kill feeder.  */
580           return gpg_error_from_syserror ();
581         }
582       syshd.type = ES_SYSHD_HANDLE;
583       syshd.u.handle = outpipe.hd;
584       err = 0;
585       outfp = es_sysopen (&syshd, "r");
586       if (!outfp)
587         {
588           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
589           log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
590           CloseHandle (outpipe.hd);
591           return err;
592         }
593       log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__,
594                  outfp, outpipe.hd, outpipe.rvid);
595       outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP.  */
596     }
597
598   if (r_errfp)
599     {
600       /* Create a pipe to make the stderr of the child process
601          available as a stream.  */
602       errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0);
603       if (errpipe.hd == INVALID_HANDLE_VALUE)
604         {
605           log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
606                      w32_strerror (-1));
607           gpg_err_set_errno (EIO);
608           /* Fixme release other stuff/kill feeder.  */
609           return gpg_error_from_syserror ();
610         }
611       syshd.type = ES_SYSHD_HANDLE;
612       syshd.u.handle = errpipe.hd;
613       err = 0;
614       errfp = es_sysopen (&syshd, "r");
615       if (!errfp)
616         {
617           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
618           log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
619           CloseHandle (errpipe.hd);
620           return err;
621         }
622       log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__,
623                  errfp, errpipe.hd, errpipe.rvid);
624       errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP.  */
625     }
626
627
628
629   /* Build the command line.  */
630   err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid,
631                                &cmdline);
632   if (err)
633     {
634       /* Fixme release other stuff/kill feeder.  */
635       CloseHandle (errpipe.hd);
636       return err;
637     }
638
639   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
640   if (!create_process (pgmname, cmdline, &pi))
641     {
642       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
643       xfree (cmdline);
644       /* Fixme release other stuff/kill feeder.  */
645       CloseHandle (errpipe.hd);
646       return gpg_error (GPG_ERR_GENERAL);
647     }
648   xfree (cmdline);
649   cmdline = NULL;
650
651   /* Note: The other end of the pipe is a rendezvous id and thus there
652      is no need for a close.  */
653
654   log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
655              " dwProcessID=%d dwThreadId=%d\n",
656              pi.hProcess, pi.hThread,
657              (int) pi.dwProcessId, (int) pi.dwThreadId);
658
659
660   /* Process has been created suspended; resume it now. */
661   ResumeThread (pi.hThread);
662   CloseHandle (pi.hThread);
663
664   if (r_outfp)
665     *r_outfp = outfp;
666   if (r_errfp)
667     *r_errfp = errfp;
668   *pid = handle_to_pid (pi.hProcess);
669   return 0;
670 }
671
672
673
674 /* Simplified version of gnupg_spawn_process.  This function forks and
675    then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
676    and ERRFD to stderr (any of them may be -1 to connect them to
677    /dev/null).  The arguments for the process are expected in the NULL
678    terminated array ARGV.  The program name itself should not be
679    included there.  Calling gnupg_wait_process is required.
680
681    Returns 0 on success or an error code. */
682 gpg_error_t
683 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
684                         int infd, int outfd, int errfd, pid_t *pid)
685 {
686   gpg_error_t err;
687   PROCESS_INFORMATION pi = {NULL};
688   char *cmdline;
689
690   /* Setup return values.  */
691   *pid = (pid_t)(-1);
692
693   if (infd != -1 || outfd != -1 || errfd != -1)
694     return gpg_error (GPG_ERR_NOT_SUPPORTED);
695
696   /* Build the command line.  */
697   err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
698   if (err)
699     return err;
700
701   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
702   if (!create_process (pgmname, cmdline, &pi))
703     {
704       log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1));
705       xfree (cmdline);
706       return gpg_error (GPG_ERR_GENERAL);
707     }
708   xfree (cmdline);
709   cmdline = NULL;
710
711   log_debug ("CreateProcess(fd) ready: hProcess=%p hThread=%p"
712              " dwProcessID=%d dwThreadId=%d\n",
713              pi.hProcess, pi.hThread,
714              (int) pi.dwProcessId, (int) pi.dwThreadId);
715
716   /* Process has been created suspended; resume it now. */
717   ResumeThread (pi.hThread);
718   CloseHandle (pi.hThread);
719
720   *pid = handle_to_pid (pi.hProcess);
721   return 0;
722 }
723
724
725 /* See exechelp.h for a description.  */
726 gpg_error_t
727 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
728 {
729   gpg_err_code_t ec;
730   HANDLE proc = fd_to_handle (pid);
731   int code;
732   DWORD exc;
733
734   if (exitcode)
735     *exitcode = -1;
736
737   if (pid == (pid_t)(-1))
738     return gpg_error (GPG_ERR_INV_VALUE);
739
740   /* FIXME: We should do a pth_waitpid here.  However this has not yet
741      been implemented.  A special W32 pth system call would even be
742      better.  */
743   code = WaitForSingleObject (proc, hang? INFINITE : 0);
744   switch (code)
745     {
746     case WAIT_TIMEOUT:
747       ec = GPG_ERR_TIMEOUT;
748       break;
749
750     case WAIT_FAILED:
751       log_error (_("waiting for process %d to terminate failed: %s\n"),
752                  (int)pid, w32_strerror (-1));
753       ec = GPG_ERR_GENERAL;
754       break;
755
756     case WAIT_OBJECT_0:
757       if (!GetExitCodeProcess (proc, &exc))
758         {
759           log_error (_("error getting exit code of process %d: %s\n"),
760                      (int)pid, w32_strerror (-1) );
761           ec = GPG_ERR_GENERAL;
762           }
763       else if (exc)
764         {
765           log_error (_("error running '%s': exit status %d\n"),
766                        pgmname, (int)exc );
767           if (exitcode)
768             *exitcode = (int)exc;
769           ec = GPG_ERR_GENERAL;
770         }
771       else
772         {
773           if (exitcode)
774             *exitcode = 0;
775           ec = 0;
776         }
777       break;
778
779     default:
780       log_error ("WaitForSingleObject returned unexpected "
781                  "code %d for pid %d\n", code, (int)pid );
782       ec = GPG_ERR_GENERAL;
783       break;
784     }
785
786   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
787 }
788
789
790 void
791 gnupg_release_process (pid_t pid)
792 {
793   if (pid != (pid_t)INVALID_HANDLE_VALUE)
794     {
795       HANDLE process = (HANDLE)pid;
796
797       CloseHandle (process);
798     }
799 }
800
801
802 /* Spawn a new process and immediatley detach from it.  The name of
803    the program to exec is PGMNAME and its arguments are in ARGV (the
804    programname is automatically passed as first argument).
805    Environment strings in ENVP are set.  An error is returned if
806    pgmname is not executable; to make this work it is necessary to
807    provide an absolute file name.  All standard file descriptors are
808    connected to /dev/null. */
809 gpg_error_t
810 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
811                               const char *envp[] )
812 {
813   gpg_error_t err;
814   char *cmdline;
815   PROCESS_INFORMATION pi = {NULL };
816
817   (void)envp;
818
819   /* Build the command line.  */
820   err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
821   if (err)
822     return err;
823
824   /* Note: There is no detached flag under CE.  */
825   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
826   if (!create_process (pgmname, cmdline, &pi))
827     {
828       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
829       xfree (cmdline);
830       return gpg_error (GPG_ERR_GENERAL);
831     }
832   xfree (cmdline);
833   cmdline = NULL;
834
835   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
836              " dwProcessID=%d dwThreadId=%d\n",
837              pi.hProcess, pi.hThread,
838              (int) pi.dwProcessId, (int) pi.dwThreadId);
839
840   /* Process has been created suspended; resume it now. */
841   ResumeThread (pi.hThread);
842   CloseHandle (pi.hThread);
843
844   return 0;
845 }
846
847
848 /* Kill a process; that is send an appropriate signal to the process.
849    gnupg_wait_process must be called to actually remove the process
850    from the system.  An invalid PID is ignored.  */
851 void
852 gnupg_kill_process (pid_t pid)
853 {
854   if (pid != (pid_t) INVALID_HANDLE_VALUE)
855     {
856       HANDLE process = (HANDLE) pid;
857
858       /* Arbitrary error code.  */
859       TerminateProcess (process, 1);
860     }
861 }