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