1 /* exechelp-w32.c - Fork and exec helpers for W32.
2 * Copyright (C) 2004, 2007, 2008, 2009,
3 * 2010 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * This file is free software; you can redistribute it and/or modify
8 * it under the terms of either
10 * - the GNU Lesser General Public License as published by the Free
11 * Software Foundation; either version 3 of the License, or (at
12 * your option) any later version.
16 * - the GNU General Public License as published by the Free
17 * Software Foundation; either version 2 of the License, or (at
18 * your option) any later version.
20 * or both in parallel, as here.
22 * This file is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, see <https://www.gnu.org/licenses/>.
33 #if !defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM)
34 #error This code is only used on W32.
48 #ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */
58 # include <sys/stat.h>
67 /* Define to 1 do enable debugging. */
68 #define DEBUG_W32_SPAWN 0
71 /* It seems Vista doesn't grok X_OK and so fails access() tests.
72 Previous versions interpreted X_OK as F_OK anyway, so we'll just
77 /* We assume that a HANDLE can be represented by an int which should
78 be true for all i386 systems (HANDLE is defined as void *) and
79 these are the only systems for which Windows is available. Further
80 we assume that -1 denotes an invalid handle. */
81 # define fd_to_handle(a) ((HANDLE)(a))
82 # define handle_to_fd(a) ((int)(a))
83 # define pid_to_handle(a) ((HANDLE)(a))
84 # define handle_to_pid(a) ((int)(a))
88 static inline gpg_error_t
89 my_error_from_syserror (void)
91 return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
94 static inline gpg_error_t
95 my_error (int errcode)
97 return gpg_err_make (default_errsource, errcode);
101 /* Return the maximum number of currently allowed open file
102 descriptors. Only useful on POSIX systems but returns a value on
103 other systems too. */
115 max_fds = 256; /* Arbitrary limit. */
121 /* Under Windows this is a dummy function. */
123 close_all_fds (int first, int *except)
130 /* Returns an array with all currently open file descriptors. The end
131 * of the array is marked by -1. The caller needs to release this
132 * array using the *standard free* and not with xfree. This allow the
133 * use of this function right at startup even before libgcrypt has
134 * been initialized. Returns NULL on error and sets ERRNO
135 * accordingly. Note that fstat prints a warning to DebugView for all
136 * invalid fds which is a bit annoying. We actually do not need this
137 * function in real code (close_all_fds is a dummy anyway) but we keep
138 * it for use by t-exechelp.c. */
140 get_all_open_fds (void)
146 array = calloc (1, sizeof *array);
152 max_fd = get_max_fds ();
153 narray = 32; /* If you change this change also t-exechelp.c. */
154 array = calloc (narray, sizeof *array);
158 /* Note: The list we return is ordered. */
159 for (idx=0, fd=0; fd < max_fd; fd++)
160 if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
166 narray += (narray < 256)? 32:256;
167 tmp = realloc (array, narray * sizeof *array);
183 /* Helper function to build_w32_commandline. */
185 build_w32_commandline_copy (char *buffer, const char *string)
190 if (!*string) /* Empty string. */
191 p = stpcpy (p, "\"\"");
192 else if (strpbrk (string, " \t\n\v\f\""))
194 /* Need to do some kind of quoting. */
195 p = stpcpy (p, "\"");
196 for (s=string; *s; s++)
206 p = stpcpy (p, string);
211 /* Build a command line for use with W32's CreateProcess. On success
212 CMDLINE gets the address of a newly allocated string. */
214 build_w32_commandline (const char *pgmname, const char * const *argv,
224 n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
227 n++; /* Need to double inner quotes. */
228 for (i=0; (s=argv[i]); i++)
230 n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
233 n++; /* Need to double inner quotes. */
237 buf = p = xtrymalloc (n);
239 return my_error_from_syserror ();
241 p = build_w32_commandline_copy (p, pgmname);
242 for (i=0; argv[i]; i++)
245 p = build_w32_commandline_copy (p, argv[i]);
253 #define INHERIT_READ 1
254 #define INHERIT_WRITE 2
255 #define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE)
257 /* Create pipe. FLAGS indicates which ends are inheritable. */
259 create_inheritable_pipe (HANDLE filedes[2], int flags)
262 SECURITY_ATTRIBUTES sec_attr;
264 memset (&sec_attr, 0, sizeof sec_attr );
265 sec_attr.nLength = sizeof sec_attr;
266 sec_attr.bInheritHandle = TRUE;
268 if (!CreatePipe (&r, &w, &sec_attr, 0))
271 if ((flags & INHERIT_READ) == 0)
272 if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0))
275 if ((flags & INHERIT_WRITE) == 0)
276 if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0))
284 log_error ("SetHandleInformation failed: %s\n", w32_strerror (-1));
292 w32_open_null (int for_write)
296 hfile = CreateFileW (L"nul",
297 for_write? GENERIC_WRITE : GENERIC_READ,
298 FILE_SHARE_READ | FILE_SHARE_WRITE,
299 NULL, OPEN_EXISTING, 0, NULL);
300 if (hfile == INVALID_HANDLE_VALUE)
301 log_debug ("can't open 'nul': %s\n", w32_strerror (-1));
307 create_pipe_and_estream (int filedes[2], int flags,
308 estream_t *r_fp, int outbound, int nonblock)
314 filedes[0] = filedes[1] = -1;
315 err = my_error (GPG_ERR_GENERAL);
316 if (!create_inheritable_pipe (fds, flags))
318 filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
319 if (filedes[0] == -1)
321 log_error ("failed to translate osfhandle %p\n", fds[0]);
322 CloseHandle (fds[1]);
326 filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND);
327 if (filedes[1] == -1)
329 log_error ("failed to translate osfhandle %p\n", fds[1]);
332 CloseHandle (fds[1]);
341 syshd.type = ES_SYSHD_HANDLE;
344 syshd.u.handle = fds[0];
345 *r_fp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
349 syshd.u.handle = fds[1];
350 *r_fp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
354 err = my_error_from_syserror ();
355 log_error (_("error creating a stream for a pipe: %s\n"),
359 filedes[0] = filedes[1] = -1;
367 /* Portable function to create a pipe. Under Windows the write end is
368 inheritable. If R_FP is not NULL, an estream is created for the
369 read end and stored at R_FP. */
371 gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
373 return create_pipe_and_estream (filedes, INHERIT_WRITE,
378 /* Portable function to create a pipe. Under Windows the read end is
379 inheritable. If R_FP is not NULL, an estream is created for the
380 write end and stored at R_FP. */
382 gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
384 return create_pipe_and_estream (filedes, INHERIT_READ,
389 /* Portable function to create a pipe. Under Windows both ends are
392 gnupg_create_pipe (int filedes[2])
394 return create_pipe_and_estream (filedes, INHERIT_BOTH,
399 /* Fork and exec the PGMNAME, see exechelp.h for details. */
401 gnupg_spawn_process (const char *pgmname, const char *argv[],
402 int *except, void (*preexec)(void), unsigned int flags,
409 SECURITY_ATTRIBUTES sec_attr;
410 PROCESS_INFORMATION pi =
412 NULL, /* Returns process handle. */
413 0, /* Returns primary thread handle. */
414 0, /* Returns pid. */
420 HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
421 HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
422 HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
423 estream_t infp = NULL;
424 estream_t outfp = NULL;
425 estream_t errfp = NULL;
426 HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
427 INVALID_HANDLE_VALUE,
428 INVALID_HANDLE_VALUE};
431 gpg_err_source_t errsource = default_errsource;
432 int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
434 (void)except; /* Not yet used. */
442 *pid = (pid_t)(-1); /* Always required. */
446 if (create_inheritable_pipe (inpipe, INHERIT_READ))
448 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
449 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
453 syshd.type = ES_SYSHD_HANDLE;
454 syshd.u.handle = inpipe[1];
455 infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
458 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
459 log_error (_("error creating a stream for a pipe: %s\n"),
461 CloseHandle (inpipe[0]);
462 CloseHandle (inpipe[1]);
463 inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
470 if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
472 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
473 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
477 syshd.type = ES_SYSHD_HANDLE;
478 syshd.u.handle = outpipe[0];
479 outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
482 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
483 log_error (_("error creating a stream for a pipe: %s\n"),
485 CloseHandle (outpipe[0]);
486 CloseHandle (outpipe[1]);
487 outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
490 else if (inpipe[1] != INVALID_HANDLE_VALUE)
491 CloseHandle (inpipe[1]);
492 if (inpipe[0] != INVALID_HANDLE_VALUE)
493 CloseHandle (inpipe[0]);
500 if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
502 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
503 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
507 syshd.type = ES_SYSHD_HANDLE;
508 syshd.u.handle = errpipe[0];
509 errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
512 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
513 log_error (_("error creating a stream for a pipe: %s\n"),
515 CloseHandle (errpipe[0]);
516 CloseHandle (errpipe[1]);
517 errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
520 else if (outpipe[0] != INVALID_HANDLE_VALUE)
521 CloseHandle (outpipe[0]);
522 if (outpipe[1] != INVALID_HANDLE_VALUE)
523 CloseHandle (outpipe[1]);
526 else if (inpipe[1] != INVALID_HANDLE_VALUE)
527 CloseHandle (inpipe[1]);
528 if (inpipe[0] != INVALID_HANDLE_VALUE)
529 CloseHandle (inpipe[0]);
534 /* Prepare security attributes. */
535 memset (&sec_attr, 0, sizeof sec_attr );
536 sec_attr.nLength = sizeof sec_attr;
537 sec_attr.bInheritHandle = FALSE;
539 /* Build the command line. */
540 err = build_w32_commandline (pgmname, argv, &cmdline);
544 if (inpipe[0] == INVALID_HANDLE_VALUE)
545 nullhd[0] = w32_open_null (0);
546 if (outpipe[1] == INVALID_HANDLE_VALUE)
547 nullhd[1] = w32_open_null (1);
548 if (errpipe[1] == INVALID_HANDLE_VALUE)
549 nullhd[2] = w32_open_null (1);
551 /* Start the process. Note that we can't run the PREEXEC function
552 because this might change our own environment. */
555 memset (&si, 0, sizeof si);
557 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
558 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
559 si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
560 si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
561 si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
563 cr_flags = (CREATE_DEFAULT_ERROR_MODE
564 | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
565 | GetPriorityClass (GetCurrentProcess ())
567 /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
568 if (!CreateProcess (pgmname, /* Program to start. */
569 cmdline, /* Command line arguments. */
570 &sec_attr, /* Process security attributes. */
571 &sec_attr, /* Thread security attributes. */
572 TRUE, /* Inherit handles. */
573 cr_flags, /* Creation flags. */
574 NULL, /* Environment. */
575 NULL, /* Use current drive/directory. */
576 &si, /* Startup information. */
577 &pi /* Returns process information. */
580 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
584 else if (inpipe[1] != INVALID_HANDLE_VALUE)
585 CloseHandle (outpipe[1]);
586 if (inpipe[0] != INVALID_HANDLE_VALUE)
587 CloseHandle (inpipe[0]);
590 else if (outpipe[0] != INVALID_HANDLE_VALUE)
591 CloseHandle (outpipe[0]);
592 if (outpipe[1] != INVALID_HANDLE_VALUE)
593 CloseHandle (outpipe[1]);
596 else if (errpipe[0] != INVALID_HANDLE_VALUE)
597 CloseHandle (errpipe[0]);
598 if (errpipe[1] != INVALID_HANDLE_VALUE)
599 CloseHandle (errpipe[1]);
600 return gpg_err_make (errsource, GPG_ERR_GENERAL);
605 /* Close the inherited handles to /dev/null. */
606 for (i=0; i < DIM (nullhd); i++)
607 if (nullhd[i] != INVALID_HANDLE_VALUE)
608 CloseHandle (nullhd[i]);
610 /* Close the inherited ends of the pipes. */
611 if (inpipe[0] != INVALID_HANDLE_VALUE)
612 CloseHandle (inpipe[0]);
613 if (outpipe[1] != INVALID_HANDLE_VALUE)
614 CloseHandle (outpipe[1]);
615 if (errpipe[1] != INVALID_HANDLE_VALUE)
616 CloseHandle (errpipe[1]);
618 /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
619 /* " dwProcessID=%d dwThreadId=%d\n", */
620 /* pi.hProcess, pi.hThread, */
621 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
622 /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */
624 /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
625 invalid argument error if we pass it the correct processID. As a
626 workaround we use -1 (ASFW_ANY). */
627 if ((flags & GNUPG_SPAWN_RUN_ASFW))
628 gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
630 /* Process has been created suspended; resume it now. */
631 ResumeThread (pi.hThread);
632 CloseHandle (pi.hThread);
641 *pid = handle_to_pid (pi.hProcess);
648 /* Simplified version of gnupg_spawn_process. This function forks and
649 then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
650 and ERRFD to stderr (any of them may be -1 to connect them to
651 /dev/null). The arguments for the process are expected in the NULL
652 terminated array ARGV. The program name itself should not be
653 included there. Calling gnupg_wait_process is required.
655 Returns 0 on success or an error code. */
657 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
658 int infd, int outfd, int errfd, pid_t *pid)
661 SECURITY_ATTRIBUTES sec_attr;
662 PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
668 /* Setup return values. */
671 /* Prepare security attributes. */
672 memset (&sec_attr, 0, sizeof sec_attr );
673 sec_attr.nLength = sizeof sec_attr;
674 sec_attr.bInheritHandle = FALSE;
676 /* Build the command line. */
677 err = build_w32_commandline (pgmname, argv, &cmdline);
681 memset (&si, 0, sizeof si);
683 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
684 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
685 stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
686 stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
687 stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
688 si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
689 si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
690 si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
692 /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
693 if (!CreateProcess (pgmname, /* Program to start. */
694 cmdline, /* Command line arguments. */
695 &sec_attr, /* Process security attributes. */
696 &sec_attr, /* Thread security attributes. */
697 TRUE, /* Inherit handles. */
698 (CREATE_DEFAULT_ERROR_MODE
699 | GetPriorityClass (GetCurrentProcess ())
700 | CREATE_SUSPENDED | DETACHED_PROCESS),
701 NULL, /* Environment. */
702 NULL, /* Use current drive/directory. */
703 &si, /* Startup information. */
704 &pi /* Returns process information. */
707 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
708 err = my_error (GPG_ERR_GENERAL);
713 for (i=0; i < 3; i++)
714 if (stdhd[i] != INVALID_HANDLE_VALUE)
715 CloseHandle (stdhd[i]);
719 /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
720 /* " dwProcessID=%d dwThreadId=%d\n", */
721 /* pi.hProcess, pi.hThread, */
722 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
724 /* Process has been created suspended; resume it now. */
725 ResumeThread (pi.hThread);
726 CloseHandle (pi.hThread);
728 *pid = handle_to_pid (pi.hProcess);
734 /* See exechelp.h for a description. */
736 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
738 return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
741 /* See exechelp.h for a description. */
743 gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
744 int hang, int *r_exitcodes)
746 gpg_err_code_t ec = 0;
751 procs = xtrycalloc (count, sizeof *procs);
753 return my_error_from_syserror ();
755 for (i = 0; i < count; i++)
760 if (pids[i] == (pid_t)(-1))
761 return my_error (GPG_ERR_INV_VALUE);
763 procs[i] = fd_to_handle (pids[i]);
766 /* FIXME: We should do a pth_waitpid here. However this has not yet
767 been implemented. A special W32 pth system call would even be
769 code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
773 ec = GPG_ERR_TIMEOUT;
777 log_error (_("waiting for processes to terminate failed: %s\n"),
779 ec = GPG_ERR_GENERAL;
783 for (i = 0; i < count; i++)
787 if (! GetExitCodeProcess (procs[i], &exc))
789 log_error (_("error getting exit code of process %d: %s\n"),
790 (int) pids[i], w32_strerror (-1) );
791 ec = GPG_ERR_GENERAL;
796 log_error (_("error running '%s': exit status %d\n"),
797 pgmnames[i], (int)exc);
799 r_exitcodes[i] = (int)exc;
800 ec = GPG_ERR_GENERAL;
811 log_error ("WaitForMultipleObjects returned unexpected "
813 ec = GPG_ERR_GENERAL;
818 return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
824 gnupg_release_process (pid_t pid)
826 if (pid != (pid_t)INVALID_HANDLE_VALUE)
828 HANDLE process = (HANDLE)pid;
830 CloseHandle (process);
835 /* Spawn a new process and immediately detach from it. The name of
836 the program to exec is PGMNAME and its arguments are in ARGV (the
837 programname is automatically passed as first argument).
838 Environment strings in ENVP are set. An error is returned if
839 pgmname is not executable; to make this work it is necessary to
840 provide an absolute file name. All standard file descriptors are
841 connected to /dev/null. */
843 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
847 SECURITY_ATTRIBUTES sec_attr;
848 PROCESS_INFORMATION pi =
850 NULL, /* Returns process handle. */
851 0, /* Returns primary thread handle. */
852 0, /* Returns pid. */
860 /* We don't use ENVP. */
863 if (access (pgmname, X_OK))
864 return my_error_from_syserror ();
866 /* Prepare security attributes. */
867 memset (&sec_attr, 0, sizeof sec_attr );
868 sec_attr.nLength = sizeof sec_attr;
869 sec_attr.bInheritHandle = FALSE;
871 /* Build the command line. */
872 err = build_w32_commandline (pgmname, argv, &cmdline);
876 /* Start the process. */
877 memset (&si, 0, sizeof si);
879 si.dwFlags = STARTF_USESHOWWINDOW;
880 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
882 cr_flags = (CREATE_DEFAULT_ERROR_MODE
883 | GetPriorityClass (GetCurrentProcess ())
884 | CREATE_NEW_PROCESS_GROUP
886 /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
887 /* pgmname, cmdline); */
888 if (!CreateProcess (pgmname, /* Program to start. */
889 cmdline, /* Command line arguments. */
890 &sec_attr, /* Process security attributes. */
891 &sec_attr, /* Thread security attributes. */
892 FALSE, /* Inherit handles. */
893 cr_flags, /* Creation flags. */
894 NULL, /* Environment. */
895 NULL, /* Use current drive/directory. */
896 &si, /* Startup information. */
897 &pi /* Returns process information. */
900 log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
902 return my_error (GPG_ERR_GENERAL);
907 /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
908 /* " dwProcessID=%d dwThreadId=%d\n", */
909 /* pi.hProcess, pi.hThread, */
910 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
912 CloseHandle (pi.hThread);
913 CloseHandle (pi.hProcess);
919 /* Kill a process; that is send an appropriate signal to the process.
920 gnupg_wait_process must be called to actually remove the process
921 from the system. An invalid PID is ignored. */
923 gnupg_kill_process (pid_t pid)
925 if (pid != (pid_t) INVALID_HANDLE_VALUE)
927 HANDLE process = (HANDLE) pid;
929 /* Arbitrary error code. */
930 TerminateProcess (process, 1);