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