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