Pass DBUS_SESSION_BUS_ADDRESS for gnome3
[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 1
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 fucntion 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 /* Create pipe where one end is inheritable: With an INHERIT_IDX of 0
237    the read end is inheritable, with 1 the write end is inheritable.  */
238 static int
239 create_inheritable_pipe (HANDLE filedes[2], int inherit_idx)
240 {
241   HANDLE r, w, h;
242   SECURITY_ATTRIBUTES sec_attr;
243
244   memset (&sec_attr, 0, sizeof sec_attr );
245   sec_attr.nLength = sizeof sec_attr;
246   sec_attr.bInheritHandle = FALSE;
247
248   if (!CreatePipe (&r, &w, &sec_attr, 0))
249     return -1;
250
251   if (!DuplicateHandle (GetCurrentProcess(), inherit_idx? w : r,
252                         GetCurrentProcess(), &h, 0,
253                         TRUE, DUPLICATE_SAME_ACCESS ))
254     {
255       log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
256       CloseHandle (r);
257       CloseHandle (w);
258       return -1;
259     }
260
261   if (inherit_idx)
262     {
263       CloseHandle (w);
264       w = h;
265     }
266   else
267     {
268       CloseHandle (r);
269       r = h;
270     }
271
272   filedes[0] = r;
273   filedes[1] = w;
274   return 0;
275 }
276
277
278 static HANDLE
279 w32_open_null (int for_write)
280 {
281   HANDLE hfile;
282
283   hfile = CreateFileW (L"nul",
284                        for_write? GENERIC_WRITE : GENERIC_READ,
285                        FILE_SHARE_READ | FILE_SHARE_WRITE,
286                        NULL, OPEN_EXISTING, 0, NULL);
287   if (hfile == INVALID_HANDLE_VALUE)
288     log_debug ("can't open 'nul': %s\n", w32_strerror (-1));
289   return hfile;
290 }
291
292
293 static gpg_error_t
294 do_create_pipe (int filedes[2], int inherit_idx)
295 {
296   gpg_error_t err = 0;
297   HANDLE fds[2];
298
299   filedes[0] = filedes[1] = -1;
300   err = gpg_error (GPG_ERR_GENERAL);
301   if (!create_inheritable_pipe (fds, inherit_idx))
302     {
303       filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), 0);
304       if (filedes[0] == -1)
305         {
306           log_error ("failed to translate osfhandle %p\n", fds[0]);
307           CloseHandle (fds[1]);
308         }
309       else
310         {
311           filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), 1);
312           if (filedes[1] == -1)
313             {
314               log_error ("failed to translate osfhandle %p\n", fds[1]);
315               close (filedes[0]);
316               filedes[0] = -1;
317               CloseHandle (fds[1]);
318             }
319           else
320             err = 0;
321         }
322     }
323   return err;
324 }
325
326 /* Portable function to create a pipe.  Under Windows the write end is
327    inheritable.  */
328 gpg_error_t
329 gnupg_create_inbound_pipe (int filedes[2])
330 {
331   return do_create_pipe (filedes, 1);
332 }
333
334
335 /* Portable function to create a pipe.  Under Windows the read end is
336    inheritable.  */
337 gpg_error_t
338 gnupg_create_outbound_pipe (int filedes[2])
339 {
340   return do_create_pipe (filedes, 0);
341 }
342
343
344 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
345 gpg_error_t
346 gnupg_spawn_process (const char *pgmname, const char *argv[],
347                      gpg_err_source_t errsource,
348                      void (*preexec)(void), unsigned int flags,
349                      estream_t infp,
350                      estream_t *r_outfp,
351                      estream_t *r_errfp,
352                      pid_t *pid)
353 {
354   gpg_error_t err;
355   SECURITY_ATTRIBUTES sec_attr;
356   PROCESS_INFORMATION pi =
357     {
358       NULL,      /* Returns process handle.  */
359       0,         /* Returns primary thread handle.  */
360       0,         /* Returns pid.  */
361       0          /* Returns tid.  */
362     };
363   STARTUPINFO si;
364   int cr_flags;
365   char *cmdline;
366   HANDLE inhandle = INVALID_HANDLE_VALUE;
367   HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
368   HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
369   estream_t outfp = NULL;
370   estream_t errfp = NULL;
371   HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
372                       INVALID_HANDLE_VALUE,
373                       INVALID_HANDLE_VALUE};
374   int i;
375   es_syshd_t syshd;
376
377   if (r_outfp)
378     *r_outfp = NULL;
379   if (r_errfp)
380     *r_errfp = NULL;
381   *pid = (pid_t)(-1); /* Always required.  */
382
383   if (infp)
384     {
385       es_fflush (infp);
386       es_rewind (infp);
387       es_syshd (infp, &syshd);
388       switch (syshd.type)
389         {
390         case ES_SYSHD_FD:
391           inhandle = (HANDLE)_get_osfhandle (syshd.u.fd);
392           break;
393         case ES_SYSHD_SOCK:
394           inhandle = (HANDLE)_get_osfhandle (syshd.u.sock);
395           break;
396         case ES_SYSHD_HANDLE:
397           inhandle = syshd.u.handle;
398           break;
399         default:
400           inhandle = INVALID_HANDLE_VALUE;
401           break;
402         }
403       if (inhandle == INVALID_HANDLE_VALUE)
404         return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
405       /* FIXME: In case we can't get a system handle (e.g. due to
406          es_fopencookie we should create a piper and a feeder
407          thread.  */
408     }
409
410   if (r_outfp)
411     {
412       if (create_inheritable_pipe (outpipe, 1))
413         {
414           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
415           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
416           return err;
417         }
418
419       syshd.type = ES_SYSHD_HANDLE;
420       syshd.u.handle = outpipe[0];
421       outfp = es_sysopen (&syshd, "r");
422       if (!outfp)
423         {
424           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
425           log_error (_("error creating a stream for a pipe: %s\n"),
426                      gpg_strerror (err));
427           CloseHandle (outpipe[0]);
428           CloseHandle (outpipe[1]);
429           outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
430           return err;
431         }
432     }
433
434   if (r_errfp)
435     {
436       if (create_inheritable_pipe (errpipe, 1))
437         {
438           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
439           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
440           return err;
441         }
442
443       syshd.type = ES_SYSHD_HANDLE;
444       syshd.u.handle = errpipe[0];
445       errfp = es_sysopen (&syshd, "r");
446       if (!errfp)
447         {
448           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
449           log_error (_("error creating a stream for a pipe: %s\n"),
450                      gpg_strerror (err));
451           CloseHandle (errpipe[0]);
452           CloseHandle (errpipe[1]);
453           errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
454           if (outfp)
455             es_fclose (outfp);
456           else if (outpipe[0] != INVALID_HANDLE_VALUE)
457             CloseHandle (outpipe[0]);
458           if (outpipe[1] != INVALID_HANDLE_VALUE)
459             CloseHandle (outpipe[1]);
460           return err;
461         }
462     }
463
464   /* Prepare security attributes.  */
465   memset (&sec_attr, 0, sizeof sec_attr );
466   sec_attr.nLength = sizeof sec_attr;
467   sec_attr.bInheritHandle = FALSE;
468
469   /* Build the command line.  */
470   err = build_w32_commandline (pgmname, argv, &cmdline);
471   if (err)
472     return err;
473
474   if (inhandle != INVALID_HANDLE_VALUE)
475     nullhd[0] = w32_open_null (0);
476   if (outpipe[1] != INVALID_HANDLE_VALUE)
477     nullhd[1] = w32_open_null (0);
478   if (errpipe[1] != INVALID_HANDLE_VALUE)
479     nullhd[2] = w32_open_null (0);
480
481   /* Start the process.  Note that we can't run the PREEXEC function
482      because this might change our own environment. */
483   (void)preexec;
484
485   memset (&si, 0, sizeof si);
486   si.cb = sizeof (si);
487   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
488   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
489   si.hStdInput  =   inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle;
490   si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
491   si.hStdError  = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
492
493   cr_flags = (CREATE_DEFAULT_ERROR_MODE
494               | ((flags & 128)? DETACHED_PROCESS : 0)
495               | GetPriorityClass (GetCurrentProcess ())
496               | CREATE_SUSPENDED);
497 /*   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
498   if (!CreateProcess (pgmname,       /* Program to start.  */
499                       cmdline,       /* Command line arguments.  */
500                       &sec_attr,     /* Process security attributes.  */
501                       &sec_attr,     /* Thread security attributes.  */
502                       TRUE,          /* Inherit handles.  */
503                       cr_flags,      /* Creation flags.  */
504                       NULL,          /* Environment.  */
505                       NULL,          /* Use current drive/directory.  */
506                       &si,           /* Startup information. */
507                       &pi            /* Returns process information.  */
508                       ))
509     {
510       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
511       xfree (cmdline);
512       if (outfp)
513         es_fclose (outfp);
514       else if (outpipe[0] != INVALID_HANDLE_VALUE)
515         CloseHandle (outpipe[0]);
516       if (outpipe[1] != INVALID_HANDLE_VALUE)
517         CloseHandle (outpipe[1]);
518       if (errfp)
519         es_fclose (errfp);
520       else if (errpipe[0] != INVALID_HANDLE_VALUE)
521         CloseHandle (errpipe[0]);
522       if (errpipe[1] != INVALID_HANDLE_VALUE)
523         CloseHandle (errpipe[1]);
524       return gpg_err_make (errsource, GPG_ERR_GENERAL);
525     }
526   xfree (cmdline);
527   cmdline = NULL;
528
529   /* Close the inherited handles to /dev/null.  */
530   for (i=0; i < DIM (nullhd); i++)
531     if (nullhd[i] != INVALID_HANDLE_VALUE)
532       CloseHandle (nullhd[i]);
533
534   /* Close the inherited ends of the pipes.  */
535   if (outpipe[1] != INVALID_HANDLE_VALUE)
536     CloseHandle (outpipe[1]);
537   if (errpipe[1] != INVALID_HANDLE_VALUE)
538     CloseHandle (errpipe[1]);
539
540   /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
541   /*            " dwProcessID=%d dwThreadId=%d\n", */
542   /*            pi.hProcess, pi.hThread, */
543   /*            (int) pi.dwProcessId, (int) pi.dwThreadId); */
544   /* log_debug ("                     outfp=%p errfp=%p\n", outfp, errfp); */
545
546   /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
547      invalid argument error if we pass it the correct processID.  As a
548      workaround we use -1 (ASFW_ANY).  */
549   if ( (flags & 64) )
550     gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
551
552   /* Process has been created suspended; resume it now. */
553   ResumeThread (pi.hThread);
554   CloseHandle (pi.hThread);
555
556   if (r_outfp)
557     *r_outfp = outfp;
558   if (r_errfp)
559     *r_errfp = errfp;
560
561   *pid = handle_to_pid (pi.hProcess);
562   return 0;
563
564 }
565
566
567
568 /* Simplified version of gnupg_spawn_process.  This function forks and
569    then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
570    and ERRFD to stderr (any of them may be -1 to connect them to
571    /dev/null).  The arguments for the process are expected in the NULL
572    terminated array ARGV.  The program name itself should not be
573    included there.  Calling gnupg_wait_process is required.
574
575    Returns 0 on success or an error code. */
576 gpg_error_t
577 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
578                         int infd, int outfd, int errfd, pid_t *pid)
579 {
580   gpg_error_t err;
581   SECURITY_ATTRIBUTES sec_attr;
582   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
583   STARTUPINFO si;
584   char *cmdline;
585   int i;
586   HANDLE stdhd[3];
587
588   /* Setup return values.  */
589   *pid = (pid_t)(-1);
590
591   /* Prepare security attributes.  */
592   memset (&sec_attr, 0, sizeof sec_attr );
593   sec_attr.nLength = sizeof sec_attr;
594   sec_attr.bInheritHandle = FALSE;
595
596   /* Build the command line.  */
597   err = build_w32_commandline (pgmname, argv, &cmdline);
598   if (err)
599     return err;
600
601   memset (&si, 0, sizeof si);
602   si.cb = sizeof (si);
603   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
604   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
605   stdhd[0] = infd  == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
606   stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
607   stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
608   si.hStdInput  = infd  == -1? stdhd[0] : (void*)_get_osfhandle (infd);
609   si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
610   si.hStdError  = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
611
612 /*   log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
613   if (!CreateProcess (pgmname,       /* Program to start.  */
614                       cmdline,       /* Command line arguments.  */
615                       &sec_attr,     /* Process security attributes.  */
616                       &sec_attr,     /* Thread security attributes.  */
617                       TRUE,          /* Inherit handles.  */
618                       (CREATE_DEFAULT_ERROR_MODE
619                        | GetPriorityClass (GetCurrentProcess ())
620                        | CREATE_SUSPENDED | DETACHED_PROCESS),
621                       NULL,          /* Environment.  */
622                       NULL,          /* Use current drive/directory.  */
623                       &si,           /* Startup information. */
624                       &pi            /* Returns process information.  */
625                       ))
626     {
627       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
628       err = gpg_error (GPG_ERR_GENERAL);
629     }
630   else
631     err = 0;
632   xfree (cmdline);
633   for (i=0; i < 3; i++)
634     if (stdhd[i] != INVALID_HANDLE_VALUE)
635       CloseHandle (stdhd[i]);
636   if (err)
637     return err;
638
639 /*   log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
640 /*              " dwProcessID=%d dwThreadId=%d\n", */
641 /*              pi.hProcess, pi.hThread, */
642 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
643
644   /* Process has been created suspended; resume it now. */
645   ResumeThread (pi.hThread);
646   CloseHandle (pi.hThread);
647
648   *pid = handle_to_pid (pi.hProcess);
649   return 0;
650
651 }
652
653
654 /* See exechelp.h for a description.  */
655 gpg_error_t
656 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
657 {
658   gpg_err_code_t ec;
659   HANDLE proc = fd_to_handle (pid);
660   int code;
661   DWORD exc;
662
663   if (r_exitcode)
664     *r_exitcode = -1;
665
666   if (pid == (pid_t)(-1))
667     return gpg_error (GPG_ERR_INV_VALUE);
668
669   /* FIXME: We should do a pth_waitpid here.  However this has not yet
670      been implemented.  A special W32 pth system call would even be
671      better.  */
672   code = WaitForSingleObject (proc, hang? INFINITE : 0);
673   switch (code)
674     {
675     case WAIT_TIMEOUT:
676       ec = GPG_ERR_TIMEOUT;
677       break;
678
679     case WAIT_FAILED:
680       log_error (_("waiting for process %d to terminate failed: %s\n"),
681                  (int)pid, w32_strerror (-1));
682       ec = GPG_ERR_GENERAL;
683       break;
684
685     case WAIT_OBJECT_0:
686       if (!GetExitCodeProcess (proc, &exc))
687         {
688           log_error (_("error getting exit code of process %d: %s\n"),
689                      (int)pid, w32_strerror (-1) );
690           ec = GPG_ERR_GENERAL;
691         }
692       else if (exc)
693         {
694           log_error (_("error running '%s': exit status %d\n"),
695                      pgmname, (int)exc );
696           if (r_exitcode)
697             *r_exitcode = (int)exc;
698           ec = GPG_ERR_GENERAL;
699         }
700       else
701         {
702           if (r_exitcode)
703             *r_exitcode = 0;
704           ec = 0;
705         }
706       break;
707
708     default:
709       log_error ("WaitForSingleObject returned unexpected "
710                  "code %d for pid %d\n", code, (int)pid );
711       ec = GPG_ERR_GENERAL;
712       break;
713     }
714
715   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
716 }
717
718
719
720 void
721 gnupg_release_process (pid_t pid)
722 {
723   if (pid != (pid_t)INVALID_HANDLE_VALUE)
724     {
725       HANDLE process = (HANDLE)pid;
726
727       CloseHandle (process);
728     }
729 }
730
731
732 /* Spawn a new process and immediatley detach from it.  The name of
733    the program to exec is PGMNAME and its arguments are in ARGV (the
734    programname is automatically passed as first argument).
735    Environment strings in ENVP are set.  An error is returned if
736    pgmname is not executable; to make this work it is necessary to
737    provide an absolute file name.  All standard file descriptors are
738    connected to /dev/null. */
739 gpg_error_t
740 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
741                               const char *envp[] )
742 {
743   gpg_error_t err;
744   SECURITY_ATTRIBUTES sec_attr;
745   PROCESS_INFORMATION pi =
746     {
747       NULL,      /* Returns process handle.  */
748       0,         /* Returns primary thread handle.  */
749       0,         /* Returns pid.  */
750       0          /* Returns tid.  */
751     };
752   STARTUPINFO si;
753   int cr_flags;
754   char *cmdline;
755
756
757   /* We don't use ENVP.  */
758   (void)envp;
759
760   if (access (pgmname, X_OK))
761     return gpg_error_from_syserror ();
762
763   /* Prepare security attributes.  */
764   memset (&sec_attr, 0, sizeof sec_attr );
765   sec_attr.nLength = sizeof sec_attr;
766   sec_attr.bInheritHandle = FALSE;
767
768   /* Build the command line.  */
769   err = build_w32_commandline (pgmname, argv, &cmdline);
770   if (err)
771     return err;
772
773   /* Start the process.  */
774   memset (&si, 0, sizeof si);
775   si.cb = sizeof (si);
776   si.dwFlags = STARTF_USESHOWWINDOW;
777   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
778
779   cr_flags = (CREATE_DEFAULT_ERROR_MODE
780               | GetPriorityClass (GetCurrentProcess ())
781               | CREATE_NEW_PROCESS_GROUP
782               | DETACHED_PROCESS);
783 /*   log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
784 /*              pgmname, cmdline); */
785   if (!CreateProcess (pgmname,       /* Program to start.  */
786                       cmdline,       /* Command line arguments.  */
787                       &sec_attr,     /* Process security attributes.  */
788                       &sec_attr,     /* Thread security attributes.  */
789                       FALSE,         /* Inherit handles.  */
790                       cr_flags,      /* Creation flags.  */
791                       NULL,          /* Environment.  */
792                       NULL,          /* Use current drive/directory.  */
793                       &si,           /* Startup information. */
794                       &pi            /* Returns process information.  */
795                       ))
796     {
797       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
798       xfree (cmdline);
799       return gpg_error (GPG_ERR_GENERAL);
800     }
801   xfree (cmdline);
802   cmdline = NULL;
803
804 /*   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
805 /*              " dwProcessID=%d dwThreadId=%d\n", */
806 /*              pi.hProcess, pi.hThread, */
807 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
808
809   CloseHandle (pi.hThread);
810
811   return 0;
812 }
813
814
815 /* Kill a process; that is send an appropriate signal to the process.
816    gnupg_wait_process must be called to actually remove the process
817    from the system.  An invalid PID is ignored.  */
818 void
819 gnupg_kill_process (pid_t pid)
820 {
821   if (pid != (pid_t) INVALID_HANDLE_VALUE)
822     {
823       HANDLE process = (HANDLE) pid;
824
825       /* Arbitrary error code.  */
826       TerminateProcess (process, 1);
827     }
828 }