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