Pass on comments from SCD.
[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_feeded.  */
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                      estream_t infile, estream_t outfile,
489                      void (*preexec)(void), unsigned int flags,
490                      estream_t *statusfile, pid_t *pid)
491 {
492   gpg_error_t err;
493   PROCESS_INFORMATION pi = {NULL };
494   char *cmdline;
495   int inpipe[2], outpipe[2], errpipe[2];
496
497   (void)preexec;
498   (void)flags;
499   
500   /* Setup return values.  */
501   *statusfile = NULL;
502   *pid = (pid_t)(-1);
503
504   /* A NULL INFILE or OUTFILE is only used by gpgtar thus we don't
505      need to implement this for CE.  */
506   if (!infile || !outfile)
507     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
508
509   es_fflush (infile);
510   es_rewind (infile);
511
512   /* Create a pipe to copy our infile to the stdin of the child
513      process.  On success inpipe[1] is owned by the feeder.  */
514   err = create_inheritable_pipe (inpipe, 0);
515   if (err)
516     {
517       log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
518       return err;
519     }
520   err = start_feeder (infile, inpipe[1], 1);
521   if (err)
522     {
523       log_error (_("error spawning feeder: %s\n"), gpg_strerror (err));
524       CloseHandle (fd_to_handle (inpipe[1]));
525       return err;
526     }
527
528   /* Create a pipe to copy stdout of the child process to our
529      outfile. On success outpipe[0] is owned by the feeded.  */
530   err = create_inheritable_pipe (outpipe, 1);
531   if (err)
532     {
533       log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
534       return err;
535     }
536   err = start_feeder (outfile, outpipe[0], 0);
537   if (err)
538     {
539       log_error (_("error spawning feeder: %s\n"), gpg_strerror (err));
540       CloseHandle (fd_to_handle (outpipe[0]));
541       return err;
542     }
543
544
545   /* Create a pipe for use with stderr of the child process.  */
546   err = create_inheritable_pipe (errpipe, 1);
547   if (err)
548     {
549       log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
550       return err;
551     }
552
553   /* Build the command line.  */
554   err = build_w32_commandline (argv,
555                                inpipe[0], 0,
556                                outpipe[1], 0,
557                                errpipe[1], 0,
558                                &cmdline);
559   if (err)
560     {
561       CloseHandle (fd_to_handle (errpipe[0]));
562       return err; 
563     }
564
565   
566   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
567   if (!create_process (pgmname, cmdline, &pi))
568     {
569       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
570       xfree (cmdline);
571       CloseHandle (fd_to_handle (errpipe[0]));
572       return gpg_error (GPG_ERR_GENERAL);
573     }
574   xfree (cmdline);
575   cmdline = NULL;
576
577   /* Note: The other end of the pipe is a rendezvous id and thus there
578      is no need to close.  */
579
580   log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
581              " dwProcessID=%d dwThreadId=%d\n",
582              pi.hProcess, pi.hThread,
583              (int) pi.dwProcessId, (int) pi.dwThreadId);
584   
585
586   /* Process has been created suspended; resume it now. */
587   ResumeThread (pi.hThread);
588   CloseHandle (pi.hThread); 
589
590   *statusfile = es_fdopen (handle_to_fd (errpipe[0]), "r");
591   if (!*statusfile)
592     {
593       err = gpg_error_from_syserror ();
594       log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err));
595       CloseHandle (pi.hProcess);
596       return err;
597     }
598
599   *pid = handle_to_pid (pi.hProcess);
600   return 0;
601
602 }
603
604
605
606 /* Simplified version of gnupg_spawn_process.  This function forks and
607    then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
608    and ERRFD to stderr (any of them may be -1 to connect them to
609    /dev/null).  The arguments for the process are expected in the NULL
610    terminated array ARGV.  The program name itself should not be
611    included there.  Calling gnupg_wait_process is required.
612
613    Returns 0 on success or an error code. */
614 gpg_error_t
615 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
616                         int infd, int outfd, int errfd, pid_t *pid)
617 {
618   gpg_error_t err;
619   PROCESS_INFORMATION pi = {NULL};
620   char *cmdline;
621
622   /* Setup return values.  */
623   *pid = (pid_t)(-1);
624
625   if (infd != -1 || outfd != -1 || errfd != -1)
626     return gpg_error (GPG_ERR_NOT_SUPPORTED);
627
628   /* Build the command line.  */
629   err = build_w32_commandline (argv, -1, 1, -1, 1, -1, 1, &cmdline);
630   if (err)
631     return err; 
632
633   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
634   if (!create_process (pgmname, cmdline, &pi))
635     {
636       log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1));
637       xfree (cmdline);
638       return gpg_error (GPG_ERR_GENERAL);
639     }
640   xfree (cmdline);
641   cmdline = NULL;
642
643   log_debug ("CreateProcess(fd) ready: hProcess=%p hThread=%p"
644              " dwProcessID=%d dwThreadId=%d\n",
645              pi.hProcess, pi.hThread,
646              (int) pi.dwProcessId, (int) pi.dwThreadId);
647   
648   /* Process has been created suspended; resume it now. */
649   ResumeThread (pi.hThread);
650   CloseHandle (pi.hThread); 
651
652   *pid = handle_to_pid (pi.hProcess);
653   return 0;
654 }
655
656
657 /* See exechelp.h for a description.  */
658 gpg_error_t
659 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
660 {
661   gpg_err_code_t ec;
662   HANDLE proc = fd_to_handle (pid);
663   int code;
664   DWORD exc;
665
666   if (exitcode)
667     *exitcode = -1;
668
669   if (pid == (pid_t)(-1))
670     return gpg_error (GPG_ERR_INV_VALUE);
671
672   /* FIXME: We should do a pth_waitpid here.  However this has not yet
673      been implemented.  A special W32 pth system call would even be
674      better.  */
675   code = WaitForSingleObject (proc, hang? INFINITE : 0);
676   switch (code) 
677     {
678     case WAIT_TIMEOUT:
679       ec = GPG_ERR_TIMEOUT;
680       break;
681       
682     case WAIT_FAILED:
683       log_error (_("waiting for process %d to terminate failed: %s\n"),
684                  (int)pid, w32_strerror (-1));
685       ec = GPG_ERR_GENERAL;
686       break;
687
688     case WAIT_OBJECT_0:
689       if (!GetExitCodeProcess (proc, &exc))
690         {
691           log_error (_("error getting exit code of process %d: %s\n"),
692                      (int)pid, w32_strerror (-1) );
693           ec = GPG_ERR_GENERAL;
694           }
695       else if (exc)
696         {
697           log_error (_("error running `%s': exit status %d\n"),
698                        pgmname, (int)exc );
699           if (exitcode)
700             *exitcode = (int)exc;
701           ec = GPG_ERR_GENERAL;
702         }
703       else
704         {
705           if (exitcode)
706             *exitcode = 0;
707           ec = 0;
708         }
709       break;
710       
711     default:
712       log_error ("WaitForSingleObject returned unexpected "
713                  "code %d for pid %d\n", code, (int)pid );
714       ec = GPG_ERR_GENERAL;
715       break;
716     }
717
718   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
719 }
720
721
722 void
723 gnupg_release_process (pid_t pid)
724 {
725   if (pid != (pid_t)INVALID_HANDLE_VALUE)
726     {
727       HANDLE process = (HANDLE)pid;
728       
729       CloseHandle (process);
730     }
731 }
732
733
734 /* Spawn a new process and immediatley detach from it.  The name of
735    the program to exec is PGMNAME and its arguments are in ARGV (the
736    programname is automatically passed as first argument).
737    Environment strings in ENVP are set.  An error is returned if
738    pgmname is not executable; to make this work it is necessary to
739    provide an absolute file name.  All standard file descriptors are
740    connected to /dev/null. */
741 gpg_error_t
742 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
743                               const char *envp[] )
744 {
745   gpg_error_t err;
746   char *cmdline;
747   PROCESS_INFORMATION pi = {NULL };
748
749   (void)envp;
750   
751   /* Build the command line.  */
752   err = build_w32_commandline (argv, -1, 1, -1, 1, -1, 1, &cmdline);
753   if (err)
754     return err; 
755
756   /* Note: There is no detached flag under CE.  */
757   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
758   if (!create_process (pgmname, cmdline, &pi))
759     {
760       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
761       xfree (cmdline);
762       return gpg_error (GPG_ERR_GENERAL);
763     }
764   xfree (cmdline);
765   cmdline = NULL;
766
767   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
768              " dwProcessID=%d dwThreadId=%d\n",
769              pi.hProcess, pi.hThread,
770              (int) pi.dwProcessId, (int) pi.dwThreadId);
771   
772   /* Process has been created suspended; resume it now. */
773   ResumeThread (pi.hThread);
774   CloseHandle (pi.hThread); 
775
776   return 0;
777 }
778
779
780 /* Kill a process; that is send an appropriate signal to the process.
781    gnupg_wait_process must be called to actually remove the process
782    from the system.  An invalid PID is ignored.  */
783 void
784 gnupg_kill_process (pid_t pid)
785 {
786   if (pid != (pid_t) INVALID_HANDLE_VALUE)
787     {
788       HANDLE process = (HANDLE) pid;
789       
790       /* Arbitrary error code.  */
791       TerminateProcess (process, 1);
792     }
793 }