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)
313 filedes[0] = filedes[1] = -1;
314 err = my_error (GPG_ERR_GENERAL);
315 if (!create_inheritable_pipe (fds, flags))
317 filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
318 if (filedes[0] == -1)
320 log_error ("failed to translate osfhandle %p\n", fds[0]);
321 CloseHandle (fds[1]);
325 filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND);
326 if (filedes[1] == -1)
328 log_error ("failed to translate osfhandle %p\n", fds[1]);
331 CloseHandle (fds[1]);
341 *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
343 *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
346 err = my_error_from_syserror ();
347 log_error (_("error creating a stream for a pipe: %s\n"),
351 filedes[0] = filedes[1] = -1;
359 /* Portable function to create a pipe. Under Windows the write end is
360 inheritable. If R_FP is not NULL, an estream is created for the
361 read end and stored at R_FP. */
363 gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
365 return create_pipe_and_estream (filedes, INHERIT_WRITE,
370 /* Portable function to create a pipe. Under Windows the read end is
371 inheritable. If R_FP is not NULL, an estream is created for the
372 write end and stored at R_FP. */
374 gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
376 return create_pipe_and_estream (filedes, INHERIT_READ,
381 /* Portable function to create a pipe. Under Windows both ends are
384 gnupg_create_pipe (int filedes[2])
386 return create_pipe_and_estream (filedes, INHERIT_BOTH,
391 /* Fork and exec the PGMNAME, see exechelp.h for details. */
393 gnupg_spawn_process (const char *pgmname, const char *argv[],
394 int *except, void (*preexec)(void), unsigned int flags,
401 SECURITY_ATTRIBUTES sec_attr;
402 PROCESS_INFORMATION pi =
404 NULL, /* Returns process handle. */
405 0, /* Returns primary thread handle. */
406 0, /* Returns pid. */
412 HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
413 HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
414 HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
415 estream_t infp = NULL;
416 estream_t outfp = NULL;
417 estream_t errfp = NULL;
418 HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
419 INVALID_HANDLE_VALUE,
420 INVALID_HANDLE_VALUE};
423 gpg_err_source_t errsource = default_errsource;
424 int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
426 (void)except; /* Not yet used. */
434 *pid = (pid_t)(-1); /* Always required. */
438 if (create_inheritable_pipe (inpipe, INHERIT_READ))
440 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
441 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
445 syshd.type = ES_SYSHD_HANDLE;
446 syshd.u.handle = inpipe[1];
447 infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
450 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
451 log_error (_("error creating a stream for a pipe: %s\n"),
453 CloseHandle (inpipe[0]);
454 CloseHandle (inpipe[1]);
455 inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
462 if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
464 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
465 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
469 syshd.type = ES_SYSHD_HANDLE;
470 syshd.u.handle = outpipe[0];
471 outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
474 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
475 log_error (_("error creating a stream for a pipe: %s\n"),
477 CloseHandle (outpipe[0]);
478 CloseHandle (outpipe[1]);
479 outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
482 else if (inpipe[1] != INVALID_HANDLE_VALUE)
483 CloseHandle (inpipe[1]);
484 if (inpipe[0] != INVALID_HANDLE_VALUE)
485 CloseHandle (inpipe[0]);
492 if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
494 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
495 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
499 syshd.type = ES_SYSHD_HANDLE;
500 syshd.u.handle = errpipe[0];
501 errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
504 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
505 log_error (_("error creating a stream for a pipe: %s\n"),
507 CloseHandle (errpipe[0]);
508 CloseHandle (errpipe[1]);
509 errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
512 else if (outpipe[0] != INVALID_HANDLE_VALUE)
513 CloseHandle (outpipe[0]);
514 if (outpipe[1] != INVALID_HANDLE_VALUE)
515 CloseHandle (outpipe[1]);
518 else if (inpipe[1] != INVALID_HANDLE_VALUE)
519 CloseHandle (inpipe[1]);
520 if (inpipe[0] != INVALID_HANDLE_VALUE)
521 CloseHandle (inpipe[0]);
526 /* Prepare security attributes. */
527 memset (&sec_attr, 0, sizeof sec_attr );
528 sec_attr.nLength = sizeof sec_attr;
529 sec_attr.bInheritHandle = FALSE;
531 /* Build the command line. */
532 err = build_w32_commandline (pgmname, argv, &cmdline);
536 if (inpipe[0] == INVALID_HANDLE_VALUE)
537 nullhd[0] = w32_open_null (0);
538 if (outpipe[1] == INVALID_HANDLE_VALUE)
539 nullhd[1] = w32_open_null (1);
540 if (errpipe[1] == INVALID_HANDLE_VALUE)
541 nullhd[2] = w32_open_null (1);
543 /* Start the process. Note that we can't run the PREEXEC function
544 because this might change our own environment. */
547 memset (&si, 0, sizeof si);
549 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
550 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
551 si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
552 si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
553 si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
555 cr_flags = (CREATE_DEFAULT_ERROR_MODE
556 | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
557 | GetPriorityClass (GetCurrentProcess ())
559 /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
560 if (!CreateProcess (pgmname, /* Program to start. */
561 cmdline, /* Command line arguments. */
562 &sec_attr, /* Process security attributes. */
563 &sec_attr, /* Thread security attributes. */
564 TRUE, /* Inherit handles. */
565 cr_flags, /* Creation flags. */
566 NULL, /* Environment. */
567 NULL, /* Use current drive/directory. */
568 &si, /* Startup information. */
569 &pi /* Returns process information. */
572 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
576 else if (inpipe[1] != INVALID_HANDLE_VALUE)
577 CloseHandle (outpipe[1]);
578 if (inpipe[0] != INVALID_HANDLE_VALUE)
579 CloseHandle (inpipe[0]);
582 else if (outpipe[0] != INVALID_HANDLE_VALUE)
583 CloseHandle (outpipe[0]);
584 if (outpipe[1] != INVALID_HANDLE_VALUE)
585 CloseHandle (outpipe[1]);
588 else if (errpipe[0] != INVALID_HANDLE_VALUE)
589 CloseHandle (errpipe[0]);
590 if (errpipe[1] != INVALID_HANDLE_VALUE)
591 CloseHandle (errpipe[1]);
592 return gpg_err_make (errsource, GPG_ERR_GENERAL);
597 /* Close the inherited handles to /dev/null. */
598 for (i=0; i < DIM (nullhd); i++)
599 if (nullhd[i] != INVALID_HANDLE_VALUE)
600 CloseHandle (nullhd[i]);
602 /* Close the inherited ends of the pipes. */
603 if (inpipe[0] != INVALID_HANDLE_VALUE)
604 CloseHandle (inpipe[0]);
605 if (outpipe[1] != INVALID_HANDLE_VALUE)
606 CloseHandle (outpipe[1]);
607 if (errpipe[1] != INVALID_HANDLE_VALUE)
608 CloseHandle (errpipe[1]);
610 /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
611 /* " dwProcessID=%d dwThreadId=%d\n", */
612 /* pi.hProcess, pi.hThread, */
613 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
614 /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */
616 /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
617 invalid argument error if we pass it the correct processID. As a
618 workaround we use -1 (ASFW_ANY). */
619 if ((flags & GNUPG_SPAWN_RUN_ASFW))
620 gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
622 /* Process has been created suspended; resume it now. */
623 ResumeThread (pi.hThread);
624 CloseHandle (pi.hThread);
633 *pid = handle_to_pid (pi.hProcess);
640 /* Simplified version of gnupg_spawn_process. This function forks and
641 then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
642 and ERRFD to stderr (any of them may be -1 to connect them to
643 /dev/null). The arguments for the process are expected in the NULL
644 terminated array ARGV. The program name itself should not be
645 included there. Calling gnupg_wait_process is required.
647 Returns 0 on success or an error code. */
649 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
650 int infd, int outfd, int errfd, pid_t *pid)
653 SECURITY_ATTRIBUTES sec_attr;
654 PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
660 /* Setup return values. */
663 /* Prepare security attributes. */
664 memset (&sec_attr, 0, sizeof sec_attr );
665 sec_attr.nLength = sizeof sec_attr;
666 sec_attr.bInheritHandle = FALSE;
668 /* Build the command line. */
669 err = build_w32_commandline (pgmname, argv, &cmdline);
673 memset (&si, 0, sizeof si);
675 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
676 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
677 stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
678 stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
679 stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
680 si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
681 si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
682 si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
684 /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
685 if (!CreateProcess (pgmname, /* Program to start. */
686 cmdline, /* Command line arguments. */
687 &sec_attr, /* Process security attributes. */
688 &sec_attr, /* Thread security attributes. */
689 TRUE, /* Inherit handles. */
690 (CREATE_DEFAULT_ERROR_MODE
691 | GetPriorityClass (GetCurrentProcess ())
692 | CREATE_SUSPENDED | DETACHED_PROCESS),
693 NULL, /* Environment. */
694 NULL, /* Use current drive/directory. */
695 &si, /* Startup information. */
696 &pi /* Returns process information. */
699 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
700 err = my_error (GPG_ERR_GENERAL);
705 for (i=0; i < 3; i++)
706 if (stdhd[i] != INVALID_HANDLE_VALUE)
707 CloseHandle (stdhd[i]);
711 /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
712 /* " dwProcessID=%d dwThreadId=%d\n", */
713 /* pi.hProcess, pi.hThread, */
714 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
716 /* Process has been created suspended; resume it now. */
717 ResumeThread (pi.hThread);
718 CloseHandle (pi.hThread);
720 *pid = handle_to_pid (pi.hProcess);
726 /* See exechelp.h for a description. */
728 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
730 return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
733 /* See exechelp.h for a description. */
735 gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
736 int hang, int *r_exitcodes)
738 gpg_err_code_t ec = 0;
743 procs = xtrycalloc (count, sizeof *procs);
745 return my_error_from_syserror ();
747 for (i = 0; i < count; i++)
752 if (pids[i] == (pid_t)(-1))
753 return my_error (GPG_ERR_INV_VALUE);
755 procs[i] = fd_to_handle (pids[i]);
758 /* FIXME: We should do a pth_waitpid here. However this has not yet
759 been implemented. A special W32 pth system call would even be
761 code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
765 ec = GPG_ERR_TIMEOUT;
769 log_error (_("waiting for processes to terminate failed: %s\n"),
771 ec = GPG_ERR_GENERAL;
775 for (i = 0; i < count; i++)
779 if (! GetExitCodeProcess (procs[i], &exc))
781 log_error (_("error getting exit code of process %d: %s\n"),
782 (int) pids[i], w32_strerror (-1) );
783 ec = GPG_ERR_GENERAL;
788 log_error (_("error running '%s': exit status %d\n"),
789 pgmnames[i], (int)exc);
791 r_exitcodes[i] = (int)exc;
792 ec = GPG_ERR_GENERAL;
803 log_error ("WaitForMultipleObjects returned unexpected "
805 ec = GPG_ERR_GENERAL;
810 return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
816 gnupg_release_process (pid_t pid)
818 if (pid != (pid_t)INVALID_HANDLE_VALUE)
820 HANDLE process = (HANDLE)pid;
822 CloseHandle (process);
827 /* Spawn a new process and immediately detach from it. The name of
828 the program to exec is PGMNAME and its arguments are in ARGV (the
829 programname is automatically passed as first argument).
830 Environment strings in ENVP are set. An error is returned if
831 pgmname is not executable; to make this work it is necessary to
832 provide an absolute file name. All standard file descriptors are
833 connected to /dev/null. */
835 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
839 SECURITY_ATTRIBUTES sec_attr;
840 PROCESS_INFORMATION pi =
842 NULL, /* Returns process handle. */
843 0, /* Returns primary thread handle. */
844 0, /* Returns pid. */
852 /* We don't use ENVP. */
855 if (access (pgmname, X_OK))
856 return my_error_from_syserror ();
858 /* Prepare security attributes. */
859 memset (&sec_attr, 0, sizeof sec_attr );
860 sec_attr.nLength = sizeof sec_attr;
861 sec_attr.bInheritHandle = FALSE;
863 /* Build the command line. */
864 err = build_w32_commandline (pgmname, argv, &cmdline);
868 /* Start the process. */
869 memset (&si, 0, sizeof si);
871 si.dwFlags = STARTF_USESHOWWINDOW;
872 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
874 cr_flags = (CREATE_DEFAULT_ERROR_MODE
875 | GetPriorityClass (GetCurrentProcess ())
876 | CREATE_NEW_PROCESS_GROUP
878 /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
879 /* pgmname, cmdline); */
880 if (!CreateProcess (pgmname, /* Program to start. */
881 cmdline, /* Command line arguments. */
882 &sec_attr, /* Process security attributes. */
883 &sec_attr, /* Thread security attributes. */
884 FALSE, /* Inherit handles. */
885 cr_flags, /* Creation flags. */
886 NULL, /* Environment. */
887 NULL, /* Use current drive/directory. */
888 &si, /* Startup information. */
889 &pi /* Returns process information. */
892 log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
894 return my_error (GPG_ERR_GENERAL);
899 /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
900 /* " dwProcessID=%d dwThreadId=%d\n", */
901 /* pi.hProcess, pi.hThread, */
902 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
904 CloseHandle (pi.hThread);
905 CloseHandle (pi.hProcess);
911 /* Kill a process; that is send an appropriate signal to the process.
912 gnupg_wait_process must be called to actually remove the process
913 from the system. An invalid PID is ignored. */
915 gnupg_kill_process (pid_t pid)
917 if (pid != (pid_t) INVALID_HANDLE_VALUE)
919 HANDLE process = (HANDLE) pid;
921 /* Arbitrary error code. */
922 TerminateProcess (process, 1);