diff options
-rw-r--r-- | src/gpg-error.h.in | 48 | ||||
-rw-r--r-- | src/gpgrt-int.h | 69 | ||||
-rw-r--r-- | src/mkheader.c | 26 | ||||
-rw-r--r-- | src/spawn-posix.c | 1064 | ||||
-rw-r--r-- | src/spawn-w32.c | 1170 |
5 files changed, 1245 insertions, 1132 deletions
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in index 3eacc1a..1589015 100644 --- a/src/gpg-error.h.in +++ b/src/gpg-error.h.in @@ -432,7 +432,6 @@ const char *gpgrt_check_version (const char *req_version); const char *gpg_error_check_version (const char *req_version); /* System specific type definitions. */ -@define:gpgrt_process_t@ @define:gpgrt_ssize_t@ @define:gpgrt_off_t@ @@ -1090,15 +1089,44 @@ void _gpgrt_log_assert (const char *expr, const char *file, int line, /* * Spawn functions (Not yet available) */ -/* Internal flag to inherit file descriptor/handle */ -#define GPGRT_SPAWN_INHERIT_FILE 1 - -#define GPGRT_SPAWN_NONBLOCK 16 /* Set the streams to non-blocking. */ -#define GPGRT_SPAWN_RUN_ASFW 64 /* Use AllowSetForegroundWindow on W32. */ -#define GPGRT_SPAWN_DETACHED 128 /* Start the process in the background. */ -#define GPGRT_SPAWN_KEEP_STDIN 256 -#define GPGRT_SPAWN_KEEP_STDOUT 512 -#define GPGRT_SPAWN_KEEP_STDERR 1024 +#define GPGRT_PROCESS_DETACHED (1 << 1) + +/* Specify how to keep/connect standard fds. */ +#define GPGRT_PROCESS_STDIN_PIPE (1 << 8) +#define GPGRT_PROCESS_STDOUT_PIPE (1 << 9) +#define GPGRT_PROCESS_STDERR_PIPE (1 << 10) +#define GPGRT_PROCESS_STDINOUT_SOCKETPAIR (1 << 11) +#define GPGRT_PROCESS_STDIN_KEEP (1 << 12) +#define GPGRT_PROCESS_STDOUT_KEEP (1 << 13) +#define GPGRT_PROCESS_STDERR_KEEP (1 << 14) +#define GPGRT_PROCESS_STDFDS_SETTING ( GPGRT_PROCESS_STDIN_PIPE \ + | GPGRT_PROCESS_STDOUT_PIPE | GPGRT_PROCESS_STDERR_PIPE \ + | GPGRT_PROCESS_STDINOUT_SOCKETPAIR | GPGRT_PROCESS_STDIN_KEEP \ + | GPGRT_PROCESS_STDOUT_KEEP | GPGRT_PROCESS_STDERR_KEEP) + +#define GPGRT_PROCESS_STREAM_NONBLOCK (1 << 16) + +typedef struct gpgrt_process *gpgrt_process_t; +@define:struct_spawn_cb_arg@ + +enum gpgrt_process_requests + { + /* Portable requests */ + GPGRT_PROCESS_NOP = 0, + GPGRT_PROCESS_GET_PROC_ID = 1, + GPGRT_PROCESS_GET_EXIT_ID = 2, + + /* POSIX only */ + GPGRT_PROCESS_GET_PID = 16, + GPGRT_PROCESS_GET_WSTATUS = 17, + GPGRT_PROCESS_KILL = 18, + + /* Windows only */ + GPGRT_PROCESS_GET_P_HANDLE = 32, + GPGRT_PROCESS_GET_HANDLES = 33, + GPGRT_PROCESS_GET_EXIT_CODE = 34, + GPGRT_PROCESS_KILL_WITH_EC = 35 + }; #if 0 /* Function and convenience macros to create pipes. */ diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h index adddb26..2cc5f41 100644 --- a/src/gpgrt-int.h +++ b/src/gpgrt-int.h @@ -684,45 +684,36 @@ gpg_err_code_t _gpgrt_make_pipe (int filedes[2], estream_t *r_fp, * descriptor. * */ -gpg_err_code_t -_gpgrt_spawn_process (const char *pgmname, const char *argv[], - int *execpt, unsigned int flags, - estream_t *r_infp, - estream_t *r_outfp, - estream_t *r_errfp, - gpgrt_process_t *r_process_id); - - -/* Variant of gpgrt_spawn_process. This function forks and then execs - * PGMNAME, while connecting INFD to stdin, OUTFD to stdout and ERRFD - * to stderr (any of them may be -1 to connect them to /dev/null). - * The arguments for the process are expected in the NULL terminated - * array ARGV. The program name itself should not be included there. - * Calling gpgrt_wait_process and gpgrt_release_process is required. - * Returns 0 on success or an error code. If SPAWN_CB is not NULL, - * the given function will be called with SPAWN_CB_ARG to determine if - * file descriptors/handles should be inherited or not. The callback - * function should return 1 to ask keeping file descriptors/handles. - * If SPAWN_CB is NULL, or it returns 0, all file descriptors (except - * INFD, OUTFD, and ERRFD) will be closed on POSIX machine. On POSIX - * machine, it is called right after the fork, by child process. - */ -gpg_err_code_t _gpgrt_spawn_process_fd (const char *pgmname, - const char *argv[], - int infd, int outfd, int errfd, - int (*spawn_cb) (void *), - void *spawn_cb_arg, - gpgrt_process_t *r_process_id); - -/* Spawn a new process and immediately detach from it. The name of - * the program to exec is PGMNAME and its arguments are in ARGV (the - * programname is automatically passed as first argument). - * Environment strings in ENVP are set. An error is returned if - * pgmname is not executable; to make this work it is necessary to - * provide an absolute file name. */ -gpg_err_code_t _gpgrt_spawn_process_detached (const char *pgmname, - const char *argv[], - const char *envp[]); +gpg_err_code_t _gpgrt_process_spawn (const char *pgmname, const char *argv1[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gpgrt_process_t *r_process); + +gpg_err_code_t _gpgrt_process_terminate (gpgrt_process_t process); + +gpg_err_code_t _gpgrt_process_get_fds (gpgrt_process_t process, + unsigned int flags, + int *r_fd_in, int *r_fd_out, + int *r_fd_err); + +gpg_err_code_t _gpgrt_process_get_streams (gpgrt_process_t process, + unsigned int flags, + gpgrt_stream_t *r_fp_in, + gpgrt_stream_t *r_fp_out, + gpgrt_stream_t *r_fp_err); + +gpg_err_code_t _gpgrt_process_ctl (gpgrt_process_t process, + unsigned int request, ...); + +gpg_err_code_t _gpgrt_process_wait (gpgrt_process_t process, int hang); + +void _gpgrt_process_release (gpgrt_process_t process); + +gpg_err_code_t _gpgrt_process_wait_list (gpgrt_process_t *process_list, + int count, int hang); + +void _gpgrt_spawn_helper (struct spawn_cb_arg *sca); /* If HANG is true, waits for the process identified by PROCESS_ID to * exit; if HANG is false, checks whether the process has terminated. diff --git a/src/mkheader.c b/src/mkheader.c index 2d7298c..cb4d574 100644 --- a/src/mkheader.c +++ b/src/mkheader.c @@ -573,23 +573,27 @@ write_special (const char *fname, int lnr, const char *tag) else fputs ("int", stdout); } - else if (!strcmp (tag, "define:gpgrt_process_t")) + else if (!strcmp (tag, "define:struct_spawn_cb_arg")) { if (have_w32_system || have_w64_system) { - fputs ("typedef void *gpgrt_process_t;\n", stdout); + fputs ("struct spawn_cb_arg;\n", stdout); + fputs ("#ifdef NEED_STRUCT_SPAWN_CB_ARG\n", stdout); + fputs ("struct spawn_cb_arg {\n", stdout); + fputs (" HANDLE hd[3];\n", stdout); + fputs (" HANDLE *inherit_hds;\n", stdout); + fputs (" BOOL allow_foreground_window;\n", stdout); + fputs (" void *arg;\n", stdout); + fputs ("};\n", stdout); + fputs ("#endif /* NEED_STRUCT_SPAWN_CB_ARG */\n", stdout); } else { - if (have_sys_types_h) - { - if (!sys_types_h_included) - { - fputs ("#include <sys/types.h>\n", stdout); - sys_types_h_included = 1; - } - } - fputs ("typedef pid_t gpgrt_process_t;\n", stdout); + fputs ("struct spawn_cb_arg {\n", stdout); + fputs (" int fds[3];\n", stdout); + fputs (" int *except_fds;\n", stdout); + fputs (" void *arg;\n", stdout); + fputs ("};\n", stdout); } } else if (!strcmp (tag, "include:err-sources")) diff --git a/src/spawn-posix.c b/src/spawn-posix.c index 8e36ee1..1c9f7ff 100644 --- a/src/spawn-posix.c +++ b/src/spawn-posix.c @@ -57,15 +57,6 @@ #include "gpgrt-int.h" -static void -out_of_core (int line) -{ - _gpgrt_log_fatal ("malloc failed at line %d: %s\n", - line, _gpg_strerror (_gpg_err_code_from_syserror ())); - /*NOTREACHED*/ -} - - /* Return the maximum number of currently allowed open file * descriptors. Only useful on POSIX systems but returns a value on * other systems too. */ @@ -93,7 +84,6 @@ get_max_fds (void) const char *s; int x; - /* FIXME: Check gpgme on how to do this right on Linux. */ dir = opendir ("/proc/self/fd"); if (dir) { @@ -198,136 +188,6 @@ _gpgrt_close_all_fds (int first, int *except) _gpg_err_set_errno (0); } - -/* Returns an array with all currently open file descriptors. The end - * of the array is marked by -1. The caller needs to release this - * array using the *standard free* and not with xfree. This allow the - * use of this function right at startup even before libgcrypt has - * been initialized. Returns NULL on error and sets ERRNO - * accordingly. - * - * FIXME: Needs to be adjusted for use here. - */ -#if 0 -int * -get_all_open_fds (void) -{ - int *array; - size_t narray; - int fd, max_fd, idx; -#ifndef HAVE_STAT - array = calloc (1, sizeof *array); - if (array) - array[0] = -1; -#else /*HAVE_STAT*/ - struct stat statbuf; - - max_fd = get_max_fds (); - narray = 32; /* If you change this change also t-exechelp.c. */ - array = calloc (narray, sizeof *array); - if (!array) - return NULL; - - /* Note: The list we return is ordered. */ - for (idx=0, fd=0; fd < max_fd; fd++) - if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) - { - if (idx+1 >= narray) - { - int *tmp; - - narray += (narray < 256)? 32:256; - tmp = realloc (array, narray * sizeof *array); - if (!tmp) - { - free (array); - return NULL; - } - array = tmp; - } - array[idx++] = fd; - } - array[idx] = -1; -#endif /*HAVE_STAT*/ - return array; -} -#endif /*0*/ - - -/* The exec core used right after the fork. This will never return. */ -static void -do_exec (const char *pgmname, const char *argv[], - int fd_in, int fd_out, int fd_err, - int *except, unsigned int flags) -{ - char **arg_list; - int i, j; - int fds[3]; - int nodevnull[3]; - - fds[0] = fd_in; - fds[1] = fd_out; - fds[2] = fd_err; - - nodevnull[0] = !!(flags & GPGRT_SPAWN_KEEP_STDIN); - nodevnull[1] = !!(flags & GPGRT_SPAWN_KEEP_STDOUT); - nodevnull[2] = !!(flags & GPGRT_SPAWN_KEEP_STDERR); - - /* Create the command line argument array. */ - i = 0; - if (argv) - while (argv[i]) - i++; - arg_list = xtrycalloc (i+2, sizeof *arg_list); - if (!arg_list) - out_of_core (__LINE__); - arg_list[0] = strrchr (pgmname, '/'); - if (arg_list[0]) - arg_list[0]++; - else - { - arg_list[0] = xtrystrdup (pgmname); - if (!arg_list[0]) - out_of_core (__LINE__); - } - if (argv) - for (i=0,j=1; argv[i]; i++, j++) - arg_list[j] = (char*)argv[i]; - - /* Assign /dev/null to unused FDs. */ - for (i=0; i <= 2; i++) - { - if (nodevnull[i]) - continue; - if (fds[i] == -1) - { - fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY); - if (fds[i] == -1) - _gpgrt_log_fatal ("failed to open '%s': %s\n", - "/dev/null", strerror (errno)); - } - } - - /* Connect the standard files. */ - for (i=0; i <= 2; i++) - { - if (nodevnull[i]) - continue; - if (fds[i] != i && dup2 (fds[i], i) == -1) - _gpgrt_log_fatal ("dup2 std%s failed: %s\n", - i==0?"in":i==1?"out":"err", strerror (errno)); - } - - /* Close all other files. */ - if (!(flags & GPGRT_SPAWN_INHERIT_FILE)) - _gpgrt_close_all_fds (3, except); - - execv (pgmname, arg_list); - /* No way to print anything, as we have may have closed all streams. */ - _exit (127); -} - - /* Helper for _gpgrt_make_pipe. */ static gpg_err_code_t do_create_pipe (int filedes[2]) @@ -398,107 +258,102 @@ _gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction, return do_create_pipe (filedes); } -/* - * UNION PROCESS_ID: - * - * gpgrt_process_t object is an object which represents process handle. - * It must be same size as pid_t and must have same bit pattern. - */ -union process { - gpgrt_process_t process_id; - pid_t pid; -}; +#include <sys/socket.h> -static gpgrt_process_t -convert_from_pid (pid_t pid) +static gpg_err_code_t +do_create_socketpair (int filedes[2]) { - union process u; + gpg_error_t err = 0; + + _gpgrt_pre_syscall (); + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, filedes) == -1) + { + err = _gpg_err_code_from_syserror (); + filedes[0] = filedes[1] = -1; + } + _gpgrt_post_syscall (); - u.pid = pid; - return u.process_id; + return err; } -static pid_t -convert_from_process (gpgrt_process_t process_id) +static int +posix_open_null (int for_write) { - union process u; + int fd; - u.process_id = process_id; - return u.pid; + fd = open ("/dev/null", for_write? O_WRONLY : O_RDONLY); + if (fd == -1) + _gpgrt_log_fatal ("failed to open '/dev/null': %s\n", strerror (errno)); + return fd; } +static void +my_exec (const char *pgmname, const char *argv[], struct spawn_cb_arg *sca) +{ + int i; -/* Fork and exec the PGMNAME, see gpgrt-int.h for details. */ -gpg_err_code_t -_gpgrt_spawn_process (const char *pgmname, const char *argv[], - int *except, unsigned int flags, - estream_t *r_infp, - estream_t *r_outfp, - estream_t *r_errfp, - gpgrt_process_t *r_process_id) + /* Assign /dev/null to unused FDs. */ + for (i = 0; i <= 2; i++) + if (sca->fds[i] == -1) + sca->fds[i] = posix_open_null (i); + + /* Connect the standard files. */ + for (i = 0; i <= 2; i++) + if (sca->fds[i] != i) + { + if (dup2 (sca->fds[i], i) == -1) + _gpgrt_log_fatal ("dup2 std%s failed: %s\n", + i==0?"in":i==1?"out":"err", strerror (errno)); + /* + * We don't close sca.fds[i] here, but close them by + * close_all_fds. Note that there may be same one in three of + * sca->fds[i]. + */ + } + + /* Close all other files. */ + _gpgrt_close_all_fds (3, sca->except_fds); + + execv (pgmname, (char *const *)argv); + /* No way to print anything, as we have may have closed all streams. */ + _exit (127); +} + + +static void +call_spawn_cb (struct spawn_cb_arg *sca, + int fd_in, int fd_out, int fd_err, + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) { - gpg_error_t err; - int inpipe[2] = {-1, -1}; - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - estream_t infp = NULL; - estream_t outfp = NULL; - estream_t errfp = NULL; - int nonblock = !!(flags & GPGRT_SPAWN_NONBLOCK); - pid_t pid; + sca->fds[0] = fd_in; + sca->fds[1] = fd_out; + sca->fds[2] = fd_err; + sca->except_fds = NULL; + sca->arg = spawn_cb_arg; + if (spawn_cb) + (*spawn_cb) (sca); +} - if (r_infp) - *r_infp = NULL; - if (r_outfp) - *r_outfp = NULL; - if (r_errfp) - *r_errfp = NULL; - *r_process_id = convert_from_pid (-1); - if (r_infp) - { - err = _gpgrt_create_outbound_pipe (inpipe, &infp, nonblock); - if (err) - return err; - } +static gpg_err_code_t +spawn_detached (const char *pgmname, const char *argv[], + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) +{ + gpg_err_code_t ec; + pid_t pid; - if (r_outfp) + /* FIXME: Is this GnuPG specific or should we keep it. */ + if (getuid() != geteuid()) { - err = _gpgrt_create_inbound_pipe (outpipe, &outfp, nonblock); - if (err) - { - if (infp) - _gpgrt_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - return err; - } + xfree (argv); + return GPG_ERR_BUG; } - if (r_errfp) + if (access (pgmname, X_OK)) { - err = _gpgrt_create_inbound_pipe (errpipe, &errfp, nonblock); - if (err) - { - if (infp) - _gpgrt_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - if (outfp) - _gpgrt_fclose (outfp); - else if (outpipe[0] != -1) - close (outpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - - return err; - } + ec = _gpg_err_code_from_syserror (); + xfree (argv); + return ec; } _gpgrt_pre_syscall (); @@ -506,444 +361,535 @@ _gpgrt_spawn_process (const char *pgmname, const char *argv[], _gpgrt_post_syscall (); if (pid == (pid_t)(-1)) { - err = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err)); - - if (infp) - _gpgrt_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - if (outfp) - _gpgrt_fclose (outfp); - else if (outpipe[0] != -1) - close (outpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - - if (errfp) - _gpgrt_fclose (errfp); - else if (errpipe[0] != -1) - close (errpipe[0]); - if (errpipe[1] != -1) - close (errpipe[1]); - return err; + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (ec)); + xfree (argv); + return ec; } if (!pid) { - /* This is the child. */ - _gpgrt_fclose (infp); - _gpgrt_fclose (outfp); - _gpgrt_fclose (errfp); - do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], - except, flags); + pid_t pid2; + struct spawn_cb_arg sca; + + if (setsid() == -1 || chdir ("/")) + _exit (1); + + pid2 = fork (); /* Double fork to let init take over the new child. */ + if (pid2 == (pid_t)(-1)) + _exit (1); + if (pid2) + _exit (0); /* Let the parent exit immediately. */ + + call_spawn_cb (&sca, -1, -1, -1, spawn_cb, spawn_cb_arg); + + my_exec (pgmname, argv, &sca); /*NOTREACHED*/ } - /* This is the parent. */ - if (inpipe[0] != -1) - close (inpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - if (errpipe[1] != -1) - close (errpipe[1]); - - if (r_infp) - *r_infp = infp; - if (r_outfp) - *r_outfp = outfp; - if (r_errfp) - *r_errfp = errfp; + _gpgrt_pre_syscall (); + if (waitpid (pid, NULL, 0) == -1) + { + _gpgrt_post_syscall (); + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error ("waitpid failed in gpgrt_spawn_process_detached: %s", + _gpg_strerror (ec)); + return ec; + } + else + _gpgrt_post_syscall (); - *r_process_id = convert_from_pid (pid); return 0; } +void +_gpgrt_spawn_helper (struct spawn_cb_arg *sca) +{ + int *user_except = sca->arg; + sca->except_fds = user_except; +} + +struct gpgrt_process { + const char *pgmname; + unsigned int terminated :1; /* or detached */ + unsigned int flags; + pid_t pid; + int fd_in; + int fd_out; + int fd_err; + int wstatus; +}; -/* Fork and exec the PGMNAME using FDs, see gpgrt-int.h for details. */ gpg_err_code_t -_gpgrt_spawn_process_fd (const char *pgmname, const char *argv[], - int infd, int outfd, int errfd, - int (*spawn_cb) (void *), - void *spawn_cb_arg, - gpgrt_process_t *r_process_id) +_gpgrt_process_spawn (const char *pgmname, const char *argv1[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gpgrt_process_t *r_process) { - gpg_error_t err; - int ask_inherit_fds = 0; + gpg_err_code_t ec; + gpgrt_process_t process; + int fd_in[2]; + int fd_out[2]; + int fd_err[2]; pid_t pid; + const char **argv; + int i, j; + + if (r_process) + *r_process = NULL; + + /* Create the command line argument array. */ + i = 0; + if (argv1) + while (argv1[i]) + i++; + argv = xtrycalloc (i+2, sizeof *argv); + if (!argv) + return _gpg_err_code_from_syserror (); + argv[0] = strrchr (pgmname, '/'); + if (argv[0]) + argv[0]++; + else + argv[0] = pgmname; + + if (argv1) + for (i=0, j=1; argv1[i]; i++, j++) + argv[j] = argv1[i]; + + if ((flags & GPGRT_PROCESS_DETACHED)) + { + if ((flags & GPGRT_PROCESS_STDFDS_SETTING)) + { + xfree (argv); + return GPG_ERR_INV_FLAG; + } + + /* In detached case, it must be no R_PROCESS. */ + if (r_process) + { + xfree (argv); + return GPG_ERR_INV_ARG; + } + + return spawn_detached (pgmname, argv, spawn_cb, spawn_cb_arg); + } + + process = xtrycalloc (1, sizeof (struct gpgrt_process)); + if (process == NULL) + { + xfree (argv); + return _gpg_err_code_from_syserror (); + } + + process->pgmname = pgmname; + process->flags = flags; + + if ((flags & GPGRT_PROCESS_STDINOUT_SOCKETPAIR)) + { + ec = do_create_socketpair (fd_in); + if (ec) + { + xfree (process); + xfree (argv); + return ec; + } + fd_out[0] = dup (fd_in[0]); + fd_out[1] = dup (fd_in[1]); + } + else + { + if ((flags & GPGRT_PROCESS_STDIN_PIPE)) + { + ec = do_create_pipe (fd_in); + if (ec) + { + xfree (process); + xfree (argv); + return ec; + } + } + else if ((flags & GPGRT_PROCESS_STDIN_KEEP)) + { + fd_in[0] = 0; + fd_in[1] = -1; + } + else + { + fd_in[0] = -1; + fd_in[1] = -1; + } + + if ((flags & GPGRT_PROCESS_STDOUT_PIPE)) + { + ec = do_create_pipe (fd_out); + if (ec) + { + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_in[1] >= 0) + close (fd_in[1]); + xfree (process); + xfree (argv); + return ec; + } + } + else if ((flags & GPGRT_PROCESS_STDOUT_KEEP)) + { + fd_out[0] = -1; + fd_out[1] = 1; + } + else + { + fd_out[0] = -1; + fd_out[1] = -1; + } + } + + if ((flags & GPGRT_PROCESS_STDERR_PIPE)) + { + ec = do_create_pipe (fd_err); + if (ec) + { + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_in[1] >= 0) + close (fd_in[1]); + if (fd_out[0] >= 0) + close (fd_out[0]); + if (fd_out[1] >= 0 && fd_out[1] != 1) + close (fd_out[1]); + xfree (process); + xfree (argv); + return ec; + } + } + else if ((flags & GPGRT_PROCESS_STDERR_KEEP)) + { + fd_err[0] = -1; + fd_err[1] = 2; + } + else + { + fd_err[0] = -1; + fd_err[1] = -1; + } - *r_process_id = convert_from_pid (-1); _gpgrt_pre_syscall (); pid = fork (); _gpgrt_post_syscall (); if (pid == (pid_t)(-1)) { - err = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err)); - return err; + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (ec)); + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_in[1] >= 0) + close (fd_in[1]); + if (fd_out[0] >= 0) + close (fd_out[0]); + if (fd_out[1] >= 0 && fd_out[1] != 1) + close (fd_out[1]); + if (fd_err[0] >= 0) + close (fd_err[0]); + if (fd_err[1] >= 0 && fd_err[1] != 2) + close (fd_err[1]); + xfree (process); + xfree (argv); + return ec; } if (!pid) { - if (spawn_cb) - ask_inherit_fds = (*spawn_cb) (spawn_cb_arg); + struct spawn_cb_arg sca; + + if (fd_in[1] >= 0) + close (fd_in[1]); + if (fd_out[0] >= 0) + close (fd_out[0]); + if (fd_err[0] >= 0) + close (fd_err[0]); + + call_spawn_cb (&sca, fd_in[0], fd_out[1], fd_err[1], + spawn_cb, spawn_cb_arg); /* Run child. */ - do_exec (pgmname, argv, infd, outfd, errfd, NULL, - ask_inherit_fds? GPGRT_SPAWN_INHERIT_FILE : 0); + my_exec (pgmname, argv, &sca); /*NOTREACHED*/ } - *r_process_id = convert_from_pid (pid); + xfree (argv); + process->pid = pid; + + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_out[1] >= 0 && fd_out[1] != 1) + close (fd_out[1]); + if (fd_err[1] >= 0 && fd_err[1] != 2) + close (fd_err[1]); + process->fd_in = fd_in[1]; + process->fd_out = fd_out[0]; + process->fd_err = fd_err[0]; + process->wstatus = -1; + process->terminated = 0; + + if (r_process == NULL) + { + ec = _gpgrt_process_wait (process, 1); + _gpgrt_process_release (process); + return ec; + } + + *r_process = process; return 0; } - -/* Waiting for child processes. - * - * waitpid(2) may return information about terminated children that we - * did not yet request, and there is no portable way to wait for a - * specific set of children. - * - * As a workaround, we store the results of children for later use. - * - * XXX: This assumes that PIDs are not reused too quickly. - * FIXME: This is not thread-safe. - */ - -struct terminated_child -{ - pid_t pid; - int exitcode; - struct terminated_child *next; -}; - -static struct terminated_child *terminated_children; - - static gpg_err_code_t -store_result (pid_t pid, int exitcode) +process_kill (gpgrt_process_t process, int sig) { - struct terminated_child *c; - - c = xtrymalloc (sizeof *c); - if (c == NULL) - return _gpg_err_code_from_syserror (); - - c->pid = pid; - c->exitcode = exitcode; - c->next = terminated_children; - terminated_children = c; + gpg_err_code_t ec = 0; + pid_t pid = process->pid; - return 0; + _gpgrt_pre_syscall (); + if (kill (pid, sig) < 0) + ec = _gpg_err_code_from_syserror (); + _gpgrt_post_syscall (); + return ec; } - -static int -get_result (pid_t pid, int *r_exitcode) +gpg_err_code_t +_gpgrt_process_terminate (gpgrt_process_t process) { - struct terminated_child *c, **prevp; - - for (prevp = &terminated_children, c = terminated_children; - c; - prevp = &c->next, c = c->next) - if (c->pid == pid) - { - *prevp = c->next; - *r_exitcode = c->exitcode; - xfree (c); - return 1; - } - - return 0; + return process_kill (process, SIGTERM); } - -/* See gpgrt-int.h for a description. */ gpg_err_code_t -_gpgrt_wait_process (const char *pgmname, gpgrt_process_t process_id, int hang, - int *r_exitcode) +_gpgrt_process_get_fds (gpgrt_process_t process, unsigned int flags, + int *r_fd_in, int *r_fd_out, int *r_fd_err) { - gpg_err_code_t ec; - int i, status; - pid_t pid; - - pid = convert_from_process (process_id); - - if (r_exitcode) - *r_exitcode = -1; - - if (pid == (pid_t)(-1)) - return GPG_ERR_INV_VALUE; - - _gpgrt_pre_syscall (); - while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1) - && errno == EINTR); - _gpgrt_post_syscall (); - - if (i == (pid_t)(-1)) + (void)flags; + if (r_fd_in) { - ec = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("waiting for process %d to terminate failed: %s\n"), - (int)pid, _gpg_strerror (ec)); + *r_fd_in = process->fd_in; + process->fd_in = -1; } - else if (!i) + if (r_fd_out) { - ec = GPG_ERR_TIMEOUT; /* Still running. */ + *r_fd_out = process->fd_out; + process->fd_out = -1; } - else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) + if (r_fd_err) { - /* FIXME: This is GnuPG specific. */ - _gpgrt_log_error (_("error running '%s': probably not installed\n"), - pgmname); - ec = GPG_ERR_CONFIGURATION; + *r_fd_err = process->fd_err; + process->fd_err = -1; } - else if (WIFEXITED (status) && WEXITSTATUS (status)) + + return 0; +} + +gpg_err_code_t +_gpgrt_process_get_streams (gpgrt_process_t process, unsigned int flags, + gpgrt_stream_t *r_fp_in, gpgrt_stream_t *r_fp_out, + gpgrt_stream_t *r_fp_err) +{ + int nonblock = (flags & GPGRT_PROCESS_STREAM_NONBLOCK)? 1: 0; + + if (r_fp_in) { - if (!r_exitcode) - _gpgrt_log_error (_("error running '%s': exit status %d\n"), pgmname, - WEXITSTATUS (status)); - else - *r_exitcode = WEXITSTATUS (status); - ec = GPG_ERR_GENERAL; + *r_fp_in = _gpgrt_fdopen (process->fd_in, nonblock? "w,nonblock" : "w"); + process->fd_in = -1; } - else if (!WIFEXITED (status)) + if (r_fp_out) { - _gpgrt_log_error (_("error running '%s': terminated\n"), pgmname); - ec = GPG_ERR_GENERAL; + *r_fp_out = _gpgrt_fdopen (process->fd_out, nonblock? "r,nonblock" : "r"); + process->fd_out = -1; } - else + if (r_fp_err) { - if (r_exitcode) - *r_exitcode = 0; - ec = 0; + *r_fp_err = _gpgrt_fdopen (process->fd_err, nonblock? "r,nonblock" : "r"); + process->fd_err = -1; } - - return ec; + return 0; } - -/* See gpgrt-int.h for a description. - * - * FIXME: What about using a poll like data structure for the pids and - * their exit codes? The whole thing is anyway problematic in a - * threaded processs because waitpid has no association between PIDS - * and threads. - */ -gpg_err_code_t -_gpgrt_wait_processes (const char **pgmnames, gpgrt_process_t *process_ids, - size_t count, int hang, int *r_exitcodes) +static gpg_err_code_t +process_vctl (gpgrt_process_t process, unsigned int request, va_list arg_ptr) { - gpg_err_code_t ec = 0; - size_t i, left; - int *dummy = NULL; - pid_t pid; - - if (!r_exitcodes) + switch (request) { - dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count); - if (!dummy) - return _gpg_err_code_from_syserror (); - } + case GPGRT_PROCESS_NOP: + return 0; - for (i = 0, left = count; i < count; i++) - { - int status = -1; + case GPGRT_PROCESS_GET_PROC_ID: + { + int *r_id = va_arg (arg_ptr, int *); - pid = convert_from_process (process_ids[i]); + if (r_id == NULL) + return GPG_ERR_INV_VALUE; - /* Skip invalid PID. */ - if (pid == (pid_t)(-1)) - { - r_exitcodes[i] = -1; - left -= 1; - continue; - } + *r_id = (int)process->pid; + return 0; + } - /* See if there was a previously stored result for this pid. */ - if (get_result (pid, &status)) - left -= 1; + case GPGRT_PROCESS_GET_EXIT_ID: + { + int status = process->wstatus; + int *r_exit_status = va_arg (arg_ptr, int *); - r_exitcodes[i] = status; - } + if (!process->terminated) + return GPG_ERR_UNFINISHED; - while (left > 0) - { - pid_t pid0; - int status; + if (WIFEXITED (status)) + { + if (r_exit_status) + *r_exit_status = WEXITSTATUS (status); + } + else + *r_exit_status = -1; - _gpgrt_pre_syscall (); - while ((pid0 = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1) - && errno == EINTR); - _gpgrt_post_syscall (); + return 0; + } - if (pid0 == (pid_t)(-1)) - { - ec = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("waiting for processes to terminate" - " failed: %s\n"), _gpg_strerror (ec)); - break; - } - else if (!pid0) - { - ec = GPG_ERR_TIMEOUT; /* Still running. */ - break; - } - else - { - for (i = 0; i < count; i++) - { - pid = convert_from_process (process_ids[i]); - if (pid0 == pid) - break; - } + case GPGRT_PROCESS_GET_PID: + { + pid_t *r_pid = va_arg (arg_ptr, pid_t *); - if (i == count) - { - /* No match, store this result. */ - ec = store_result (pid0, status); - if (ec) - break; - continue; - } + if (r_pid == NULL) + return GPG_ERR_INV_VALUE; - /* Process PROCESS_PIDS[i] died. */ - if (r_exitcodes[i] != (pid_t) -1) - { - _gpgrt_log_error ("PID %d was reused", (int)pid0); - ec = GPG_ERR_GENERAL; - break; - } + *r_pid = process->pid; + return 0; + } - left -= 1; - r_exitcodes[i] = status; - } - } + case GPGRT_PROCESS_GET_WSTATUS: + { + int status = process->wstatus; + int *r_if_exited = va_arg (arg_ptr, int *); + int *r_if_signaled = va_arg (arg_ptr, int *); + int *r_exit_status = va_arg (arg_ptr, int *); + int *r_termsig = va_arg (arg_ptr, int *); - for (i = 0; i < count; i++) - { - if (r_exitcodes[i] == -1) - continue; + if (!process->terminated) + return GPG_ERR_UNFINISHED; - if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127) - { - _gpgrt_log_error (_("error running '%s': probably not installed\n"), - pgmnames[i]); - ec = GPG_ERR_CONFIGURATION; - } - else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i])) - { - if (dummy) - _gpgrt_log_error (_("error running '%s': exit status %d\n"), - pgmnames[i], WEXITSTATUS (r_exitcodes[i])); - else - r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]); - ec = GPG_ERR_GENERAL; - } - else if (!WIFEXITED (r_exitcodes[i])) - { - _gpgrt_log_error (_("error running '%s': terminated\n"), pgmnames[i]); - ec = GPG_ERR_GENERAL; - } + if (WIFEXITED (status)) + { + if (r_if_exited) + *r_if_exited = 1; + if (r_if_signaled) + *r_if_signaled = 0; + if (r_exit_status) + *r_exit_status = WEXITSTATUS (status); + if (r_termsig) + *r_termsig = 0; + } + else if (WIFSIGNALED (status)) + { + if (r_if_exited) + *r_if_exited = 0; + if (r_if_signaled) + *r_if_signaled = 1; + if (r_exit_status) + *r_exit_status = 0; + if (r_termsig) + *r_termsig = WTERMSIG (status); + } + + return 0; + } + + case GPGRT_PROCESS_KILL: + { + int sig = va_arg (arg_ptr, int); + + return process_kill (process, sig); + } + + default: + break; } - xfree (dummy); - return ec; + return GPG_ERR_UNKNOWN_COMMAND; } +gpg_err_code_t +_gpgrt_process_ctl (gpgrt_process_t process, unsigned int request, ...) +{ + va_list arg_ptr; + gpg_err_code_t ec; + + va_start (arg_ptr, request); + ec = process_vctl (process, request, arg_ptr); + va_end (arg_ptr); + return ec; +} -/* See gpgrt-int.h for a description. FIXME: We should add a prexec - * callback. */ gpg_err_code_t -_gpgrt_spawn_process_detached (const char *pgmname, const char *argv[], - const char *envp[]) +_gpgrt_process_wait (gpgrt_process_t process, int hang) { gpg_err_code_t ec; + int status; pid_t pid; - int i; - /* FIXME: Is this GnuPG specific or should we keep it. */ - if (getuid() != geteuid()) - return GPG_ERR_BUG; - - if (access (pgmname, X_OK)) - return _gpg_err_code_from_syserror (); + if (process->terminated) + /* Already terminated. */ + return 0; _gpgrt_pre_syscall (); - pid = fork (); + while ((pid = waitpid (process->pid, &status, hang? 0: WNOHANG)) + == (pid_t)(-1) && errno == EINTR); _gpgrt_post_syscall (); + if (pid == (pid_t)(-1)) { ec = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (ec)); - return ec; + _gpgrt_log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, _gpg_strerror (ec)); } - - if (!pid) + else if (!pid) { - pid_t pid2; - - /* gcry_control (GCRYCTL_TERM_SECMEM); */ - if (setsid() == -1 || chdir ("/")) - _exit (1); - - pid2 = fork (); /* Double fork to let init take over the new child. */ - if (pid2 == (pid_t)(-1)) - _exit (1); - if (pid2) - _exit (0); /* Let the parent exit immediately. */ - - for (i=0; envp && envp[i]; i++) - { - char *p = xtrystrdup (envp[i]); - if (!p) - out_of_core (__LINE__); - putenv (p); - } - - do_exec (pgmname, argv, -1, -1, -1, NULL, 0); - /*NOTREACHED*/ + ec = GPG_ERR_TIMEOUT; /* Still running. */ } - - _gpgrt_pre_syscall (); - if (waitpid (pid, NULL, 0) == -1) + else { - _gpgrt_post_syscall (); - ec = _gpg_err_code_from_syserror (); - _gpgrt_log_error ("waitpid failed in gpgrt_spawn_process_detached: %s", - _gpg_strerror (ec)); - return ec; + process->terminated = 1; + process->wstatus = status; + ec = 0; } - else - _gpgrt_post_syscall (); - return 0; + return ec; } - -/* Kill a process; that is send an appropriate signal to the process. - * gnupg_wait_process must be called to actually remove the process - from the system. An invalid PID is ignored. */ void -_gpgrt_kill_process (gpgrt_process_t process_id) +_gpgrt_process_release (gpgrt_process_t process) { - pid_t pid; + if (!process) + return; - pid = convert_from_process (process_id); - if (pid != (pid_t)(-1)) + if (process->terminated) { - _gpgrt_pre_syscall (); - kill (pid, SIGTERM); - _gpgrt_post_syscall (); + _gpgrt_process_terminate (process); + _gpgrt_process_wait (process, 1); } -} + xfree (process); +} -void -_gpgrt_release_process (gpgrt_process_t process_id) +gpg_err_code_t +_gpgrt_process_wait_list (gpgrt_process_t *process_list, int count, int hang) { - (void)process_id; + gpg_err_code_t ec = 0; + int i; + + for (i = 0; i < count; i++) + { + if (process_list[i]->terminated) + continue; + + ec = _gpgrt_process_wait (process_list[i], hang); + if (ec) + break; + } + + return ec; } diff --git a/src/spawn-w32.c b/src/spawn-w32.c index 324b5fe..b2eb760 100644 --- a/src/spawn-w32.c +++ b/src/spawn-w32.c @@ -42,6 +42,7 @@ #define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */ #include <windows.h> +#define NEED_STRUCT_SPAWN_CB_ARG #include "gpgrt-int.h" /* Define to 1 do enable debugging. */ @@ -86,70 +87,6 @@ get_max_fds (void) } -/* Under Windows this is a dummy function. */ -/* static void */ -/* close_all_fds (int first, int *except) */ -/* { */ -/* (void)first; */ -/* (void)except; */ -/* } */ - - -/* Returns an array with all currently open file descriptors. The end - * of the array is marked by -1. The caller needs to release this - * array using the *standard free* and not with xfree. This allow the - * use of this function right at startup even before libgcrypt has - * been initialized. Returns NULL on error and sets ERRNO - * accordingly. Note that fstat prints a warning to DebugView for all - * invalid fds which is a bit annoying. We actually do not need this - * function in real code (close_all_fds is a dummy anyway) but we keep - * it for use by t-exechelp.c. */ -#if 0 -int * -get_all_open_fds (void) -{ - int *array; - size_t narray; - int fd, max_fd, idx; -#ifndef HAVE_STAT - array = calloc (1, sizeof *array); - if (array) - array[0] = -1; -#else /*HAVE_STAT*/ - struct stat statbuf; - - max_fd = get_max_fds (); - narray = 32; /* If you change this change also t-exechelp.c. */ - array = calloc (narray, sizeof *array); - if (!array) - return NULL; - - /* Note: The list we return is ordered. */ - for (idx=0, fd=0; fd < max_fd; fd++) - if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) - { - if (idx+1 >= narray) - { - int *tmp; - - narray += (narray < 256)? 32:256; - tmp = realloc (array, narray * sizeof *array); - if (!tmp) - { - free (array); - return NULL; - } - array = tmp; - } - array[idx++] = fd; - } - array[idx] = -1; -#endif /*HAVE_STAT*/ - return array; -} -#endif - - /* Helper function to build_w32_commandline. */ static char * build_w32_commandline_copy (char *buffer, const char *string) @@ -221,9 +158,9 @@ build_w32_commandline (const char *pgmname, const char * const *argv, } -#define INHERIT_READ 1 -#define INHERIT_WRITE 2 -#define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE) +#define INHERIT_READ 1 +#define INHERIT_WRITE 2 +#define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE) /* Create pipe. FLAGS indicates which ends are inheritable. */ static int @@ -365,169 +302,407 @@ _gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction, int nonblock) } -/* - * UNION PROCESS_ID: - * - * gpgrt_process_t is an object which represents process handle. - * It must be same size as HANDLE and must have same bit pattern. - */ -union process { - gpgrt_process_t process_id; - HANDLE process_handle; +struct gpgrt_process { + const char *pgmname; + unsigned int terminated :1; /* or detached */ + unsigned int flags; + HANDLE hProcess; + HANDLE hd_in; + HANDLE hd_out; + HANDLE hd_err; + int exitcode; }; -static gpgrt_process_t -convert_from_handle (HANDLE process_handle) +/* + * Check if STARTUPINFOEXW supports PROC_THREAD_ATTRIBUTE_HANDLE_LIST. + */ +static int +check_windows_version (void) { - union process u; + static int is_vista_or_later = -1; + + OSVERSIONINFO osvi; - u.process_handle = process_handle; - return u.process_id; + if (is_vista_or_later == -1) + { + memset (&osvi,0,sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + GetVersionEx (&osvi); + + /* The feature is available on Vista or later. */ + is_vista_or_later = (osvi.dwMajorVersion >= 6); + } + + return is_vista_or_later; } -static HANDLE -convert_from_process (gpgrt_process_t process_id) + +static gpg_err_code_t +spawn_detached (const char *pgmname, char *cmdline, + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) { - union process u; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFOEXW si; + int cr_flags; + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; + gpg_err_code_t ec; + int ret; + struct spawn_cb_arg sca; + BOOL ask_inherit = FALSE; + + ec = _gpgrt_access (pgmname, X_OK); + if (ec) + { + xfree (cmdline); + return ec; + } + + memset (&si, 0, sizeof si); + + sca.allow_foreground_window = FALSE; + sca.hd[0] = INVALID_HANDLE_VALUE; + sca.hd[1] = INVALID_HANDLE_VALUE; + sca.hd[2] = INVALID_HANDLE_VALUE; + sca.inherit_hds = NULL; + sca.arg = spawn_cb_arg; + if (spawn_cb) + (*spawn_cb) (&sca); + + if (sca.inherit_hds) + { + SIZE_T attr_list_size = 0; + HANDLE hd[16]; + HANDLE *hd_p = sca.inherit_hds; + int j = 0; - u.process_id = process_id; - return u.process_handle; + if (hd_p) + { + while (*hd_p != INVALID_HANDLE_VALUE) + if (j < DIM (hd)) + hd[j++] = *hd_p++; + else + { + _gpgrt_log_error ("Too much handles\n"); + break; + } + } + + if (j) + { + if (check_windows_version ()) + { + InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size); + si.lpAttributeList = xtrymalloc (attr_list_size); + if (si.lpAttributeList == NULL) + { + xfree (cmdline); + return _gpg_err_code_from_syserror (); + } + InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0, + &attr_list_size); + UpdateProcThreadAttribute (si.lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + hd, sizeof (HANDLE) * j, NULL, NULL); + } + + ask_inherit = TRUE; + } + } + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Start the process. */ + si.StartupInfo.cb = sizeof (si); + si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_NEW_PROCESS_GROUP + | DETACHED_PROCESS); + + /* Take care: CreateProcessW may modify wpgmname */ + if (!(wpgmname = _gpgrt_utf8_to_wchar (pgmname))) + ret = 0; + else if (!(wcmdline = _gpgrt_utf8_to_wchar (cmdline))) + ret = 0; + else + ret = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + ask_inherit, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + (STARTUPINFOW *)&si, /* Startup information. */ + &pi /* Returns process information. */ + ); + if (!ret) + { + if (!wpgmname || !wcmdline) + _gpgrt_log_error ("CreateProcess failed (utf8_to_wchar): %s\n", + strerror (errno)); + else + _gpgrt_log_error ("CreateProcess(detached) failed: %d\n", + (int)GetLastError ()); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + return GPG_ERR_GENERAL; + } + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + + /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + /* Note: AllowSetForegroundWindow doesn't make sense for background + process. */ + + CloseHandle (pi.hThread); + CloseHandle (pi.hProcess); + return 0; } -/* Fork and exec the PGMNAME, see gpgrt-int.h for details. */ +void +_gpgrt_spawn_helper (struct spawn_cb_arg *sca) +{ + HANDLE *user_except = sca->arg; + sca->inherit_hds = user_except; +} + gpg_err_code_t -_gpgrt_spawn_process (const char *pgmname, const char *argv[], - int *except, unsigned int flags, - estream_t *r_infp, estream_t *r_outfp, estream_t *r_errfp, - gpgrt_process_t *r_process_id) +_gpgrt_process_spawn (const char *pgmname, const char *argv[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gpgrt_process_t *r_process) { - gpg_err_code_t err; + gpg_err_code_t ec; + gpgrt_process_t process; SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFO si; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFOEXW si; int cr_flags; char *cmdline; - HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - estream_t infp = NULL; - estream_t outfp = NULL; - estream_t errfp = NULL; - HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, - INVALID_HANDLE_VALUE, - INVALID_HANDLE_VALUE}; - int i; - es_syshd_t syshd; - int nonblock = !!(flags & GPGRT_SPAWN_NONBLOCK); + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; int ret; + HANDLE hd_in[2]; + HANDLE hd_out[2]; + HANDLE hd_err[2]; + struct spawn_cb_arg sca; + int i; + BOOL ask_inherit = FALSE; - (void)except; /* Not yet used. */ - - if (r_infp) - *r_infp = NULL; - if (r_outfp) - *r_outfp = NULL; - if (r_errfp) - *r_errfp = NULL; - *r_process_id = convert_from_handle (INVALID_HANDLE_VALUE); + /* Build the command line. */ + ec = build_w32_commandline (pgmname, argv, &cmdline); + if (ec) + return ec; - if (r_infp) + if ((flags & GPGRT_PROCESS_DETACHED)) { - if (create_inheritable_pipe (inpipe, INHERIT_READ)) + if ((flags & GPGRT_PROCESS_STDFDS_SETTING)) { - err = GPG_ERR_GENERAL; - _gpgrt_log_error (_("error creating a pipe: %s\n"), - _gpg_strerror (err)); - return err; + xfree (cmdline); + return GPG_ERR_INV_FLAG; } - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = inpipe[1]; - infp = _gpgrt_sysopen (&syshd, nonblock? "w,nonblock" : "w"); - if (!infp) + /* In detached case, it must be no R_PROCESS. */ + if (r_process) { - err = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("error creating a stream for a pipe: %s\n"), - _gpg_strerror (err)); - CloseHandle (inpipe[0]); - CloseHandle (inpipe[1]); - inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE; - return err; + xfree (cmdline); + return GPG_ERR_INV_ARG; } + + return spawn_detached (pgmname, cmdline, spawn_cb, spawn_cb_arg); } - if (r_outfp) + if (r_process) + *r_process = NULL; + + process = xtrymalloc (sizeof (struct gpgrt_process)); + if (process == NULL) + { + xfree (cmdline); + return _gpg_err_code_from_syserror (); + } + + process->pgmname = pgmname; + process->flags = flags; + + if ((flags & GPGRT_PROCESS_STDINOUT_SOCKETPAIR)) { - if (create_inheritable_pipe (outpipe, INHERIT_WRITE)) + xfree (process); + xfree (cmdline); + return GPG_ERR_NOT_SUPPORTED; + } + + if ((flags & GPGRT_PROCESS_STDIN_PIPE)) + { + ec = create_inheritable_pipe (hd_in, INHERIT_READ); + if (ec) { - err = GPG_ERR_GENERAL; - _gpgrt_log_error (_("error creating a pipe: %s\n"), - _gpg_strerror (err)); - return err; + xfree (process); + xfree (cmdline); + return ec; } + } + else if ((flags & GPGRT_PROCESS_STDIN_KEEP)) + { + hd_in[0] = GetStdHandle (STD_INPUT_HANDLE); + hd_in[1] = INVALID_HANDLE_VALUE; + } + else + { + hd_in[0] = w32_open_null (0); + hd_in[1] = INVALID_HANDLE_VALUE; + } - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = outpipe[0]; - outfp = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r"); - if (!outfp) + if ((flags & GPGRT_PROCESS_STDOUT_PIPE)) + { + ec = create_inheritable_pipe (hd_out, INHERIT_WRITE); + if (ec) { - err = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("error creating a stream for a pipe: %s\n"), - _gpg_strerror (err)); - CloseHandle (outpipe[0]); - CloseHandle (outpipe[1]); - outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; - if (infp) - _gpgrt_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - return err; + if (hd_in[0] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[0]); + if (hd_in[1] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[1]); + xfree (process); + xfree (cmdline); + return ec; } } + else if ((flags & GPGRT_PROCESS_STDOUT_KEEP)) + { + hd_out[0] = INVALID_HANDLE_VALUE; + hd_out[1] = GetStdHandle (STD_OUTPUT_HANDLE); + } + else + { + hd_out[0] = INVALID_HANDLE_VALUE; + hd_out[1] = w32_open_null (1); + } - if (r_errfp) + if ((flags & GPGRT_PROCESS_STDERR_PIPE)) { - if (create_inheritable_pipe (errpipe, INHERIT_WRITE)) + ec = create_inheritable_pipe (hd_err, INHERIT_WRITE); + if (ec) { - err = GPG_ERR_GENERAL; - _gpgrt_log_error (_("error creating a pipe: %s\n"), - _gpg_strerror (err)); - return err; + if (hd_in[0] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[0]); + if (hd_in[1] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[1]); + if (hd_out[0] != INVALID_HANDLE_VALUE) + CloseHandle (hd_out[0]); + if (hd_out[1] != INVALID_HANDLE_VALUE) + CloseHandle (hd_out[1]); + xfree (process); + xfree (cmdline); + return ec; } + } + else if ((flags & GPGRT_PROCESS_STDERR_KEEP)) + { + hd_err[0] = INVALID_HANDLE_VALUE; + hd_err[1] = GetStdHandle (STD_ERROR_HANDLE); + } + else + { + hd_err[0] = INVALID_HANDLE_VALUE; + hd_err[1] = w32_open_null (1); + } - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = errpipe[0]; - errfp = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r"); - if (!errfp) + memset (&si, 0, sizeof si); + + sca.allow_foreground_window = FALSE; + sca.hd[0] = hd_in[0]; + sca.hd[1] = hd_out[1]; + sca.hd[2] = hd_err[1]; + sca.inherit_hds = NULL; + sca.arg = spawn_cb_arg; + if (spawn_cb) + (*spawn_cb) (&sca); + + i = 0; + if (sca.hd[0] != INVALID_HANDLE_VALUE) + i++; + if (sca.hd[1] != INVALID_HANDLE_VALUE) + i++; + if (sca.hd[2] != INVALID_HANDLE_VALUE) + i++; + + if (i != 0 || sca.inherit_hds) + { + SIZE_T attr_list_size = 0; + HANDLE hd[16]; + HANDLE *hd_p = sca.inherit_hds; + int j = 0; + + if (sca.hd[0] != INVALID_HANDLE_VALUE) + hd[j++] = sca.hd[0]; + if (sca.hd[1] != INVALID_HANDLE_VALUE) + hd[j++] = sca.hd[1]; + if (sca.hd[1] != INVALID_HANDLE_VALUE) + hd[j++] = sca.hd[2]; + if (hd_p) { - err = _gpg_err_code_from_syserror (); - _gpgrt_log_error (_("error creating a stream for a pipe: %s\n"), - _gpg_strerror (err)); - CloseHandle (errpipe[0]); - CloseHandle (errpipe[1]); - errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; - if (outfp) - _gpgrt_fclose (outfp); - else if (outpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (infp) - _gpgrt_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - return err; + while (*hd_p != INVALID_HANDLE_VALUE) + if (j < DIM (hd)) + hd[j++] = *hd_p++; + else + { + _gpgrt_log_error ("Too much handles\n"); + break; + } + } + + if (j) + { + if (check_windows_version ()) + { + InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size); + si.lpAttributeList = xtrymalloc (attr_list_size); + if (si.lpAttributeList == NULL) + { + if ((flags & GPGRT_PROCESS_STDIN_PIPE) + || !(flags & GPGRT_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GPGRT_PROCESS_STDIN_PIPE)) + CloseHandle (hd_in[1]); + if ((flags & GPGRT_PROCESS_STDOUT_PIPE)) + CloseHandle (hd_out[0]); + if ((flags & GPGRT_PROCESS_STDOUT_PIPE) + || !(flags & GPGRT_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GPGRT_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GPGRT_PROCESS_STDERR_PIPE) + || !(flags & GPGRT_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + xfree (process); + xfree (cmdline); + return _gpg_err_code_from_syserror (); + } + InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0, + &attr_list_size); + UpdateProcThreadAttribute (si.lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + hd, sizeof (HANDLE) * j, NULL, NULL); + } + ask_inherit = TRUE; } } @@ -536,93 +711,86 @@ _gpgrt_spawn_process (const char *pgmname, const char *argv[], sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; - /* Build the command line. */ - err = build_w32_commandline (pgmname, argv, &cmdline); - if (err) - return err; - - if (inpipe[0] == INVALID_HANDLE_VALUE) - nullhd[0] = ((flags & GPGRT_SPAWN_KEEP_STDIN)? - GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0)); - if (outpipe[1] == INVALID_HANDLE_VALUE) - nullhd[1] = ((flags & GPGRT_SPAWN_KEEP_STDOUT)? - GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1)); - if (errpipe[1] == INVALID_HANDLE_VALUE) - nullhd[2] = ((flags & GPGRT_SPAWN_KEEP_STDOUT)? - GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1)); - - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; - si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0]; - si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; - si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; - + /* Start the process. */ + si.StartupInfo.cb = sizeof (si); + si.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE; + si.StartupInfo.hStdInput = sca.hd[0]; + si.StartupInfo.hStdOutput = sca.hd[1]; + si.StartupInfo.hStdError = sca.hd[2]; + + /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ cr_flags = (CREATE_DEFAULT_ERROR_MODE - | ((flags & GPGRT_SPAWN_DETACHED)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED); - _gpgrt_log_debug ("CreateProcess, path='%s' cmdline='%s'\n", - pgmname, cmdline); - ret = CreateProcess (pgmname, /* Program to start. */ - cmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - TRUE, /* Inherit handles. */ - cr_flags, /* Creation flags. */ - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - ); + if (!(wpgmname = _gpgrt_utf8_to_wchar (pgmname))) + ret = 0; + else if (!(wcmdline = _gpgrt_utf8_to_wchar (cmdline))) + ret = 0; + else + ret = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + ask_inherit, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + (STARTUPINFOW *)&si, /* Startup information. */ + &pi /* Returns process information. */ + ); if (!ret) { - _gpgrt_log_error ("CreateProcess failed: ec=%d\n", (int)GetLastError ()); + if (!wpgmname || !wcmdline) + _gpgrt_log_error ("CreateProcess failed (utf8_to_wchar): %s\n", + strerror (errno)); + else + _gpgrt_log_error ("CreateProcess failed: ec=%d\n", + (int)GetLastError ()); + if ((flags & GPGRT_PROCESS_STDIN_PIPE) + || !(flags & GPGRT_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GPGRT_PROCESS_STDIN_PIPE)) + CloseHandle (hd_in[1]); + if ((flags & GPGRT_PROCESS_STDOUT_PIPE)) + CloseHandle (hd_out[0]); + if ((flags & GPGRT_PROCESS_STDOUT_PIPE) + || !(flags & GPGRT_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GPGRT_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GPGRT_PROCESS_STDERR_PIPE) + || !(flags & GPGRT_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + xfree (wpgmname); + xfree (wcmdline); + xfree (process); xfree (cmdline); - if (infp) - _gpgrt_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - if (outfp) - _gpgrt_fclose (outfp); - else if (outpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (errfp) - _gpgrt_fclose (errfp); - else if (errpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[0]); - if (errpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[1]); return GPG_ERR_GENERAL; } - xfree (cmdline); - cmdline = NULL; - - /* Close the inherited handles to /dev/null. */ - for (i=0; i < DIM (nullhd); i++) - if (nullhd[i] != INVALID_HANDLE_VALUE) - CloseHandle (nullhd[i]); - /* Close the inherited ends of the pipes. */ - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (errpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[1]); - - _gpgrt_log_debug ("CreateProcess ready: hProcess=%p hThread=%p" - " dwProcessID=%d dwThreadId=%d\n", - pi.hProcess, pi.hThread, - (int) pi.dwProcessId, (int) pi.dwThreadId); - _gpgrt_log_debug (" outfp=%p errfp=%p\n", outfp, errfp); + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); - if ((flags & GPGRT_SPAWN_RUN_ASFW)) + if ((flags & GPGRT_PROCESS_STDIN_PIPE) + || !(flags & GPGRT_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GPGRT_PROCESS_STDOUT_PIPE) + || !(flags & GPGRT_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GPGRT_PROCESS_STDERR_PIPE) + || !(flags & GPGRT_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + + /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + if (sca.allow_foreground_window) { /* Fixme: For unknown reasons AllowSetForegroundWindow returns * an invalid argument error if we pass it the correct @@ -638,316 +806,292 @@ _gpgrt_spawn_process (const char *pgmname, const char *argv[], CloseHandle (pi.hThread); _gpgrt_post_syscall (); - if (r_infp) - *r_infp = infp; - if (r_outfp) - *r_outfp = outfp; - if (r_errfp) - *r_errfp = errfp; + process->hProcess = pi.hProcess; + process->hd_in = hd_in[1]; + process->hd_out = hd_out[0]; + process->hd_err = hd_err[0]; + process->exitcode = -1; + process->terminated = 0; + + if (r_process == NULL) + { + ec = _gpgrt_process_wait (process, 1); + _gpgrt_process_release (process); + return ec; + } - *r_process_id = convert_from_handle (pi.hProcess); + *r_process = process; return 0; } - -/* Fork and exec the PGMNAME using FDs, see gpgrt-int.h for details. */ gpg_err_code_t -_gpgrt_spawn_process_fd (const char *pgmname, const char *argv[], - int infd, int outfd, int errfd, - int (*spawn_cb) (void *), - void *spawn_cb_arg, - gpgrt_process_t *r_process_id) +_gpgrt_process_get_fds (gpgrt_process_t process, unsigned int flags, + int *r_fd_in, int *r_fd_out, int *r_fd_err) { - gpg_err_code_t err; - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; - STARTUPINFO si; - char *cmdline; - int ret, i; - HANDLE stdhd[3]; - int ask_inherit = 0; + (void)flags; + if (r_fd_in) + { + *r_fd_in = _open_osfhandle ((intptr_t)process->hd_in, O_APPEND); + process->hd_in = INVALID_HANDLE_VALUE; + } + if (r_fd_out) + { + *r_fd_out = _open_osfhandle ((intptr_t)process->hd_out, O_RDONLY); + process->hd_out = INVALID_HANDLE_VALUE; + } + if (r_fd_err) + { + *r_fd_err = _open_osfhandle ((intptr_t)process->hd_err, O_RDONLY); + process->hd_err = INVALID_HANDLE_VALUE; + } - if (spawn_cb) - ask_inherit = (*spawn_cb) (spawn_cb_arg); + return 0; +} - /* Setup return values. */ - *r_process_id = convert_from_handle (INVALID_HANDLE_VALUE); +gpg_err_code_t +_gpgrt_process_get_streams (gpgrt_process_t process, unsigned int flags, + estream_t *r_fp_in, estream_t *r_fp_out, + estream_t *r_fp_err) +{ + int nonblock = (flags & GPGRT_PROCESS_STREAM_NONBLOCK)? 1: 0; + es_syshd_t syshd; - /* Prepare security attributes. */ - memset (&sec_attr, 0, sizeof sec_attr ); - sec_attr.nLength = sizeof sec_attr; + syshd.type = ES_SYSHD_HANDLE; + if (r_fp_in) + { + syshd.u.handle = process->hd_in; + *r_fp_in = _gpgrt_sysopen (&syshd, nonblock? "w,nonblock" : "w"); + process->hd_in = INVALID_HANDLE_VALUE; + } + if (r_fp_out) + { + syshd.u.handle = process->hd_out; + *r_fp_out = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + process->hd_out = INVALID_HANDLE_VALUE; + } + if (r_fp_err) + { + syshd.u.handle = process->hd_err; + *r_fp_err = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + process->hd_err = INVALID_HANDLE_VALUE; + } + return 0; +} - if (ask_inherit) - sec_attr.bInheritHandle = TRUE; - else - sec_attr.bInheritHandle = FALSE; +static gpg_err_code_t +process_kill (gpgrt_process_t process, unsigned int exitcode) +{ + gpg_err_code_t ec = 0; - /* Build the command line. */ - err = build_w32_commandline (pgmname, argv, &cmdline); - if (err) - return err; + _gpgrt_pre_syscall (); + if (TerminateProcess (process->hProcess, exitcode)) + ec = _gpg_err_code_from_syserror (); + _gpgrt_post_syscall (); + return ec; +} - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; - stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; - stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; - stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; - si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd); - si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd); - si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd); - - _gpgrt_log_debug ("CreateProcess, path='%s' cmdline='%s'\n", - pgmname, cmdline); - ret = CreateProcess (pgmname, /* Program to start. */ - cmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - TRUE, /* Inherit handles. */ - (CREATE_DEFAULT_ERROR_MODE - | GetPriorityClass (GetCurrentProcess ()) - | CREATE_SUSPENDED | DETACHED_PROCESS), - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - ); - if (!ret) +static gpg_err_code_t +process_vctl (gpgrt_process_t process, unsigned int request, va_list arg_ptr) +{ + switch (request) { - _gpgrt_log_error ("CreateProcess failed: ec=%d\n", (int)GetLastError ()); - err = GPG_ERR_GENERAL; - } - else - err = 0; + case GPGRT_PROCESS_NOP: + return 0; - xfree (cmdline); + case GPGRT_PROCESS_GET_PROC_ID: + { + int *r_id = va_arg (arg_ptr, int *); - for (i=0; i < 3; i++) - if (stdhd[i] != INVALID_HANDLE_VALUE) - CloseHandle (stdhd[i]); + if (r_id == NULL) + return GPG_ERR_INV_VALUE; - if (err) - return err; + *r_id = (int)GetProcessId (process->hProcess); + return 0; + } - _gpgrt_log_debug ("CreateProcess ready: hProcess=%p hThread=%p" - " dwProcessID=%d dwThreadId=%d\n", - pi.hProcess, pi.hThread, - (int) pi.dwProcessId, (int) pi.dwThreadId); + case GPGRT_PROCESS_GET_EXIT_ID: + { + int *r_exit_status = va_arg (arg_ptr, int *); + unsigned long exit_code; - /* Process has been created suspended; resume it now. */ - ResumeThread (pi.hThread); - CloseHandle (pi.hThread); + *r_exit_status = -1; - *r_process_id = convert_from_handle (pi.hProcess); - return 0; -} + if (!process->terminated) + return GPG_ERR_UNFINISHED; + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; -/* See gpgrt-int.h for a description. */ -gpg_err_code_t -_gpgrt_wait_process (const char *pgmname, gpgrt_process_t process_id, - int hang, int *r_exitcode) -{ - return _gpgrt_wait_processes (&pgmname, &process_id, 1, hang, r_exitcode); -} + if (GetExitCodeProcess (process->hProcess, &exit_code) == 0) + return _gpg_err_code_from_syserror (); + *r_exit_status = (int)exit_code; + return 0; + } -/* See gpgrt-int.h for a description. */ -gpg_err_code_t -_gpgrt_wait_processes (const char **pgmnames, gpgrt_process_t *process_ids, - size_t count, int hang, int *r_exitcodes) -{ - gpg_err_code_t ec = 0; - size_t i; - HANDLE *procs; - int code; + case GPGRT_PROCESS_GET_P_HANDLE: + { + HANDLE *r_hProcess = va_arg (arg_ptr, HANDLE *); - procs = xtrycalloc (count, sizeof *procs); - if (procs == NULL) - return _gpg_err_code_from_syserror (); + if (r_hProcess == NULL) + return GPG_ERR_INV_VALUE; - for (i = 0; i < count; i++) - { - HANDLE process_handle = convert_from_process (process_ids[i]); + *r_hProcess = process->hProcess; + process->hProcess = INVALID_HANDLE_VALUE; + return 0; + } - if (r_exitcodes) - r_exitcodes[i] = -1; + case GPGRT_PROCESS_GET_HANDLES: + { + HANDLE *r_hd_in = va_arg (arg_ptr, HANDLE *); + HANDLE *r_hd_out = va_arg (arg_ptr, HANDLE *); + HANDLE *r_hd_err = va_arg (arg_ptr, HANDLE *); - if (process_handle == INVALID_HANDLE_VALUE) - return GPG_ERR_INV_VALUE; + if (r_hd_in) + { + *r_hd_in = process->hd_in; + process->hd_in = INVALID_HANDLE_VALUE; + } + if (r_hd_out) + { + *r_hd_out = process->hd_out; + process->hd_out = INVALID_HANDLE_VALUE; + } + if (r_hd_err) + { + *r_hd_err = process->hd_err; + process->hd_err = INVALID_HANDLE_VALUE; + } + return 0; + } + + case GPGRT_PROCESS_GET_EXIT_CODE: + { + unsigned long *r_exitcode = va_arg (arg_ptr, unsigned long *); + + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + if (process->hProcess == INVALID_HANDLE_VALUE) + { + *r_exitcode = (unsigned long)-1; + return 0; + } + + if (GetExitCodeProcess (process->hProcess, r_exitcode) == 0) + return _gpg_err_code_from_syserror (); + return 0; + } + + case GPGRT_PROCESS_KILL_WITH_EC: + { + unsigned int exitcode = va_arg (arg_ptr, unsigned int); + + if (process->terminated) + return 0; + + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; + + return process_kill (process, exitcode); + } - procs[i] = process_handle; + default: + break; } + return GPG_ERR_UNKNOWN_COMMAND; +} + +gpg_err_code_t +_gpgrt_process_ctl (gpgrt_process_t process, unsigned int request, ...) +{ + va_list arg_ptr; + gpg_err_code_t ec; + + va_start (arg_ptr, request); + ec = process_vctl (process, request, arg_ptr); + va_end (arg_ptr); + return ec; +} + +gpg_err_code_t +_gpgrt_process_wait (gpgrt_process_t process, int hang) +{ + gpg_err_code_t ec; + int code; + + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; + _gpgrt_pre_syscall (); - code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0); + code = WaitForSingleObject (process->hProcess, hang? INFINITE : 0); _gpgrt_post_syscall (); + switch (code) { case WAIT_TIMEOUT: - ec = GPG_ERR_TIMEOUT; - goto leave; + ec = GPG_ERR_TIMEOUT; /* Still running. */ + break; case WAIT_FAILED: - _gpgrt_log_error (_("waiting for processes to terminate failed: ec=%d\n"), + _gpgrt_log_error (_("waiting for process to terminate failed: ec=%d\n"), (int)GetLastError ()); ec = GPG_ERR_GENERAL; - goto leave; + break; case WAIT_OBJECT_0: - for (i = 0; i < count; i++) - { - DWORD exc; - - if (! GetExitCodeProcess (procs[i], &exc)) - { - _gpgrt_log_error (_("error getting exit code of process %p:" - " ec=%d\n"), - process_ids[i], (int)GetLastError ()); - ec = GPG_ERR_GENERAL; - } - else if (exc) - { - if (!r_exitcodes) - _gpgrt_log_error (_("error running '%s': exit status %d\n"), - pgmnames[i], (int)exc); - else - r_exitcodes[i] = (int)exc; - ec = GPG_ERR_GENERAL; - } - else - { - if (r_exitcodes) - r_exitcodes[i] = 0; - } - } + process->terminated = 1; + ec = 0; break; default: - _gpgrt_log_debug ("WaitForMultipleObjects returned unexpected code %d\n", + _gpgrt_log_debug ("WaitForSingleObject returned unexpected code %d\n", code); ec = GPG_ERR_GENERAL; break; } - leave: return ec; } - -/* See gpgrt-int.h for a description. */ gpg_err_code_t -_gpgrt_spawn_process_detached (const char *pgmname, const char *argv[], - const char *envp[]) +_gpgrt_process_terminate (gpgrt_process_t process) { - gpg_err_code_t err; - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFO si; - int cr_flags; - char *cmdline; - int ret; - gpg_err_code_t ec; - - /* We don't use ENVP. */ - (void)envp; - - ec = _gpgrt_access (pgmname, X_OK); - if (ec) - return ec; - - /* Prepare security attributes. */ - memset (&sec_attr, 0, sizeof sec_attr ); - sec_attr.nLength = sizeof sec_attr; - sec_attr.bInheritHandle = FALSE; - - /* Build the command line. */ - err = build_w32_commandline (pgmname, argv, &cmdline); - if (err) - return err; - - /* Start the process. */ - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; - - cr_flags = (CREATE_DEFAULT_ERROR_MODE - | GetPriorityClass (GetCurrentProcess ()) - | CREATE_NEW_PROCESS_GROUP - | DETACHED_PROCESS); - _gpgrt_log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", - pgmname, cmdline); - ret = CreateProcess (pgmname, /* Program to start. */ - cmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - FALSE, /* Inherit handles. */ - cr_flags, /* Creation flags. */ - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - ); - if (!ret) - { - _gpgrt_log_error ("CreateProcess(detached) failed: ec=%d\n", - (int)GetLastError ()); - xfree (cmdline); - return GPG_ERR_GENERAL; - } - xfree (cmdline); - cmdline = NULL; - - _gpgrt_log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" - " dwProcessID=%d dwThreadId=%d\n", - pi.hProcess, pi.hThread, - (int) pi.dwProcessId, (int) pi.dwThreadId); - - CloseHandle (pi.hThread); - CloseHandle (pi.hProcess); - - return 0; + return process_kill (process, 1); } - -/* Kill a process; that is send an appropriate signal to the process. - gnupg_wait_process must be called to actually remove the process - from the system. An invalid PID is ignored. */ void -_gpgrt_kill_process (gpgrt_process_t process_id) +_gpgrt_process_release (gpgrt_process_t process) { - HANDLE process_handle = convert_from_process (process_id); + if (!process) + return; - if (process_handle != INVALID_HANDLE_VALUE) + if (process->terminated) { - /* Arbitrary error code. */ - _gpgrt_pre_syscall (); - TerminateProcess (process_handle, 1); - _gpgrt_post_syscall (); + _gpgrt_process_terminate (process); + _gpgrt_process_wait (process, 1); } -} + CloseHandle (process->hProcess); + xfree (process); +} -void -_gpgrt_release_process (gpgrt_process_t process_id) +gpg_err_code_t +_gpgrt_process_wait_list (gpgrt_process_t *process_list, int count, int hang) { - HANDLE process_handle = convert_from_process (process_id); + gpg_err_code_t ec = 0; + int i; - if (process_handle != INVALID_HANDLE_VALUE) - CloseHandle (process_handle); -} + for (i = 0; i < count; i++) + { + if (process_list[i]->terminated) + continue; -void -_gpgrt_close_all_fds (int from, int *keep_fds) -{ - (void)from; - (void)keep_fds; + ec = _gpgrt_process_wait (process_list[i], hang); + if (ec) + break; + } + + return ec; } |