Print --version etc via 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 /* 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 (int 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] = handle_to_fd (r);
294   filedes[1] = handle_to_fd (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   int 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 (fds[0], 0);
325       if (filedes[0] == -1)
326         {
327           log_error ("failed to translate osfhandle %p\n", (void*)fds[0]);
328           CloseHandle (fd_to_handle (fds[1]));
329         }
330       else 
331         {
332           filedes[1] = _open_osfhandle (fds[1], 1);
333           if (filedes[1] == -1)
334             {
335               log_error ("failed to translate osfhandle %p\n", (void*)fds[1]);
336               close (filedes[0]);
337               filedes[0] = -1;
338               CloseHandle (fd_to_handle (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                      estream_t infile, estream_t outfile,
369                      void (*preexec)(void), unsigned int flags,
370                      estream_t *statusfile, pid_t *pid)
371 {
372   gpg_error_t err;
373   SECURITY_ATTRIBUTES sec_attr;
374   PROCESS_INFORMATION pi = 
375     {
376       NULL,      /* Returns process handle.  */
377       0,         /* Returns primary thread handle.  */
378       0,         /* Returns pid.  */
379       0          /* Returns tid.  */
380     };
381   STARTUPINFO si;
382   int cr_flags;
383   char *cmdline;
384   int fd, fdout, rp[2];
385
386   (void)preexec;
387
388   /* Setup return values.  */
389   *statusfile = NULL;
390   *pid = (pid_t)(-1);
391   es_fflush (infile);
392   es_rewind (infile);
393   fd = _get_osfhandle (es_fileno (infile));
394   fdout = _get_osfhandle (es_fileno (outfile));
395   if (fd == -1 || fdout == -1)
396     log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
397
398   /* Prepare security attributes.  */
399   memset (&sec_attr, 0, sizeof sec_attr );
400   sec_attr.nLength = sizeof sec_attr;
401   sec_attr.bInheritHandle = FALSE;
402   
403   /* Build the command line.  */
404   err = build_w32_commandline (pgmname, argv, &cmdline);
405   if (err)
406     return err; 
407
408   /* Create a pipe.  */
409   if (create_inheritable_pipe (rp, 1))
410     {
411       err = gpg_error (GPG_ERR_GENERAL);
412       log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
413       xfree (cmdline);
414       return err;
415     }
416   
417   /* Start the process.  Note that we can't run the PREEXEC function
418      because this would change our own environment. */
419   memset (&si, 0, sizeof si);
420   si.cb = sizeof (si);
421   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
422   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
423   si.hStdInput  = fd_to_handle (fd);
424   si.hStdOutput = fd_to_handle (fdout);
425   si.hStdError  = fd_to_handle (rp[1]);
426
427   cr_flags = (CREATE_DEFAULT_ERROR_MODE
428               | ((flags & 128)? DETACHED_PROCESS : 0)
429               | GetPriorityClass (GetCurrentProcess ())
430               | CREATE_SUSPENDED); 
431 /*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
432   if (!CreateProcess (pgmname,       /* Program to start.  */
433                       cmdline,       /* Command line arguments.  */
434                       &sec_attr,     /* Process security attributes.  */
435                       &sec_attr,     /* Thread security attributes.  */
436                       TRUE,          /* Inherit handles.  */
437                       cr_flags,      /* Creation flags.  */
438                       NULL,          /* Environment.  */
439                       NULL,          /* Use current drive/directory.  */
440                       &si,           /* Startup information. */
441                       &pi            /* Returns process information.  */
442                       ))
443     {
444       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
445       xfree (cmdline);
446       CloseHandle (fd_to_handle (rp[0]));
447       CloseHandle (fd_to_handle (rp[1]));
448       return gpg_error (GPG_ERR_GENERAL);
449     }
450   xfree (cmdline);
451   cmdline = NULL;
452
453   /* Close the other end of the pipe.  */
454   CloseHandle (fd_to_handle (rp[1]));
455   
456 /*   log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
457 /*              " dwProcessID=%d dwThreadId=%d\n", */
458 /*              pi.hProcess, pi.hThread, */
459 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
460   
461   /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
462      invalid argument error if we pass the correct processID to
463      it.  As a workaround we use -1 (ASFW_ANY).  */
464   if ( (flags & 64) )
465     gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
466
467   /* Process has been created suspended; resume it now. */
468   ResumeThread (pi.hThread);
469   CloseHandle (pi.hThread); 
470
471   {
472     int x;
473
474     x = _open_osfhandle (rp[0], 0);
475     if (x == -1)
476       log_error ("failed to translate osfhandle %p\n", (void*)rp[0] );
477     else 
478       *statusfile = es_fdopen (x, "r");
479   }
480   if (!*statusfile)
481     {
482       err = gpg_error_from_syserror ();
483       log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err));
484       CloseHandle (pi.hProcess);
485       return err;
486     }
487
488   *pid = handle_to_pid (pi.hProcess);
489   return 0;
490
491 }
492
493
494
495 /* Simplified version of gnupg_spawn_process.  This function forks and
496    then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
497    and ERRFD to stderr (any of them may be -1 to connect them to
498    /dev/null).  The arguments for the process are expected in the NULL
499    terminated array ARGV.  The program name itself should not be
500    included there.  Calling gnupg_wait_process is required.
501
502    Returns 0 on success or an error code. */
503 gpg_error_t
504 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
505                         int infd, int outfd, int errfd, pid_t *pid)
506 {
507   gpg_error_t err;
508   SECURITY_ATTRIBUTES sec_attr;
509   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
510   STARTUPINFO si;
511   char *cmdline;
512   int i;
513   HANDLE stdhd[3];
514
515   /* Setup return values.  */
516   *pid = (pid_t)(-1);
517
518   /* Prepare security attributes.  */
519   memset (&sec_attr, 0, sizeof sec_attr );
520   sec_attr.nLength = sizeof sec_attr;
521   sec_attr.bInheritHandle = FALSE;
522   
523   /* Build the command line.  */
524   err = build_w32_commandline (pgmname, argv, &cmdline);
525   if (err)
526     return err; 
527
528   memset (&si, 0, sizeof si);
529   si.cb = sizeof (si);
530   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
531   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
532   stdhd[0] = infd  == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
533   stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
534   stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
535   si.hStdInput  = infd  == -1? stdhd[0] : (void*)_get_osfhandle (infd);
536   si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
537   si.hStdError  = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
538
539 /*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
540   if (!CreateProcess (pgmname,       /* Program to start.  */
541                       cmdline,       /* Command line arguments.  */
542                       &sec_attr,     /* Process security attributes.  */
543                       &sec_attr,     /* Thread security attributes.  */
544                       TRUE,          /* Inherit handles.  */
545                       (CREATE_DEFAULT_ERROR_MODE
546                        | GetPriorityClass (GetCurrentProcess ())
547                        | CREATE_SUSPENDED | DETACHED_PROCESS),
548                       NULL,          /* Environment.  */
549                       NULL,          /* Use current drive/directory.  */
550                       &si,           /* Startup information. */
551                       &pi            /* Returns process information.  */
552                       ))
553     {
554       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
555       err = gpg_error (GPG_ERR_GENERAL);
556     }
557   else
558     err = 0;
559   xfree (cmdline);
560   for (i=0; i < 3; i++)
561     if (stdhd[i] != INVALID_HANDLE_VALUE)
562       CloseHandle (stdhd[i]);
563   if (err)
564     return err;
565
566 /*   log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
567 /*              " dwProcessID=%d dwThreadId=%d\n", */
568 /*              pi.hProcess, pi.hThread, */
569 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
570
571   /* Process has been created suspended; resume it now. */
572   ResumeThread (pi.hThread);
573   CloseHandle (pi.hThread); 
574
575   *pid = handle_to_pid (pi.hProcess);
576   return 0;
577
578 }
579
580
581 /* Wait for the process identified by PID to terminate. PGMNAME should
582    be the same as supplied to the spawn function and is only used for
583    diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
584    for any failures of the spawned program or other error codes.  If
585    EXITCODE is not NULL the exit code of the process is stored at this
586    address or -1 if it could not be retrieved. */
587 gpg_error_t
588 gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
589 {
590   gpg_err_code_t ec;
591   HANDLE proc = fd_to_handle (pid);
592   int code;
593   DWORD exc;
594
595   if (exitcode)
596     *exitcode = -1;
597
598   if (pid == (pid_t)(-1))
599     return gpg_error (GPG_ERR_INV_VALUE);
600
601   /* FIXME: We should do a pth_waitpid here.  However this has not yet
602      been implemented.  A special W32 pth system call would even be
603      better.  */
604   code = WaitForSingleObject (proc, INFINITE);
605   switch (code) 
606     {
607       case WAIT_FAILED:
608         log_error (_("waiting for process %d to terminate failed: %s\n"),
609                    (int)pid, w32_strerror (-1));
610         ec = GPG_ERR_GENERAL;
611         break;
612
613       case WAIT_OBJECT_0:
614         if (!GetExitCodeProcess (proc, &exc))
615           {
616             log_error (_("error getting exit code of process %d: %s\n"),
617                          (int)pid, w32_strerror (-1) );
618             ec = GPG_ERR_GENERAL;
619           }
620         else if (exc)
621           {
622             log_error (_("error running `%s': exit status %d\n"),
623                        pgmname, (int)exc );
624             if (exitcode)
625               *exitcode = (int)exc;
626             ec = GPG_ERR_GENERAL;
627           }
628         else
629           {
630             if (exitcode)
631               *exitcode = 0;
632             ec = 0;
633           }
634         CloseHandle (proc);
635         break;
636
637       default:
638         log_error ("WaitForSingleObject returned unexpected "
639                    "code %d for pid %d\n", code, (int)pid );
640         ec = GPG_ERR_GENERAL;
641         break;
642     }
643
644   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
645 }
646
647
648 /* Spawn a new process and immediatley detach from it.  The name of
649    the program to exec is PGMNAME and its arguments are in ARGV (the
650    programname is automatically passed as first argument).
651    Environment strings in ENVP are set.  An error is returned if
652    pgmname is not executable; to make this work it is necessary to
653    provide an absolute file name.  All standard file descriptors are
654    connected to /dev/null. */
655 gpg_error_t
656 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
657                               const char *envp[] )
658 {
659   gpg_error_t err;
660   SECURITY_ATTRIBUTES sec_attr;
661   PROCESS_INFORMATION pi = 
662     {
663       NULL,      /* Returns process handle.  */
664       0,         /* Returns primary thread handle.  */
665       0,         /* Returns pid.  */
666       0          /* Returns tid.  */
667     };
668   STARTUPINFO si;
669   int cr_flags;
670   char *cmdline;
671
672
673   /* FIXME: We don't make use of ENVP yet.  It is currently only used
674      to pass the GPG_AGENT_INFO variable to gpg-agent.  As the default
675      on windows is to use a standard socket, this does not really
676      matter.  */
677   (void)envp;
678
679   if (access (pgmname, X_OK))
680     return gpg_error_from_syserror ();
681
682   /* Prepare security attributes.  */
683   memset (&sec_attr, 0, sizeof sec_attr );
684   sec_attr.nLength = sizeof sec_attr;
685   sec_attr.bInheritHandle = FALSE;
686   
687   /* Build the command line.  */
688   err = build_w32_commandline (pgmname, argv, &cmdline);
689   if (err)
690     return err; 
691
692   /* Start the process.  */
693   memset (&si, 0, sizeof si);
694   si.cb = sizeof (si);
695   si.dwFlags = STARTF_USESHOWWINDOW;
696   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
697
698   cr_flags = (CREATE_DEFAULT_ERROR_MODE
699               | GetPriorityClass (GetCurrentProcess ())
700               | CREATE_NEW_PROCESS_GROUP
701               | DETACHED_PROCESS); 
702 /*   log_debug ("CreateProcess(detached), path=`%s' cmdline=`%s'\n", */
703 /*              pgmname, cmdline); */
704   if (!CreateProcess (pgmname,       /* Program to start.  */
705                       cmdline,       /* Command line arguments.  */
706                       &sec_attr,     /* Process security attributes.  */
707                       &sec_attr,     /* Thread security attributes.  */
708                       FALSE,         /* Inherit handles.  */
709                       cr_flags,      /* Creation flags.  */
710                       NULL,          /* Environment.  */
711                       NULL,          /* Use current drive/directory.  */
712                       &si,           /* Startup information. */
713                       &pi            /* Returns process information.  */
714                       ))
715     {
716       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
717       xfree (cmdline);
718       return gpg_error (GPG_ERR_GENERAL);
719     }
720   xfree (cmdline);
721   cmdline = NULL;
722
723 /*   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
724 /*              " dwProcessID=%d dwThreadId=%d\n", */
725 /*              pi.hProcess, pi.hThread, */
726 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
727
728   CloseHandle (pi.hThread); 
729
730   return 0;
731 }
732
733
734 /* Kill a process; that is send an appropriate signal to the process.
735    gnupg_wait_process must be called to actually remove the process
736    from the system.  An invalid PID is ignored.  */
737 void
738 gnupg_kill_process (pid_t pid)
739 {
740   if (pid != (pid_t) INVALID_HANDLE_VALUE)
741     {
742       HANDLE process = (HANDLE) pid;
743       
744       /* Arbitrary error code.  */
745       TerminateProcess (process, 1);
746     }
747 }