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