chiark / gitweb /
Import gnupg2_2.1.17.orig.tar.bz2
[gnupg2.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 <https://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], estream_t *r_fp, int nonblock)
454 {
455   if (r_fp)
456     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
457   else
458     return create_inheritable_pipe (filedes, 1);
459 }
460
461
462 /* Portable function to create a pipe.  Under Windows the read end is
463    inheritable (i.e. an rendezvous id).  */
464 gpg_error_t
465 gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
466 {
467   if (r_fp)
468     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
469   else
470     return create_inheritable_pipe (filedes, 0);
471 }
472
473
474 /* Portable function to create a pipe.  Under Windows both ends are
475    inheritable.  */
476 gpg_error_t
477 gnupg_create_pipe (int filedes[2])
478 {
479   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
480 }
481
482
483 static int
484 create_process (const char *pgmname, const char *cmdline,
485                 PROCESS_INFORMATION *pi)
486 {
487   int res;
488   wchar_t *wpgmname, *wcmdline;
489
490   wpgmname = utf8_to_wchar (pgmname);
491   if (!wpgmname)
492     return 0;
493   wcmdline = utf8_to_wchar (cmdline);
494   if (!wcmdline)
495     {
496       xfree (wpgmname);
497       return 0;
498     }
499   res = CreateProcess (wpgmname,      /* Program to start.  */
500                        wcmdline,      /* Command line arguments.  */
501                        NULL,          /* Process security attributes.  */
502                        NULL,          /* Thread security attributes.  */
503                        FALSE,          /* Inherit handles.  */
504                        CREATE_SUSPENDED, /* Creation flags.  */
505                        NULL,          /* Environment.  */
506                        NULL,          /* Use current drive/directory.  */
507                        NULL,          /* Startup information. */
508                        pi);           /* Returns process information.  */
509   xfree (wcmdline);
510   xfree (wpgmname);
511   return res;
512 }
513
514
515 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
516 gpg_error_t
517 gnupg_spawn_process (const char *pgmname, const char *argv[],
518                      int *except, void (*preexec)(void), unsigned int flags,
519                      estream_t *r_infp,
520                      estream_t *r_outfp,
521                      estream_t *r_errfp,
522                      pid_t *pid)
523 {
524   gpg_error_t err;
525   PROCESS_INFORMATION pi = {NULL };
526   char *cmdline;
527   es_syshd_t syshd;
528   struct {
529     HANDLE hd;
530     int rvid;
531   } inpipe = {INVALID_HANDLE_VALUE, 0};
532   struct {
533     HANDLE hd;
534     int rvid;
535   } outpipe = {INVALID_HANDLE_VALUE, 0};
536   struct {
537     HANDLE hd;
538     int rvid;
539   } errpipe = {INVALID_HANDLE_VALUE, 0};
540   estream_t outfp = NULL;
541   estream_t errfp = NULL;
542   gpg_err_source_t errsource = default_errsource;
543
544   (void)except; /* Not yet used.  */
545   (void)preexec;
546   (void)flags;
547
548   /* Setup return values.  */
549   if (r_outfp)
550     *r_outfp = NULL;
551   if (r_errfp)
552     *r_errfp = NULL;
553   *pid = (pid_t)(-1); /* Always required.  */
554
555   log_debug ("%s: enter\n", __func__);
556   if (infp)
557     {
558       es_fflush (infp);
559       es_rewind (infp);
560
561       /* Create a pipe to copy our infile to the stdin of the child
562          process.  On success inpipe.hd is owned by the feeder.  */
563       inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1);
564       if (inpipe.hd == INVALID_HANDLE_VALUE)
565         {
566           log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
567                      w32_strerror (-1));
568           gpg_err_set_errno (EIO);
569           return gpg_error_from_syserror ();
570         }
571       log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__,
572                  infp, inpipe.hd, inpipe.rvid);
573       err = start_feeder (infp, inpipe.hd, 1);
574       if (err)
575         {
576           log_error ("error spawning feeder: %s\n", gpg_strerror (err));
577           CloseHandle (inpipe.hd);
578           return err;
579         }
580       inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder.  */
581       log_debug ("%s: inpipe %p created; feeder started\n", __func__,
582                  infp);
583     }
584
585   if (r_outfp)
586     {
587       /* Create a pipe to make the stdout of the child process
588          available as a stream.  */
589       outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0);
590       if (outpipe.hd == INVALID_HANDLE_VALUE)
591         {
592           log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
593                      w32_strerror (-1));
594           gpg_err_set_errno (EIO);
595           /* Fixme release other stuff/kill feeder.  */
596           return gpg_error_from_syserror ();
597         }
598       syshd.type = ES_SYSHD_HANDLE;
599       syshd.u.handle = outpipe.hd;
600       err = 0;
601       outfp = es_sysopen (&syshd, "r");
602       if (!outfp)
603         {
604           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
605           log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
606           CloseHandle (outpipe.hd);
607           return err;
608         }
609       log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__,
610                  outfp, outpipe.hd, outpipe.rvid);
611       outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP.  */
612     }
613
614   if (r_errfp)
615     {
616       /* Create a pipe to make the stderr of the child process
617          available as a stream.  */
618       errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0);
619       if (errpipe.hd == INVALID_HANDLE_VALUE)
620         {
621           log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
622                      w32_strerror (-1));
623           gpg_err_set_errno (EIO);
624           /* Fixme release other stuff/kill feeder.  */
625           return gpg_error_from_syserror ();
626         }
627       syshd.type = ES_SYSHD_HANDLE;
628       syshd.u.handle = errpipe.hd;
629       err = 0;
630       errfp = es_sysopen (&syshd, "r");
631       if (!errfp)
632         {
633           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
634           log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
635           CloseHandle (errpipe.hd);
636           return err;
637         }
638       log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__,
639                  errfp, errpipe.hd, errpipe.rvid);
640       errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP.  */
641     }
642
643
644
645   /* Build the command line.  */
646   err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid,
647                                &cmdline);
648   if (err)
649     {
650       /* Fixme release other stuff/kill feeder.  */
651       CloseHandle (errpipe.hd);
652       return err;
653     }
654
655   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
656   if (!create_process (pgmname, cmdline, &pi))
657     {
658       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
659       xfree (cmdline);
660       /* Fixme release other stuff/kill feeder.  */
661       CloseHandle (errpipe.hd);
662       return gpg_error (GPG_ERR_GENERAL);
663     }
664   xfree (cmdline);
665   cmdline = NULL;
666
667   /* Note: The other end of the pipe is a rendezvous id and thus there
668      is no need for a close.  */
669
670   log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
671              " dwProcessID=%d dwThreadId=%d\n",
672              pi.hProcess, pi.hThread,
673              (int) pi.dwProcessId, (int) pi.dwThreadId);
674
675
676   /* Process has been created suspended; resume it now. */
677   ResumeThread (pi.hThread);
678   CloseHandle (pi.hThread);
679
680   if (r_outfp)
681     *r_outfp = outfp;
682   if (r_errfp)
683     *r_errfp = errfp;
684   *pid = handle_to_pid (pi.hProcess);
685   return 0;
686 }
687
688
689
690 /* Simplified version of gnupg_spawn_process.  This function forks and
691    then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
692    and ERRFD to stderr (any of them may be -1 to connect them to
693    /dev/null).  The arguments for the process are expected in the NULL
694    terminated array ARGV.  The program name itself should not be
695    included there.  Calling gnupg_wait_process is required.
696
697    Returns 0 on success or an error code. */
698 gpg_error_t
699 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
700                         int infd, int outfd, int errfd, pid_t *pid)
701 {
702   gpg_error_t err;
703   PROCESS_INFORMATION pi = {NULL};
704   char *cmdline;
705
706   /* Setup return values.  */
707   *pid = (pid_t)(-1);
708
709   if (infd != -1 || outfd != -1 || errfd != -1)
710     return gpg_error (GPG_ERR_NOT_SUPPORTED);
711
712   /* Build the command line.  */
713   err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
714   if (err)
715     return err;
716
717   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
718   if (!create_process (pgmname, cmdline, &pi))
719     {
720       log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1));
721       xfree (cmdline);
722       return gpg_error (GPG_ERR_GENERAL);
723     }
724   xfree (cmdline);
725   cmdline = NULL;
726
727   log_debug ("CreateProcess(fd) ready: hProcess=%p hThread=%p"
728              " dwProcessID=%d dwThreadId=%d\n",
729              pi.hProcess, pi.hThread,
730              (int) pi.dwProcessId, (int) pi.dwThreadId);
731
732   /* Process has been created suspended; resume it now. */
733   ResumeThread (pi.hThread);
734   CloseHandle (pi.hThread);
735
736   *pid = handle_to_pid (pi.hProcess);
737   return 0;
738 }
739
740
741 /* See exechelp.h for a description.  */
742 gpg_error_t
743 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
744 {
745   gpg_err_code_t ec;
746   HANDLE proc = fd_to_handle (pid);
747   int code;
748   DWORD exc;
749
750   if (exitcode)
751     *exitcode = -1;
752
753   if (pid == (pid_t)(-1))
754     return gpg_error (GPG_ERR_INV_VALUE);
755
756   /* FIXME: We should do a pth_waitpid here.  However this has not yet
757      been implemented.  A special W32 pth system call would even be
758      better.  */
759   code = WaitForSingleObject (proc, hang? INFINITE : 0);
760   switch (code)
761     {
762     case WAIT_TIMEOUT:
763       ec = GPG_ERR_TIMEOUT;
764       break;
765
766     case WAIT_FAILED:
767       log_error (_("waiting for process %d to terminate failed: %s\n"),
768                  (int)pid, w32_strerror (-1));
769       ec = GPG_ERR_GENERAL;
770       break;
771
772     case WAIT_OBJECT_0:
773       if (!GetExitCodeProcess (proc, &exc))
774         {
775           log_error (_("error getting exit code of process %d: %s\n"),
776                      (int)pid, w32_strerror (-1) );
777           ec = GPG_ERR_GENERAL;
778           }
779       else if (exc)
780         {
781           log_error (_("error running '%s': exit status %d\n"),
782                        pgmname, (int)exc );
783           if (exitcode)
784             *exitcode = (int)exc;
785           ec = GPG_ERR_GENERAL;
786         }
787       else
788         {
789           if (exitcode)
790             *exitcode = 0;
791           ec = 0;
792         }
793       break;
794
795     default:
796       log_error ("WaitForSingleObject returned unexpected "
797                  "code %d for pid %d\n", code, (int)pid );
798       ec = GPG_ERR_GENERAL;
799       break;
800     }
801
802   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
803 }
804
805
806 /* See exechelp.h for a description.  */
807 gpg_error_t
808 gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
809                       int hang, int *r_exitcodes)
810 {
811   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
812 }
813
814
815 void
816 gnupg_release_process (pid_t pid)
817 {
818   if (pid != (pid_t)INVALID_HANDLE_VALUE)
819     {
820       HANDLE process = (HANDLE)pid;
821
822       CloseHandle (process);
823     }
824 }
825
826
827 /* Spawn a new process and immediately detach from it.  The name of
828    the program to exec is PGMNAME and its arguments are in ARGV (the
829    programname is automatically passed as first argument).
830    Environment strings in ENVP are set.  An error is returned if
831    pgmname is not executable; to make this work it is necessary to
832    provide an absolute file name.  All standard file descriptors are
833    connected to /dev/null. */
834 gpg_error_t
835 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
836                               const char *envp[] )
837 {
838   gpg_error_t err;
839   char *cmdline;
840   PROCESS_INFORMATION pi = {NULL };
841
842   (void)envp;
843
844   /* Build the command line.  */
845   err = build_w32_commandline (argv, 0, 0, 0, &cmdline);
846   if (err)
847     return err;
848
849   /* Note: There is no detached flag under CE.  */
850   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
851   if (!create_process (pgmname, cmdline, &pi))
852     {
853       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
854       xfree (cmdline);
855       return gpg_error (GPG_ERR_GENERAL);
856     }
857   xfree (cmdline);
858   cmdline = NULL;
859
860   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
861              " dwProcessID=%d dwThreadId=%d\n",
862              pi.hProcess, pi.hThread,
863              (int) pi.dwProcessId, (int) pi.dwThreadId);
864
865   /* Process has been created suspended; resume it now. */
866   ResumeThread (pi.hThread);
867   CloseHandle (pi.hThread);
868
869   return 0;
870 }
871
872
873 /* Kill a process; that is send an appropriate signal to the process.
874    gnupg_wait_process must be called to actually remove the process
875    from the system.  An invalid PID is ignored.  */
876 void
877 gnupg_kill_process (pid_t pid)
878 {
879   if (pid != (pid_t) INVALID_HANDLE_VALUE)
880     {
881       HANDLE process = (HANDLE) pid;
882
883       /* Arbitrary error code.  */
884       TerminateProcess (process, 1);
885     }
886 }