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