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