13f6007160e6900e0442c34db21585fac3901c87
[gnupg.git] / common / exechelp-w32.c
1 /* exechelp-w32.c - Fork and exec helpers for W32.
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 W32.
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
52 #include "util.h"
53 #include "i18n.h"
54 #include "sysutils.h"
55 #include "exechelp.h"
56
57 /* Define to 1 do enable debugging.  */
58 #define DEBUG_W32_SPAWN 1
59
60
61 /* It seems Vista doesn't grok X_OK and so fails access() tests.
62    Previous versions interpreted X_OK as F_OK anyway, so we'll just
63    use F_OK directly. */
64 #undef X_OK
65 #define X_OK F_OK
66
67 /* We assume that a HANDLE can be represented by an int which should
68    be true for all i386 systems (HANDLE is defined as void *) and
69    these are the only systems for which Windows is available.  Further
70    we assume that -1 denotes an invalid handle.  */
71 # define fd_to_handle(a)  ((HANDLE)(a))
72 # define handle_to_fd(a)  ((int)(a))
73 # define pid_to_handle(a) ((HANDLE)(a))
74 # define handle_to_pid(a) ((int)(a))
75
76
77 /* Return the maximum number of currently allowed open file
78    descriptors.  Only useful on POSIX systems but returns a value on
79    other systems too.  */
80 int
81 get_max_fds (void)
82 {
83   int max_fds = -1;
84
85 #ifdef OPEN_MAX
86   if (max_fds == -1)
87     max_fds = OPEN_MAX;
88 #endif
89
90   if (max_fds == -1)
91     max_fds = 256;  /* Arbitrary limit.  */
92
93   return max_fds;
94 }
95
96
97 /* Close all file descriptors starting with descriptor FIRST.  If
98    EXCEPT is not NULL, it is expected to be a list of file descriptors
99    which shall not be closed.  This list shall be sorted in ascending
100    order with the end marked by -1.  */
101 void
102 close_all_fds (int first, int *except)
103 {
104   int max_fd = get_max_fds ();
105   int fd, i, except_start;
106
107   if (except)
108     {
109       except_start = 0;
110       for (fd=first; fd < max_fd; fd++)
111         {
112           for (i=except_start; except[i] != -1; i++)
113             {
114               if (except[i] == fd)
115                 {
116                   /* If we found the descriptor in the exception list
117                      we can start the next compare run at the next
118                      index because the exception list is ordered.  */
119                 except_start = i + 1;
120                 break;
121                 }
122             }
123           if (except[i] == -1)
124             close (fd);
125         }
126     }
127   else
128     {
129       for (fd=first; fd < max_fd; fd++)
130         close (fd);
131     }
132
133   gpg_err_set_errno (0);
134 }
135
136
137 /* Returns an array with all currently open file descriptors.  The end
138    of the array is marked by -1.  The caller needs to release this
139    array using the *standard free* and not with xfree.  This allow the
140    use of this fucntion right at startup even before libgcrypt has
141    been initialized.  Returns NULL on error and sets ERRNO
142    accordingly.  */
143 int *
144 get_all_open_fds (void)
145 {
146   int *array;
147   size_t narray;
148   int fd, max_fd, idx;
149 #ifndef HAVE_STAT
150   array = calloc (1, sizeof *array);
151   if (array)
152     array[0] = -1;
153 #else /*HAVE_STAT*/
154   struct stat statbuf;
155
156   max_fd = get_max_fds ();
157   narray = 32;  /* If you change this change also t-exechelp.c.  */
158   array = calloc (narray, sizeof *array);
159   if (!array)
160     return NULL;
161   
162   /* Note:  The list we return is ordered.  */
163   for (idx=0, fd=0; fd < max_fd; fd++)
164     if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
165       {
166         if (idx+1 >= narray)
167           {
168             int *tmp;
169
170             narray += (narray < 256)? 32:256;
171             tmp = realloc (array, narray * sizeof *array);
172             if (!tmp)
173               {
174                 free (array);
175                 return NULL;
176               }
177             array = tmp;
178           }
179         array[idx++] = fd;
180       }
181   array[idx] = -1;
182 #endif /*HAVE_STAT*/
183   return array;
184 }
185
186
187 /* Helper function to build_w32_commandline. */
188 static char *
189 build_w32_commandline_copy (char *buffer, const char *string)
190 {
191   char *p = buffer;
192   const char *s;
193
194   if (!*string) /* Empty string. */
195     p = stpcpy (p, "\"\"");
196   else if (strpbrk (string, " \t\n\v\f\""))
197     {
198       /* Need to do some kind of quoting.  */
199       p = stpcpy (p, "\"");
200       for (s=string; *s; s++)
201         {
202           *p++ = *s;
203           if (*s == '\"')
204             *p++ = *s;
205         }
206       *p++ = '\"';
207       *p = 0;
208     }
209   else
210     p = stpcpy (p, string);
211
212   return p;
213 }
214
215 /* Build a command line for use with W32's CreateProcess.  On success
216    CMDLINE gets the address of a newly allocated string.  */
217 static gpg_error_t
218 build_w32_commandline (const char *pgmname, const char * const *argv, 
219                        char **cmdline)
220 {
221   int i, n;
222   const char *s;
223   char *buf, *p;
224
225   *cmdline = NULL;
226   n = 0;
227   s = pgmname;
228   n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
229   for (; *s; s++)
230     if (*s == '\"')
231       n++;  /* Need to double inner quotes.  */
232   for (i=0; (s=argv[i]); i++)
233     {
234       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
235       for (; *s; s++)
236         if (*s == '\"')
237           n++;  /* Need to double inner quotes.  */
238     }
239   n++;
240
241   buf = p = xtrymalloc (n);
242   if (!buf)
243     return gpg_error_from_syserror ();
244
245   p = build_w32_commandline_copy (p, pgmname);
246   for (i=0; argv[i]; i++) 
247     {
248       *p++ = ' ';
249       p = build_w32_commandline_copy (p, argv[i]);
250     }
251
252   *cmdline= buf;
253   return 0;
254 }
255
256
257 /* Create pipe where one end is inheritable: With an INHERIT_IDX of 0
258    the read end is inheritable, with 1 the write end is inheritable.  */
259 static int
260 create_inheritable_pipe (HANDLE filedes[2], int inherit_idx)
261 {
262   HANDLE r, w, h;
263   SECURITY_ATTRIBUTES sec_attr;
264
265   memset (&sec_attr, 0, sizeof sec_attr );
266   sec_attr.nLength = sizeof sec_attr;
267   sec_attr.bInheritHandle = FALSE;
268     
269   if (!CreatePipe (&r, &w, &sec_attr, 0))
270     return -1;
271
272   if (!DuplicateHandle (GetCurrentProcess(), inherit_idx? w : r,
273                         GetCurrentProcess(), &h, 0,
274                         TRUE, DUPLICATE_SAME_ACCESS ))
275     {
276       log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
277       CloseHandle (r);
278       CloseHandle (w);
279       return -1;
280     }
281
282   if (inherit_idx)
283     {
284       CloseHandle (w);
285       w = h;
286     }
287   else
288     {
289       CloseHandle (r);
290       r = h;
291     }
292
293   filedes[0] = r;
294   filedes[1] = w;
295   return 0;
296 }
297
298
299 static HANDLE
300 w32_open_null (int for_write)
301 {
302   HANDLE hfile;
303
304   hfile = CreateFileW (L"nul",
305                        for_write? GENERIC_WRITE : GENERIC_READ,
306                        FILE_SHARE_READ | FILE_SHARE_WRITE,
307                        NULL, OPEN_EXISTING, 0, NULL);
308   if (hfile == INVALID_HANDLE_VALUE)
309     log_debug ("can't open `nul': %s\n", w32_strerror (-1));
310   return hfile;
311 }
312
313
314 static gpg_error_t
315 do_create_pipe (int filedes[2], int inherit_idx)
316 {
317   gpg_error_t err = 0;
318   HANDLE fds[2];
319
320   filedes[0] = filedes[1] = -1;
321   err = gpg_error (GPG_ERR_GENERAL);
322   if (!create_inheritable_pipe (fds, inherit_idx))
323     {
324       filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), 0);
325       if (filedes[0] == -1)
326         {
327           log_error ("failed to translate osfhandle %p\n", fds[0]);
328           CloseHandle (fds[1]);
329         }
330       else 
331         {
332           filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), 1);
333           if (filedes[1] == -1)
334             {
335               log_error ("failed to translate osfhandle %p\n", fds[1]);
336               close (filedes[0]);
337               filedes[0] = -1;
338               CloseHandle (fds[1]);
339             }
340           else
341             err = 0;
342         }
343     }
344   return err;
345 }
346
347 /* Portable function to create a pipe.  Under Windows the write end is
348    inheritable.  */
349 gpg_error_t
350 gnupg_create_inbound_pipe (int filedes[2])
351 {
352   return do_create_pipe (filedes, 1);
353 }
354
355
356 /* Portable function to create a pipe.  Under Windows the read end is
357    inheritable.  */
358 gpg_error_t
359 gnupg_create_outbound_pipe (int filedes[2])
360 {
361   return do_create_pipe (filedes, 0);
362 }
363
364
365 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
366 gpg_error_t
367 gnupg_spawn_process (const char *pgmname, const char *argv[],
368                      gpg_err_source_t errsource,
369                      void (*preexec)(void), unsigned int flags,
370                      estream_t infp,
371                      estream_t *r_outfp,
372                      estream_t *r_errfp,
373                      pid_t *pid)
374 {
375   gpg_error_t err;
376   SECURITY_ATTRIBUTES sec_attr;
377   PROCESS_INFORMATION pi = 
378     {
379       NULL,      /* Returns process handle.  */
380       0,         /* Returns primary thread handle.  */
381       0,         /* Returns pid.  */
382       0          /* Returns tid.  */
383     };
384   STARTUPINFO si;
385   int cr_flags;
386   char *cmdline;
387   HANDLE inhandle = INVALID_HANDLE_VALUE;
388   HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
389   HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
390   estream_t outfp = NULL;
391   estream_t errfp = NULL;
392   HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
393                       INVALID_HANDLE_VALUE,
394                       INVALID_HANDLE_VALUE};
395   int i;
396   es_syshd_t syshd;
397
398   if (r_outfp)
399     *r_outfp = NULL;
400   if (r_errfp)
401     *r_errfp = NULL;
402   *pid = (pid_t)(-1); /* Always required.  */
403   
404   if (infp)
405     {
406       es_fflush (infp);
407       es_rewind (infp);
408       es_syshd (infp, &syshd);
409       switch (syshd.type)
410         {
411         case ES_SYSHD_FD:
412           inhandle = (HANDLE)_get_osfhandle (syshd.u.fd);
413           break;
414         case ES_SYSHD_SOCK:
415           inhandle = (HANDLE)_get_osfhandle (syshd.u.sock);
416           break;
417         case ES_SYSHD_HANDLE:
418           inhandle = syshd.u.handle;
419           break;
420         default:
421           inhandle = INVALID_HANDLE_VALUE;
422           break;
423         }      
424       if (inhandle == INVALID_HANDLE_VALUE)
425         return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
426       /* FIXME: In case we can't get a system handle (e.g. due to
427          es_fopencookie we should create a piper and a feeder
428          thread.  */
429     }
430
431   if (r_outfp)
432     {
433       if (create_inheritable_pipe (outpipe, 1))
434         {
435           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
436           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
437           return err;
438         }
439
440       syshd.type = ES_SYSHD_HANDLE;
441       syshd.u.handle = outpipe[0];
442       outfp = es_sysopen (&syshd, "r");
443       if (!outfp)
444         {
445           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
446           log_error (_("error creating a stream for a pipe: %s\n"),
447                      gpg_strerror (err));
448           CloseHandle (outpipe[0]);
449           CloseHandle (outpipe[1]);
450           outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
451           return err;
452         }
453     }
454
455   if (r_errfp)
456     {
457       if (create_inheritable_pipe (errpipe, 1))
458         {
459           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
460           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
461           return err;
462         }
463
464       syshd.type = ES_SYSHD_HANDLE;
465       syshd.u.handle = errpipe[0];
466       errfp = es_sysopen (&syshd, "r");
467       if (!errfp)
468         {
469           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
470           log_error (_("error creating a stream for a pipe: %s\n"),
471                      gpg_strerror (err));
472           CloseHandle (errpipe[0]);
473           CloseHandle (errpipe[1]);
474           errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
475           if (outfp)
476             es_fclose (outfp);
477           else if (outpipe[0] != INVALID_HANDLE_VALUE)
478             CloseHandle (outpipe[0]);
479           if (outpipe[1] != INVALID_HANDLE_VALUE)
480             CloseHandle (outpipe[1]);
481           return err;
482         }
483     }
484
485   /* Prepare security attributes.  */
486   memset (&sec_attr, 0, sizeof sec_attr );
487   sec_attr.nLength = sizeof sec_attr;
488   sec_attr.bInheritHandle = FALSE;
489   
490   /* Build the command line.  */
491   err = build_w32_commandline (pgmname, argv, &cmdline);
492   if (err)
493     return err; 
494
495   if (inhandle != INVALID_HANDLE_VALUE)
496     nullhd[0] = w32_open_null (0);
497   if (outpipe[1] != INVALID_HANDLE_VALUE)
498     nullhd[1] = w32_open_null (0);
499   if (errpipe[1] != INVALID_HANDLE_VALUE)
500     nullhd[2] = w32_open_null (0);
501
502   /* Start the process.  Note that we can't run the PREEXEC function
503      because this might change our own environment. */
504   (void)preexec;
505
506   memset (&si, 0, sizeof si);
507   si.cb = sizeof (si);
508   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
509   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
510   si.hStdInput  =   inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle;
511   si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
512   si.hStdError  = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
513
514   cr_flags = (CREATE_DEFAULT_ERROR_MODE
515               | ((flags & 128)? DETACHED_PROCESS : 0)
516               | GetPriorityClass (GetCurrentProcess ())
517               | CREATE_SUSPENDED); 
518 /*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
519   if (!CreateProcess (pgmname,       /* Program to start.  */
520                       cmdline,       /* Command line arguments.  */
521                       &sec_attr,     /* Process security attributes.  */
522                       &sec_attr,     /* Thread security attributes.  */
523                       TRUE,          /* Inherit handles.  */
524                       cr_flags,      /* Creation flags.  */
525                       NULL,          /* Environment.  */
526                       NULL,          /* Use current drive/directory.  */
527                       &si,           /* Startup information. */
528                       &pi            /* Returns process information.  */
529                       ))
530     {
531       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
532       xfree (cmdline);
533       if (outfp)
534         es_fclose (outfp);
535       else if (outpipe[0] != INVALID_HANDLE_VALUE)
536         CloseHandle (outpipe[0]);
537       if (outpipe[1] != INVALID_HANDLE_VALUE)
538         CloseHandle (outpipe[1]);
539       if (errfp)
540         es_fclose (errfp);
541       else if (errpipe[0] != INVALID_HANDLE_VALUE)
542         CloseHandle (errpipe[0]);
543       if (errpipe[1] != INVALID_HANDLE_VALUE)
544         CloseHandle (errpipe[1]);
545       return gpg_err_make (errsource, GPG_ERR_GENERAL);
546     }
547   xfree (cmdline);
548   cmdline = NULL;
549
550   /* Close the inherited handles to /dev/null.  */
551   for (i=0; i < DIM (nullhd); i++)
552     if (nullhd[i] != INVALID_HANDLE_VALUE)
553       CloseHandle (nullhd[i]);
554
555   /* Close the inherited ends of the pipes.  */
556   if (outpipe[1] != INVALID_HANDLE_VALUE)
557     CloseHandle (outpipe[1]);
558   if (errpipe[1] != INVALID_HANDLE_VALUE)
559     CloseHandle (errpipe[1]);
560   
561   /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
562   /*            " dwProcessID=%d dwThreadId=%d\n", */
563   /*            pi.hProcess, pi.hThread, */
564   /*            (int) pi.dwProcessId, (int) pi.dwThreadId); */
565   /* log_debug ("                     outfp=%p errfp=%p\n", outfp, errfp); */
566
567   /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
568      invalid argument error if we pass it the correct processID.  As a
569      workaround we use -1 (ASFW_ANY).  */
570   if ( (flags & 64) )
571     gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
572
573   /* Process has been created suspended; resume it now. */
574   ResumeThread (pi.hThread);
575   CloseHandle (pi.hThread); 
576
577   if (r_outfp)
578     *r_outfp = outfp;
579   if (r_errfp)
580     *r_errfp = errfp;
581
582   *pid = handle_to_pid (pi.hProcess);
583   return 0;
584
585 }
586
587
588
589 /* Simplified version of gnupg_spawn_process.  This function forks and
590    then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
591    and ERRFD to stderr (any of them may be -1 to connect them to
592    /dev/null).  The arguments for the process are expected in the NULL
593    terminated array ARGV.  The program name itself should not be
594    included there.  Calling gnupg_wait_process is required.
595
596    Returns 0 on success or an error code. */
597 gpg_error_t
598 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
599                         int infd, int outfd, int errfd, pid_t *pid)
600 {
601   gpg_error_t err;
602   SECURITY_ATTRIBUTES sec_attr;
603   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
604   STARTUPINFO si;
605   char *cmdline;
606   int i;
607   HANDLE stdhd[3];
608
609   /* Setup return values.  */
610   *pid = (pid_t)(-1);
611
612   /* Prepare security attributes.  */
613   memset (&sec_attr, 0, sizeof sec_attr );
614   sec_attr.nLength = sizeof sec_attr;
615   sec_attr.bInheritHandle = FALSE;
616   
617   /* Build the command line.  */
618   err = build_w32_commandline (pgmname, argv, &cmdline);
619   if (err)
620     return err; 
621
622   memset (&si, 0, sizeof si);
623   si.cb = sizeof (si);
624   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
625   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
626   stdhd[0] = infd  == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
627   stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
628   stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
629   si.hStdInput  = infd  == -1? stdhd[0] : (void*)_get_osfhandle (infd);
630   si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
631   si.hStdError  = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
632
633 /*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
634   if (!CreateProcess (pgmname,       /* Program to start.  */
635                       cmdline,       /* Command line arguments.  */
636                       &sec_attr,     /* Process security attributes.  */
637                       &sec_attr,     /* Thread security attributes.  */
638                       TRUE,          /* Inherit handles.  */
639                       (CREATE_DEFAULT_ERROR_MODE
640                        | GetPriorityClass (GetCurrentProcess ())
641                        | CREATE_SUSPENDED | DETACHED_PROCESS),
642                       NULL,          /* Environment.  */
643                       NULL,          /* Use current drive/directory.  */
644                       &si,           /* Startup information. */
645                       &pi            /* Returns process information.  */
646                       ))
647     {
648       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
649       err = gpg_error (GPG_ERR_GENERAL);
650     }
651   else
652     err = 0;
653   xfree (cmdline);
654   for (i=0; i < 3; i++)
655     if (stdhd[i] != INVALID_HANDLE_VALUE)
656       CloseHandle (stdhd[i]);
657   if (err)
658     return err;
659
660 /*   log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
661 /*              " dwProcessID=%d dwThreadId=%d\n", */
662 /*              pi.hProcess, pi.hThread, */
663 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
664
665   /* Process has been created suspended; resume it now. */
666   ResumeThread (pi.hThread);
667   CloseHandle (pi.hThread); 
668
669   *pid = handle_to_pid (pi.hProcess);
670   return 0;
671
672 }
673
674
675 /* See exechelp.h for a description.  */
676 gpg_error_t
677 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
678 {
679   gpg_err_code_t ec;
680   HANDLE proc = fd_to_handle (pid);
681   int code;
682   DWORD exc;
683
684   if (r_exitcode)
685     *r_exitcode = -1;
686
687   if (pid == (pid_t)(-1))
688     return gpg_error (GPG_ERR_INV_VALUE);
689
690   /* FIXME: We should do a pth_waitpid here.  However this has not yet
691      been implemented.  A special W32 pth system call would even be
692      better.  */
693   code = WaitForSingleObject (proc, hang? INFINITE : 0);
694   switch (code) 
695     {
696     case WAIT_TIMEOUT:
697       ec = GPG_ERR_TIMEOUT;
698       break;
699
700     case WAIT_FAILED:
701       log_error (_("waiting for process %d to terminate failed: %s\n"),
702                  (int)pid, w32_strerror (-1));
703       ec = GPG_ERR_GENERAL;
704       break;
705
706     case WAIT_OBJECT_0:
707       if (!GetExitCodeProcess (proc, &exc))
708         {
709           log_error (_("error getting exit code of process %d: %s\n"),
710                      (int)pid, w32_strerror (-1) );
711           ec = GPG_ERR_GENERAL;
712         }
713       else if (exc)
714         {
715           log_error (_("error running `%s': exit status %d\n"),
716                      pgmname, (int)exc );
717           if (r_exitcode)
718             *r_exitcode = (int)exc;
719           ec = GPG_ERR_GENERAL;
720         }
721       else
722         {
723           if (r_exitcode)
724             *r_exitcode = 0;
725           ec = 0;
726         }
727       break;
728       
729     default:
730       log_error ("WaitForSingleObject returned unexpected "
731                  "code %d for pid %d\n", code, (int)pid );
732       ec = GPG_ERR_GENERAL;
733       break;
734     }
735   
736   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
737 }
738
739
740
741 void
742 gnupg_release_process (pid_t pid)
743 {
744   if (pid != (pid_t)INVALID_HANDLE_VALUE)
745     {
746       HANDLE process = (HANDLE)pid;
747       
748       CloseHandle (process);
749     }
750 }
751
752
753 /* Spawn a new process and immediatley detach from it.  The name of
754    the program to exec is PGMNAME and its arguments are in ARGV (the
755    programname is automatically passed as first argument).
756    Environment strings in ENVP are set.  An error is returned if
757    pgmname is not executable; to make this work it is necessary to
758    provide an absolute file name.  All standard file descriptors are
759    connected to /dev/null. */
760 gpg_error_t
761 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
762                               const char *envp[] )
763 {
764   gpg_error_t err;
765   SECURITY_ATTRIBUTES sec_attr;
766   PROCESS_INFORMATION pi = 
767     {
768       NULL,      /* Returns process handle.  */
769       0,         /* Returns primary thread handle.  */
770       0,         /* Returns pid.  */
771       0          /* Returns tid.  */
772     };
773   STARTUPINFO si;
774   int cr_flags;
775   char *cmdline;
776
777
778   /* FIXME: We don't make use of ENVP yet.  It is currently only used
779      to pass the GPG_AGENT_INFO variable to gpg-agent.  As the default
780      on windows is to use a standard socket, this does not really
781      matter.  */
782   (void)envp;
783
784   if (access (pgmname, X_OK))
785     return gpg_error_from_syserror ();
786
787   /* Prepare security attributes.  */
788   memset (&sec_attr, 0, sizeof sec_attr );
789   sec_attr.nLength = sizeof sec_attr;
790   sec_attr.bInheritHandle = FALSE;
791   
792   /* Build the command line.  */
793   err = build_w32_commandline (pgmname, argv, &cmdline);
794   if (err)
795     return err; 
796
797   /* Start the process.  */
798   memset (&si, 0, sizeof si);
799   si.cb = sizeof (si);
800   si.dwFlags = STARTF_USESHOWWINDOW;
801   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
802
803   cr_flags = (CREATE_DEFAULT_ERROR_MODE
804               | GetPriorityClass (GetCurrentProcess ())
805               | CREATE_NEW_PROCESS_GROUP
806               | DETACHED_PROCESS); 
807 /*   log_debug ("CreateProcess(detached), path=`%s' cmdline=`%s'\n", */
808 /*              pgmname, cmdline); */
809   if (!CreateProcess (pgmname,       /* Program to start.  */
810                       cmdline,       /* Command line arguments.  */
811                       &sec_attr,     /* Process security attributes.  */
812                       &sec_attr,     /* Thread security attributes.  */
813                       FALSE,         /* Inherit handles.  */
814                       cr_flags,      /* Creation flags.  */
815                       NULL,          /* Environment.  */
816                       NULL,          /* Use current drive/directory.  */
817                       &si,           /* Startup information. */
818                       &pi            /* Returns process information.  */
819                       ))
820     {
821       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
822       xfree (cmdline);
823       return gpg_error (GPG_ERR_GENERAL);
824     }
825   xfree (cmdline);
826   cmdline = NULL;
827
828 /*   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
829 /*              " dwProcessID=%d dwThreadId=%d\n", */
830 /*              pi.hProcess, pi.hThread, */
831 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
832
833   CloseHandle (pi.hThread); 
834
835   return 0;
836 }
837
838
839 /* Kill a process; that is send an appropriate signal to the process.
840    gnupg_wait_process must be called to actually remove the process
841    from the system.  An invalid PID is ignored.  */
842 void
843 gnupg_kill_process (pid_t pid)
844 {
845   if (pid != (pid_t) INVALID_HANDLE_VALUE)
846     {
847       HANDLE process = (HANDLE) pid;
848       
849       /* Arbitrary error code.  */
850       TerminateProcess (process, 1);
851     }
852 }