Add unfinished gpgtar.
[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 /* Wait for the process identified by PID to terminate. PGMNAME should
657    be the same as supplied to the spawn function and is only used for
658    diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
659    for any failures of the spawned program or other error codes.  If
660    EXITCODE is not NULL the exit code of the process is stored at this
661    address or -1 if it could not be retrieved. */
662 gpg_error_t
663 gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
664 {
665   gpg_err_code_t ec;
666   HANDLE proc = fd_to_handle (pid);
667   int code;
668   DWORD exc;
669
670   if (exitcode)
671     *exitcode = -1;
672
673   if (pid == (pid_t)(-1))
674     return gpg_error (GPG_ERR_INV_VALUE);
675
676   /* FIXME: We should do a pth_waitpid here.  However this has not yet
677      been implemented.  A special W32 pth system call would even be
678      better.  */
679   code = WaitForSingleObject (proc, INFINITE);
680   switch (code) 
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         CloseHandle (proc);
710         break;
711
712       default:
713         log_error ("WaitForSingleObject returned unexpected "
714                    "code %d for pid %d\n", code, (int)pid );
715         ec = GPG_ERR_GENERAL;
716         break;
717     }
718
719   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
720 }
721
722
723 /* Spawn a new process and immediatley detach from it.  The name of
724    the program to exec is PGMNAME and its arguments are in ARGV (the
725    programname is automatically passed as first argument).
726    Environment strings in ENVP are set.  An error is returned if
727    pgmname is not executable; to make this work it is necessary to
728    provide an absolute file name.  All standard file descriptors are
729    connected to /dev/null. */
730 gpg_error_t
731 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
732                               const char *envp[] )
733 {
734   gpg_error_t err;
735   char *cmdline;
736   PROCESS_INFORMATION pi = {NULL };
737
738   (void)envp;
739   
740   /* Build the command line.  */
741   err = build_w32_commandline (argv, -1, 1, -1, 1, -1, 1, &cmdline);
742   if (err)
743     return err; 
744
745   /* Note: There is no detached flag under CE.  */
746   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
747   if (!create_process (pgmname, cmdline, &pi))
748     {
749       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
750       xfree (cmdline);
751       return gpg_error (GPG_ERR_GENERAL);
752     }
753   xfree (cmdline);
754   cmdline = NULL;
755
756   log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
757              " dwProcessID=%d dwThreadId=%d\n",
758              pi.hProcess, pi.hThread,
759              (int) pi.dwProcessId, (int) pi.dwThreadId);
760   
761   /* Process has been created suspended; resume it now. */
762   ResumeThread (pi.hThread);
763   CloseHandle (pi.hThread); 
764
765   return 0;
766 }
767
768
769 /* Kill a process; that is send an appropriate signal to the process.
770    gnupg_wait_process must be called to actually remove the process
771    from the system.  An invalid PID is ignored.  */
772 void
773 gnupg_kill_process (pid_t pid)
774 {
775   if (pid != (pid_t) INVALID_HANDLE_VALUE)
776     {
777       HANDLE process = (HANDLE) pid;
778       
779       /* Arbitrary error code.  */
780       TerminateProcess (process, 1);
781     }
782 }