From a035938216c39230e1476925119d3cff76932e7e Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 11 May 2023 19:18:21 +0900 Subject: [PATCH] common,agent,gpg,dirmngr,g13,scd,tests,tools: New spawn function. * common/exechelp-posix.c (do_exec, gnupg_spawn_process): Remove. (check_syscall_func, pre_syscall, post_syscall) : New. (do_create_socketpair, posix_open_null, call_spawn_cb): New. (my_exec, spawn_detached, gnupg_spawn_helper): New. (gnupg_process_spawn, process_kill, gnupg_process_terminate): New. (gnupg_process_get_fds, gnupg_process_get_streams): New. (process_vctl, gnupg_process_ctl): New. (gnupg_process_wait, gnupg_process_release): New. (gnupg_process_wait_list): New. * common/exechelp-w32.c: Add definition of _WIN32_WINNT as 0x600. (check_syscall_func, pre_syscall, post_syscall): New. (gnupg_spawn_process): Remove. (check_windows_version): New. (spawn_detached, gnupg_spawn_helper, gnupg_process_spawn): New. (gnupg_process_get_fds, gnupg_process_get_streams): New. (process_kill, process_vctl, gnupg_process_ctl): New. (gnupg_process_wait, gnupg_process_terminate): New. (gnupg_process_release, gnupg_process_wait_list): New. * common/exechelp.h: Re-write for new API. * common/exectool.c (gnupg_exec_tool_stream): Follow the change. * common/asshelp.c (start_new_service): Likewise. * agent/genkey.c (do_check_passphrase_pattern): Likewise. * dirmngr/ldap-wrapper.c (struct wrapper_context_s): Use PROC. (destroy_wrapper): Follow the change of API. (read_log_data): Follow the change of API, use printable_pid. (ldap_reaper_thread, ldap_wrapper_release_context): Likewise. (ldap_wrapper_connection_cleanup, ldap_wrapper): Likewise. * g10/photoid.c (run_with_pipe): Follow the change of API. (show_photo): Likewise. * g13/be-encfs.c (run_umount_helper): Likewise. (run_encfs_tool): Likewise. * g13/g13.c: Add including ./common/exechelp.h. * g13/mount.c: Likewise. * g13/runner.c: Follow the change of API. * g13/runner.h: Follow the change of API. * scd/app.c (setup_env): New. (report_change): Follow the change of API. * tests/gpgscm/ffi.c (proc_object_finalize): New. (proc_object_to_string): New. (proc_wrap, proc_unwrap): New. (do_spawn_process): Remove. (do_process_spawn): New. (setup_std_fds): New. (do_spawn_process_fd): Remove. (do_process_spawn_fd): New. (do_wait_process): Remove. (do_process_wait): New. (do_wait_processes): Remove. * tests/gpgscm/t-child.scm: Follow the change of API. * tests/gpgscm/tests.scm: Likewise. * tests/openpgp/defs.scm: Likewise. * tests/tpm2dtests/defs.scm: Likewise. * tools/gpg-card.c: Likewise. * tools/gpgconf-comp.c: Likewise. * tools/gpgconf.c: Likewise. * tools/gpgtar-create.c: Likewise. * tools/gpgtar-extract.c: Likewise. * tools/gpgtar-list.c: Likewise. -- GnuPG-bug-id: 6275 Signed-off-by: NIIBE Yutaka --- agent/genkey.c | 18 +- common/asshelp.c | 14 +- common/exechelp-posix.c | 1109 ++++++++++++++++-------------- common/exechelp-w32.c | 1342 +++++++++++++++++++++---------------- common/exechelp.h | 201 +++--- common/exectool.c | 40 +- dirmngr/ldap-wrapper.c | 79 ++- g10/photoid.c | 42 +- g13/be-encfs.c | 63 +- g13/g13.c | 1 + g13/mount.c | 3 +- g13/runner.c | 36 +- g13/runner.h | 2 +- scd/app.c | 21 +- tests/gpgscm/ffi.c | 222 +++--- tests/gpgscm/t-child.scm | 39 +- tests/gpgscm/tests.scm | 66 +- tests/openpgp/defs.scm | 7 +- tests/tpm2dtests/defs.scm | 7 +- tools/gpg-card.c | 16 +- tools/gpgconf-comp.c | 115 ++-- tools/gpgconf.c | 18 +- tools/gpgtar-create.c | 33 +- tools/gpgtar-extract.c | 30 +- tools/gpgtar-list.c | 33 +- 25 files changed, 1936 insertions(+), 1621 deletions(-) diff --git a/agent/genkey.c b/agent/genkey.c index 7660443ca..563407253 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -99,7 +99,7 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN); estream_t stream_to_check_pattern = NULL; const char *argv[10]; - pid_t pid; + gnupg_process_t proc; int result, i; const char *pattern; char *patternfname; @@ -142,11 +142,17 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) argv[i] = NULL; log_assert (i < sizeof argv); - if (gnupg_spawn_process (pgmname, argv, NULL, 0, - &stream_to_check_pattern, NULL, NULL, &pid)) + if (gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDIN_PIPE, + NULL, NULL, &proc)) result = 1; /* Execute error - assume password should no be used. */ else { + int status; + + gnupg_process_get_streams (proc, 0, &stream_to_check_pattern, + NULL, NULL); + es_set_binary (stream_to_check_pattern); if (es_fwrite (pw, strlen (pw), 1, stream_to_check_pattern) != 1) { @@ -157,11 +163,13 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) else es_fflush (stream_to_check_pattern); es_fclose (stream_to_check_pattern); - if (gnupg_wait_process (pgmname, pid, 1, NULL)) + gnupg_process_wait (proc, 1); + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status); + if (status) result = 1; /* Helper returned an error - probably a match. */ else result = 0; /* Success; i.e. no match. */ - gnupg_release_process (pid); + gnupg_process_release (proc); } xfree (patternfname); diff --git a/common/asshelp.c b/common/asshelp.c index eb3e41bf5..8b3df2a4b 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -523,16 +523,12 @@ start_new_service (assuan_context_t *r_ctx, && assuan_socket_connect (ctx, sockname, 0, connect_flags)) { #ifdef HAVE_W32_SYSTEM - err = gnupg_spawn_process_detached (program? program : program_name, - argv, NULL); + err = gnupg_process_spawn (program? program : program_name, argv, + GNUPG_PROCESS_DETACHED, + NULL, NULL, NULL); #else /*!W32*/ - pid_t pid; - - err = gnupg_spawn_process_fd (program? program : program_name, - argv, -1, -1, -1, &pid); - if (!err) - err = gnupg_wait_process (program? program : program_name, - pid, 1, NULL); + err = gnupg_process_spawn (program? program : program_name, argv, + 0, NULL, NULL, NULL); #endif /*!W32*/ if (err) log_error ("failed to start %s '%s': %s\n", diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c index fa613449d..7b20a3796 100644 --- a/common/exechelp-posix.c +++ b/common/exechelp-posix.c @@ -273,73 +273,6 @@ get_all_open_fds (void) } -/* 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 & GNUPG_SPAWN_KEEP_STDIN); - nodevnull[1] = !!(flags & GNUPG_SPAWN_KEEP_STDOUT); - nodevnull[2] = !!(flags & GNUPG_SPAWN_KEEP_STDERR); - - /* Create the command line argument array. */ - i = 0; - if (argv) - while (argv[i]) - i++; - arg_list = xcalloc (i+2, sizeof *arg_list); - arg_list[0] = strrchr (pgmname, '/'); - if (arg_list[0]) - arg_list[0]++; - else - arg_list[0] = xstrdup (pgmname); - 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) - 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) - log_fatal ("dup2 std%s failed: %s\n", - i==0?"in":i==1?"out":"err", strerror (errno)); - } - - /* Close all other files. */ - close_all_fds (3, except); - - execv (pgmname, arg_list); - /* No way to print anything, as we have closed all streams. */ - _exit (127); -} - - static gpg_error_t do_create_pipe (int filedes[2]) { @@ -431,453 +364,160 @@ gnupg_close_pipe (int fd) close (fd); } +#include -/* Fork and exec the PGMNAME, see exechelp.h for details. */ -gpg_error_t -gnupg_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, - pid_t *pid) -{ - 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 & GNUPG_SPAWN_NONBLOCK); - - if (r_infp) - *r_infp = NULL; - if (r_outfp) - *r_outfp = NULL; - if (r_errfp) - *r_errfp = NULL; - *pid = (pid_t)(-1); /* Always required. */ - - if (r_infp) - { - err = create_pipe_and_estream (inpipe, &infp, 1, nonblock); - if (err) - return err; - } - - if (r_outfp) - { - err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock); - if (err) - { - if (infp) - es_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - return err; - } - } - - if (r_errfp) - { - err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock); - if (err) - { - if (infp) - es_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != -1) - close (outpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - - return err; - } - } - - - *pid = fork (); - if (*pid == (pid_t)(-1)) - { - err = my_error_from_syserror (); - log_error (_("error forking process: %s\n"), gpg_strerror (err)); - - if (infp) - es_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != -1) - close (outpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - - if (errfp) - es_fclose (errfp); - else if (errpipe[0] != -1) - close (errpipe[0]); - if (errpipe[1] != -1) - close (errpipe[1]); - return err; - } - - if (!*pid) - { - /* This is the child. */ - gcry_control (GCRYCTL_TERM_SECMEM); - es_fclose (infp); - es_fclose (outfp); - es_fclose (errfp); - do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], - except, flags); - /*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; - - return 0; -} - - - -/* Simplified version of gnupg_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 gnupg_wait_process is required. - - Returns 0 on success or an error code. */ -gpg_error_t -gnupg_spawn_process_fd (const char *pgmname, const char *argv[], - int infd, int outfd, int errfd, pid_t *pid) -{ - gpg_error_t err; - - *pid = fork (); - if (*pid == (pid_t)(-1)) - { - err = my_error_from_syserror (); - log_error (_("error forking process: %s\n"), strerror (errno)); - return err; - } - - if (!*pid) - { - gcry_control (GCRYCTL_TERM_SECMEM); - /* Run child. */ - do_exec (pgmname, argv, infd, outfd, errfd, NULL, 0); - /*NOTREACHED*/ - } - - 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. */ - -struct terminated_child -{ +struct gnupg_process { + const char *pgmname; + unsigned int terminated :1; /* or detached */ + unsigned int flags; pid_t pid; - int exitcode; - struct terminated_child *next; + int fd_in; + int fd_out; + int fd_err; + int wstatus; }; -struct terminated_child *terminated_children; +static int gnupg_process_syscall_func_initialized; +/* Functions called before and after blocking syscalls. */ +static void (*pre_syscall_func) (void); +static void (*post_syscall_func) (void); -static gpg_error_t -store_result (pid_t pid, int exitcode) +static void +check_syscall_func (void) { - 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; - - return 0; + if (!gnupg_process_syscall_func_initialized) + { + gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func); + gnupg_process_syscall_func_initialized = 1; + } } +static void +pre_syscall (void) +{ + if (pre_syscall_func) + pre_syscall_func (); +} + +static void +post_syscall (void) +{ + if (post_syscall_func) + post_syscall_func (); +} + + +static gpg_err_code_t +do_create_socketpair (int filedes[2]) +{ + gpg_error_t err = 0; + + pre_syscall (); + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, filedes) == -1) + { + err = gpg_err_code_from_syserror (); + filedes[0] = filedes[1] = -1; + } + post_syscall (); + + return err; +} static int -get_result (pid_t pid, int *r_exitcode) +posix_open_null (int for_write) { - struct terminated_child *c, **prevp; + int fd; - for (prevp = &terminated_children, c = terminated_children; - c; - prevp = &c->next, c = c->next) - if (c->pid == pid) + fd = open ("/dev/null", for_write? O_WRONLY : O_RDONLY); + if (fd == -1) + log_fatal ("failed to open '/dev/null': %s\n", strerror (errno)); + return fd; +} + +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) +{ + 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); +} + +static void +my_exec (const char *pgmname, const char *argv[], struct spawn_cb_arg *sca) +{ + int i; + + /* 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) { - *prevp = c->next; - *r_exitcode = c->exitcode; - xfree (c); - return 1; + if (dup2 (sca->fds[i], i) == -1) + 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]. + */ } - return 0; + /* Close all other files. */ + 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); } - -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) -{ - gpg_err_code_t ec; - int i, status; - - if (r_exitcode) - *r_exitcode = -1; - - if (pid == (pid_t)(-1)) - return gpg_error (GPG_ERR_INV_VALUE); - -#ifdef USE_NPTH - i = npth_waitpid (pid, &status, hang? 0:WNOHANG); -#else - while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1) - && errno == EINTR); -#endif - - if (i == (pid_t)(-1)) - { - ec = gpg_err_code_from_errno (errno); - log_error (_("waiting for process %d to terminate failed: %s\n"), - (int)pid, strerror (errno)); - } - else if (!i) - { - ec = GPG_ERR_TIMEOUT; /* Still running. */ - } - else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) - { - log_error (_("error running '%s': probably not installed\n"), pgmname); - ec = GPG_ERR_CONFIGURATION; - } - else if (WIFEXITED (status) && WEXITSTATUS (status)) - { - if (!r_exitcode) - log_error (_("error running '%s': exit status %d\n"), pgmname, - WEXITSTATUS (status)); - else - *r_exitcode = WEXITSTATUS (status); - ec = GPG_ERR_GENERAL; - } - else if (!WIFEXITED (status)) - { - log_error (_("error running '%s': terminated\n"), pgmname); - ec = GPG_ERR_GENERAL; - } - else - { - if (r_exitcode) - *r_exitcode = 0; - ec = 0; - } - - return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); -} - -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, - int hang, int *r_exitcodes) -{ - gpg_err_code_t ec = 0; - size_t i, left; - int *dummy = NULL; - - if (r_exitcodes == NULL) - { - dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count); - if (dummy == NULL) - return gpg_err_code_from_syserror (); - } - - for (i = 0, left = count; i < count; i++) - { - int status = -1; - - /* Skip invalid PID. */ - if (pids[i] == (pid_t)(-1)) - { - r_exitcodes[i] = -1; - left -= 1; - continue; - } - - /* See if there was a previously stored result for this pid. */ - if (get_result (pids[i], &status)) - left -= 1; - - r_exitcodes[i] = status; - } - - while (left > 0) - { - pid_t pid; - int status; - -#ifdef USE_NPTH - pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG); -#else - while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1) - && errno == EINTR); -#endif - - if (pid == (pid_t)(-1)) - { - ec = gpg_err_code_from_errno (errno); - log_error (_("waiting for processes to terminate failed: %s\n"), - strerror (errno)); - break; - } - else if (!pid) - { - ec = GPG_ERR_TIMEOUT; /* Still running. */ - break; - } - else - { - for (i = 0; i < count; i++) - if (pid == pids[i]) - break; - - if (i == count) - { - /* No match, store this result. */ - ec = store_result (pid, status); - if (ec) - break; - continue; - } - - /* Process PIDS[i] died. */ - if (r_exitcodes[i] != (pid_t) -1) - { - log_error ("PID %d was reused", pid); - ec = GPG_ERR_GENERAL; - break; - } - - left -= 1; - r_exitcodes[i] = status; - } - } - - for (i = 0; i < count; i++) - { - if (r_exitcodes[i] == -1) - continue; - - if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127) - { - 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) - 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])) - { - log_error (_("error running '%s': terminated\n"), pgmnames[i]); - ec = GPG_ERR_GENERAL; - } - } - - xfree (dummy); - return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); -} - - - -void -gnupg_release_process (pid_t pid) -{ - (void)pid; -} - - -/* 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. All standard file descriptors are - connected to /dev/null. */ -gpg_error_t -gnupg_spawn_process_detached (const char *pgmname, const char *argv[], - const char *envp[] ) +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; - int i; + /* FIXME: Is this GnuPG specific or should we keep it. */ if (getuid() != geteuid()) - return my_error (GPG_ERR_BUG); + { + xfree (argv); + return GPG_ERR_BUG; + } - if ((ec = gnupg_access (pgmname, X_OK))) - return gpg_err_make (default_errsource, ec); + if (access (pgmname, X_OK)) + { + ec = gpg_err_code_from_syserror (); + xfree (argv); + return ec; + } + pre_syscall (); pid = fork (); + post_syscall (); if (pid == (pid_t)(-1)) { - log_error (_("error forking process: %s\n"), strerror (errno)); - return my_error_from_syserror (); + ec = gpg_err_code_from_syserror (); + log_error (_("error forking process: %s\n"), gpg_strerror (ec)); + xfree (argv); + return ec; } + if (!pid) { pid_t pid2; + struct spawn_cb_arg sca; - gcry_control (GCRYCTL_TERM_SECMEM); if (setsid() == -1 || chdir ("/")) _exit (1); @@ -887,31 +527,506 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[], if (pid2) _exit (0); /* Let the parent exit immediately. */ - if (envp) - for (i=0; envp[i]; i++) - putenv (xstrdup (envp[i])); - - do_exec (pgmname, argv, -1, -1, -1, NULL, 0); + call_spawn_cb (&sca, -1, -1, -1, spawn_cb, spawn_cb_arg); + my_exec (pgmname, argv, &sca); /*NOTREACHED*/ } + pre_syscall (); if (waitpid (pid, NULL, 0) == -1) - log_error ("waitpid failed in gnupg_spawn_process_detached: %s", - strerror (errno)); + { + post_syscall (); + ec = gpg_err_code_from_syserror (); + log_error ("waitpid failed in gpgrt_spawn_process_detached: %s", + gpg_strerror (ec)); + return ec; + } + else + post_syscall (); return 0; } - -/* 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 -gnupg_kill_process (pid_t pid) +gnupg_spawn_helper (struct spawn_cb_arg *sca) { - if (pid != (pid_t)(-1)) - { - kill (pid, SIGTERM); - } + int *user_except = sca->arg; + sca->except_fds = user_except; +} + +gpg_err_code_t +gnupg_process_spawn (const char *pgmname, const char *argv1[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gnupg_process_t *r_process) +{ + gpg_err_code_t ec; + gnupg_process_t process; + int fd_in[2]; + int fd_out[2]; + int fd_err[2]; + pid_t pid; + const char **argv; + int i, j; + + check_syscall_func (); + + 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 & GNUPG_PROCESS_DETACHED)) + { + if ((flags & GNUPG_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 gnupg_process)); + if (process == NULL) + { + xfree (argv); + return gpg_err_code_from_syserror (); + } + + process->pgmname = pgmname; + process->flags = flags; + + if ((flags & GNUPG_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 & GNUPG_PROCESS_STDIN_PIPE)) + { + ec = do_create_pipe (fd_in); + if (ec) + { + xfree (process); + xfree (argv); + return ec; + } + } + else if ((flags & GNUPG_PROCESS_STDIN_KEEP)) + { + fd_in[0] = 0; + fd_in[1] = -1; + } + else + { + fd_in[0] = -1; + fd_in[1] = -1; + } + + if ((flags & GNUPG_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 & GNUPG_PROCESS_STDOUT_KEEP)) + { + fd_out[0] = -1; + fd_out[1] = 1; + } + else + { + fd_out[0] = -1; + fd_out[1] = -1; + } + } + + if ((flags & GNUPG_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 & GNUPG_PROCESS_STDERR_KEEP)) + { + fd_err[0] = -1; + fd_err[1] = 2; + } + else + { + fd_err[0] = -1; + fd_err[1] = -1; + } + + pre_syscall (); + pid = fork (); + post_syscall (); + if (pid == (pid_t)(-1)) + { + ec = gpg_err_code_from_syserror (); + 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) + { + 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. */ + my_exec (pgmname, argv, &sca); + /*NOTREACHED*/ + } + + 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 = gnupg_process_wait (process, 1); + gnupg_process_release (process); + return ec; + } + + *r_process = process; + return 0; +} + +static gpg_err_code_t +process_kill (gnupg_process_t process, int sig) +{ + gpg_err_code_t ec = 0; + pid_t pid = process->pid; + + pre_syscall (); + if (kill (pid, sig) < 0) + ec = gpg_err_code_from_syserror (); + post_syscall (); + return ec; +} + +gpg_err_code_t +gnupg_process_terminate (gnupg_process_t process) +{ + return process_kill (process, SIGTERM); +} + +gpg_err_code_t +gnupg_process_get_fds (gnupg_process_t process, unsigned int flags, + int *r_fd_in, int *r_fd_out, int *r_fd_err) +{ + (void)flags; + if (r_fd_in) + { + *r_fd_in = process->fd_in; + process->fd_in = -1; + } + if (r_fd_out) + { + *r_fd_out = process->fd_out; + process->fd_out = -1; + } + if (r_fd_err) + { + *r_fd_err = process->fd_err; + process->fd_err = -1; + } + + return 0; +} + +gpg_err_code_t +gnupg_process_get_streams (gnupg_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 & GNUPG_PROCESS_STREAM_NONBLOCK)? 1: 0; + + if (r_fp_in) + { + *r_fp_in = es_fdopen (process->fd_in, nonblock? "w,nonblock" : "w"); + process->fd_in = -1; + } + if (r_fp_out) + { + *r_fp_out = es_fdopen (process->fd_out, nonblock? "r,nonblock" : "r"); + process->fd_out = -1; + } + if (r_fp_err) + { + *r_fp_err = es_fdopen (process->fd_err, nonblock? "r,nonblock" : "r"); + process->fd_err = -1; + } + return 0; +} + +static gpg_err_code_t +process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr) +{ + switch (request) + { + case GNUPG_PROCESS_NOP: + return 0; + + case GNUPG_PROCESS_GET_PROC_ID: + { + int *r_id = va_arg (arg_ptr, int *); + + if (r_id == NULL) + return GPG_ERR_INV_VALUE; + + *r_id = (int)process->pid; + return 0; + } + + case GNUPG_PROCESS_GET_EXIT_ID: + { + int status = process->wstatus; + int *r_exit_status = va_arg (arg_ptr, int *); + + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + if (WIFEXITED (status)) + { + if (r_exit_status) + *r_exit_status = WEXITSTATUS (status); + } + else + *r_exit_status = -1; + + return 0; + } + + case GNUPG_PROCESS_GET_PID: + { + pid_t *r_pid = va_arg (arg_ptr, pid_t *); + + if (r_pid == NULL) + return GPG_ERR_INV_VALUE; + + *r_pid = process->pid; + return 0; + } + + case GNUPG_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 *); + + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + 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 GNUPG_PROCESS_KILL: + { + int sig = va_arg (arg_ptr, int); + + return process_kill (process, sig); + } + + default: + break; + } + + return GPG_ERR_UNKNOWN_COMMAND; +} + +gpg_err_code_t +gnupg_process_ctl (gnupg_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 +gnupg_process_wait (gnupg_process_t process, int hang) +{ + gpg_err_code_t ec; + int status; + pid_t pid; + + if (process->terminated) + /* Already terminated. */ + return 0; + + pre_syscall (); + while ((pid = waitpid (process->pid, &status, hang? 0: WNOHANG)) + == (pid_t)(-1) && errno == EINTR); + post_syscall (); + + if (pid == (pid_t)(-1)) + { + ec = gpg_err_code_from_syserror (); + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, gpg_strerror (ec)); + } + else if (!pid) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + } + else + { + process->terminated = 1; + process->wstatus = status; + ec = 0; + } + + return ec; +} + +void +gnupg_process_release (gnupg_process_t process) +{ + if (!process) + return; + + if (process->terminated) + { + gnupg_process_terminate (process); + gnupg_process_wait (process, 1); + } + + xfree (process); +} + +gpg_err_code_t +gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang) +{ + gpg_err_code_t ec = 0; + int i; + + for (i = 0; i < count; i++) + { + if (process_list[i]->terminated) + continue; + + ec = gnupg_process_wait (process_list[i], hang); + if (ec) + break; + } + + return ec; } diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c index 0034e03f2..8057cc3b4 100644 --- a/common/exechelp-w32.c +++ b/common/exechelp-w32.c @@ -33,6 +33,8 @@ #if !defined(HAVE_W32_SYSTEM) #error This code is only used on W32. +#else +#define _WIN32_WINNT 0x600 #endif #include @@ -63,9 +65,11 @@ #include "util.h" #include "i18n.h" #include "sysutils.h" +#define NEED_STRUCT_SPAWN_CB_ARG #include "exechelp.h" #include +#include /* Define to 1 do enable debugging. */ #define DEBUG_W32_SPAWN 0 @@ -405,646 +409,828 @@ gnupg_close_pipe (int fd) if (fd != -1) close (fd); } + +struct gnupg_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 int gnupg_process_syscall_func_initialized; -/* Fork and exec the PGMNAME, see exechelp.h for details. */ -gpg_error_t -gnupg_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, - pid_t *pid) +/* Functions called before and after blocking syscalls. */ +static void (*pre_syscall_func) (void); +static void (*post_syscall_func) (void); + +static void +check_syscall_func (void) { - gpg_error_t err; - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = + if (!gnupg_process_syscall_func_initialized) { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFOW si; - int cr_flags; - char *cmdline; - wchar_t *wcmdline = NULL; - wchar_t *wpgmname = NULL; - 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, rc; - es_syshd_t syshd; - gpg_err_source_t errsource = default_errsource; - int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); - - (void)except; /* Not yet used. */ - - if (r_infp) - *r_infp = NULL; - if (r_outfp) - *r_outfp = NULL; - if (r_errfp) - *r_errfp = NULL; - *pid = (pid_t)(-1); /* Always required. */ - - if (r_infp) - { - if (create_inheritable_pipe (inpipe, INHERIT_READ)) - { - err = gpg_err_make (errsource, GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - return err; - } - - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = inpipe[1]; - infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w"); - if (!infp) - { - err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); - 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; - } + gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func); + gnupg_process_syscall_func_initialized = 1; } +} - if (r_outfp) - { - if (create_inheritable_pipe (outpipe, INHERIT_WRITE)) - { - err = gpg_err_make (errsource, GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - return err; - } - - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = outpipe[0]; - outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); - if (!outfp) - { - err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); - 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) - es_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - return err; - } - } - - if (r_errfp) - { - if (create_inheritable_pipe (errpipe, INHERIT_WRITE)) - { - err = gpg_err_make (errsource, GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - return err; - } - - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = errpipe[0]; - errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); - if (!errfp) - { - err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); - 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) - es_fclose (outfp); - else if (outpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (infp) - es_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - return err; - } - } - - /* 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; - - if (inpipe[0] == INVALID_HANDLE_VALUE) - nullhd[0] = ((flags & GNUPG_SPAWN_KEEP_STDIN)? - GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0)); - if (outpipe[1] == INVALID_HANDLE_VALUE) - nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)? - GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1)); - if (errpipe[1] == INVALID_HANDLE_VALUE) - nullhd[2] = ((flags & GNUPG_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_HIDE; - 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]; - - cr_flags = (CREATE_DEFAULT_ERROR_MODE - | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0) - | GetPriorityClass (GetCurrentProcess ()) - | CREATE_SUSPENDED); - /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", */ - /* pgmname, cmdline); */ - /* Take care: CreateProcessW may modify wpgmname */ - if (!(wpgmname = utf8_to_wchar (pgmname))) - rc = 0; - else if (!(wcmdline = utf8_to_wchar (cmdline))) - rc = 0; - else - rc = CreateProcessW (wpgmname, /* Program to start. */ - wcmdline, /* 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 (!rc) - { - if (!wpgmname || !wcmdline) - log_error ("CreateProcess failed (utf8_to_wchar): %s\n", - strerror (errno)); - else - log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); - xfree (wpgmname); - xfree (wcmdline); - xfree (cmdline); - if (infp) - es_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (errfp) - es_fclose (errfp); - else if (errpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[0]); - if (errpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[1]); - return gpg_err_make (errsource, GPG_ERR_GENERAL); - } - xfree (wpgmname); - xfree (wcmdline); - 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]); - - /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ - /* " dwProcessID=%d dwThreadId=%d\n", */ - /* pi.hProcess, pi.hThread, */ - /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ - /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ - - /* Fixme: For unknown reasons AllowSetForegroundWindow returns an - invalid argument error if we pass it the correct processID. As a - workaround we use -1 (ASFW_ANY). */ - if ((flags & GNUPG_SPAWN_RUN_ASFW)) - gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); - - /* Process has been created suspended; resume it now. */ - ResumeThread (pi.hThread); - CloseHandle (pi.hThread); - - if (r_infp) - *r_infp = infp; - if (r_outfp) - *r_outfp = outfp; - if (r_errfp) - *r_errfp = errfp; - - *pid = handle_to_pid (pi.hProcess); - return 0; +static void +pre_syscall (void) +{ + if (pre_syscall_func) + pre_syscall_func (); +} +static void +post_syscall (void) +{ + if (post_syscall_func) + post_syscall_func (); } - -/* Simplified version of gnupg_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 gnupg_wait_process is required. - - Returns 0 on success or an error code. */ -gpg_error_t -gnupg_spawn_process_fd (const char *pgmname, const char *argv[], - int infd, int outfd, int errfd, pid_t *pid) +/* + * Check if STARTUPINFOEXW supports PROC_THREAD_ATTRIBUTE_HANDLE_LIST. + */ +static int +check_windows_version (void) +{ + static int is_vista_or_later = -1; + + OSVERSIONINFO osvi; + + 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 gpg_err_code_t +spawn_detached (const char *pgmname, char *cmdline, + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) { - gpg_error_t err; SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; - STARTUPINFOW si; - char *cmdline; + STARTUPINFOEXW si; + int cr_flags; wchar_t *wcmdline = NULL; wchar_t *wpgmname = NULL; - int i, rc; - HANDLE stdhd[3]; + gpg_err_code_t ec; + int ret; + struct spawn_cb_arg sca; + BOOL ask_inherit = FALSE; - /* Setup return values. */ - *pid = (pid_t)(-1); - - /* 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; + ec = gnupg_access (pgmname, X_OK); + if (ec) + { + xfree (cmdline); + 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); -/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ - /* Take care: CreateProcessW may modify wpgmname */ - if (!(wpgmname = utf8_to_wchar (pgmname))) - rc = 0; - else if (!(wcmdline = utf8_to_wchar (cmdline))) - rc = 0; - else - rc = CreateProcessW (wpgmname, /* Program to start. */ - wcmdline, /* 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 (!rc) + 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) { - if (!wpgmname || !wcmdline) - log_error ("CreateProcess failed (utf8_to_wchar): %s\n", - strerror (errno)); - else - log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); - err = my_error (GPG_ERR_GENERAL); - } - else - err = 0; - xfree (wpgmname); - xfree (wcmdline); - xfree (cmdline); - for (i=0; i < 3; i++) - if (stdhd[i] != INVALID_HANDLE_VALUE) - CloseHandle (stdhd[i]); - if (err) - return err; + SIZE_T attr_list_size = 0; + HANDLE hd[16]; + HANDLE *hd_p = sca.inherit_hds; + int j = 0; -/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ -/* " dwProcessID=%d dwThreadId=%d\n", */ -/* pi.hProcess, pi.hThread, */ -/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ - - /* Process has been created suspended; resume it now. */ - ResumeThread (pi.hThread); - CloseHandle (pi.hThread); - - *pid = handle_to_pid (pi.hProcess); - return 0; - -} - - -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) -{ - return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode); -} - -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, - int hang, int *r_exitcodes) -{ - gpg_err_code_t ec = 0; - size_t i; - HANDLE *procs; - int code; - - procs = xtrycalloc (count, sizeof *procs); - if (procs == NULL) - return my_error_from_syserror (); - - for (i = 0; i < count; i++) - { - if (r_exitcodes) - r_exitcodes[i] = -1; - - if (pids[i] == (pid_t)(-1)) - return my_error (GPG_ERR_INV_VALUE); - - procs[i] = pid_to_handle (pids[i]); - } - - /* FIXME: We should do a pth_waitpid here. However this has not yet - been implemented. A special W32 pth system call would even be - better. */ - code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0); - switch (code) - { - case WAIT_TIMEOUT: - ec = GPG_ERR_TIMEOUT; - goto leave; - - case WAIT_FAILED: - log_error (_("waiting for processes to terminate failed: %s\n"), - w32_strerror (-1)); - ec = GPG_ERR_GENERAL; - goto leave; - - case WAIT_OBJECT_0: - for (i = 0; i < count; i++) + if (hd_p) { - DWORD exc; - - if (! GetExitCodeProcess (procs[i], &exc)) - { - log_error (_("error getting exit code of process %d: %s\n"), - (int) pids[i], w32_strerror (-1) ); - ec = GPG_ERR_GENERAL; - } - else if (exc) - { - if (!r_exitcodes) - 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; - } + while (*hd_p != INVALID_HANDLE_VALUE) + if (j < DIM (hd)) + hd[j++] = *hd_p++; + else + { + log_error ("Too much handles\n"); + break; + } } - break; - default: - log_error ("WaitForMultipleObjects returned unexpected " - "code %d\n", code); - ec = GPG_ERR_GENERAL; - 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; + } } - leave: - return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); -} - - - -void -gnupg_release_process (pid_t pid) -{ - if (pid != (pid_t)INVALID_HANDLE_VALUE) - { - HANDLE process = (HANDLE)pid; - - CloseHandle (process); - } -} - - -/* 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. All standard file descriptors are - connected to /dev/null. */ -gpg_error_t -gnupg_spawn_process_detached (const char *pgmname, const char *argv[], - const char *envp[] ) -{ - gpg_error_t err; - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFOW si; - int cr_flags; - char *cmdline; - wchar_t *wcmdline = NULL; - wchar_t *wpgmname = NULL; - BOOL in_job = FALSE; - gpg_err_code_t ec; - int rc; - int jobdebug; - - /* We don't use ENVP. */ - (void)envp; - - cmdline = getenv ("GNUPG_EXEC_DEBUG_FLAGS"); - jobdebug = (cmdline && (atoi (cmdline) & 1)); - - if ((ec = gnupg_access (pgmname, X_OK))) - return gpg_err_make (default_errsource, 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; + 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); - /* Check if we were spawned as part of a Job. - * In a job we need to add CREATE_BREAKAWAY_FROM_JOB - * to the cr_flags, otherwise our child processes - * are killed when we terminate. */ - if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job)) - { - log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1)); - in_job = FALSE; - } - - if (in_job) - { - /* Only try to break away from job if it is allowed, otherwise - * CreateProcess() would fail with an "Access is denied" error. */ - JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; - if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, - &info, sizeof info, NULL)) - { - log_error ("QueryInformationJobObject() failed: %s\n", - w32_strerror (-1)); - } - else if ((info.BasicLimitInformation.LimitFlags & - JOB_OBJECT_LIMIT_BREAKAWAY_OK)) - { - if (jobdebug) - log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n"); - cr_flags |= CREATE_BREAKAWAY_FROM_JOB; - } - else if ((info.BasicLimitInformation.LimitFlags & - JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) - { - /* The child process should automatically detach from the job. */ - if (jobdebug) - log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; " - "JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n"); - } - else - { - /* It seems that the child process must remain in the job. - * This is not necessarily an error, although it can cause premature - * termination of the child process when the job is closed. */ - if (jobdebug) - log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n"); - } - } - else - { - if (jobdebug) - log_debug ("Process is not in a Job\n"); - } - - /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */ - /* pgmname, cmdline); */ /* Take care: CreateProcessW may modify wpgmname */ if (!(wpgmname = utf8_to_wchar (pgmname))) - rc = 0; + ret = 0; else if (!(wcmdline = utf8_to_wchar (cmdline))) - rc = 0; + ret = 0; else - rc = CreateProcessW (wpgmname, /* Program to start. */ - wcmdline, /* 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 (!rc) + 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) log_error ("CreateProcess failed (utf8_to_wchar): %s\n", strerror (errno)); else - log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); + log_error ("CreateProcess(detached) failed: %d\n", + (int)GetLastError ()); xfree (wpgmname); xfree (wcmdline); xfree (cmdline); - return my_error (GPG_ERR_GENERAL); + return GPG_ERR_GENERAL; } + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); xfree (wpgmname); xfree (wcmdline); xfree (cmdline); - cmdline = NULL; -/* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ -/* " dwProcessID=%d dwThreadId=%d\n", */ -/* pi.hProcess, pi.hThread, */ -/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + /* 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; } -/* 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 -gnupg_kill_process (pid_t pid) +gnupg_spawn_helper (struct spawn_cb_arg *sca) { - if (pid != (pid_t) INVALID_HANDLE_VALUE) - { - HANDLE process = (HANDLE) pid; - - /* Arbitrary error code. */ - TerminateProcess (process, 1); - } + HANDLE *user_except = sca->arg; + sca->inherit_hds = user_except; +} + +gpg_err_code_t +gnupg_process_spawn (const char *pgmname, const char *argv[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gnupg_process_t *r_process) +{ + gpg_err_code_t ec; + gnupg_process_t process; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFOEXW si; + int cr_flags; + char *cmdline; + 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; + + check_syscall_func (); + + /* Build the command line. */ + ec = build_w32_commandline (pgmname, argv, &cmdline); + if (ec) + return ec; + + if ((flags & GNUPG_PROCESS_DETACHED)) + { + if ((flags & GNUPG_PROCESS_STDFDS_SETTING)) + { + xfree (cmdline); + return GPG_ERR_INV_FLAG; + } + + /* In detached case, it must be no R_PROCESS. */ + if (r_process) + { + xfree (cmdline); + return GPG_ERR_INV_ARG; + } + + return spawn_detached (pgmname, cmdline, spawn_cb, spawn_cb_arg); + } + + if (r_process) + *r_process = NULL; + + process = xtrymalloc (sizeof (struct gnupg_process)); + if (process == NULL) + { + xfree (cmdline); + return gpg_err_code_from_syserror (); + } + + process->pgmname = pgmname; + process->flags = flags; + + if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR)) + { + xfree (process); + xfree (cmdline); + return GPG_ERR_NOT_SUPPORTED; + } + + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + { + ec = create_inheritable_pipe (hd_in, INHERIT_READ); + if (ec) + { + xfree (process); + xfree (cmdline); + return ec; + } + } + else if ((flags & GNUPG_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; + } + + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + { + ec = create_inheritable_pipe (hd_out, INHERIT_WRITE); + if (ec) + { + 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 & GNUPG_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 ((flags & GNUPG_PROCESS_STDERR_PIPE)) + { + ec = create_inheritable_pipe (hd_err, INHERIT_WRITE); + if (ec) + { + 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 & GNUPG_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); + } + + 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) + { + while (*hd_p != INVALID_HANDLE_VALUE) + if (j < DIM (hd)) + hd[j++] = *hd_p++; + else + { + 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 & GNUPG_PROCESS_STDIN_PIPE) + || !(flags & GNUPG_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + CloseHandle (hd_in[1]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + CloseHandle (hd_out[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || !(flags & GNUPG_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || !(flags & GNUPG_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; + } + } + + /* 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_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 + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED); + if (!(wpgmname = utf8_to_wchar (pgmname))) + ret = 0; + else if (!(wcmdline = 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) + log_error ("CreateProcess failed (utf8_to_wchar): %s\n", + strerror (errno)); + else + log_error ("CreateProcess failed: ec=%d\n", + (int)GetLastError ()); + if ((flags & GNUPG_PROCESS_STDIN_PIPE) + || !(flags & GNUPG_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + CloseHandle (hd_in[1]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + CloseHandle (hd_out[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || !(flags & GNUPG_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || !(flags & GNUPG_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + xfree (wpgmname); + xfree (wcmdline); + xfree (process); + xfree (cmdline); + return GPG_ERR_GENERAL; + } + + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + + if ((flags & GNUPG_PROCESS_STDIN_PIPE) + || !(flags & GNUPG_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || !(flags & GNUPG_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || !(flags & GNUPG_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 + * processID. As a workaround we use -1 (ASFW_ANY). */ + if (!AllowSetForegroundWindow (ASFW_ANY /*pi.dwProcessId*/)) + log_info ("AllowSetForegroundWindow() failed: ec=%d\n", + (int)GetLastError ()); + } + + /* Process has been created suspended; resume it now. */ + pre_syscall (); + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + post_syscall (); + + 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 = gnupg_process_wait (process, 1); + gnupg_process_release (process); + return ec; + } + + *r_process = process; + return 0; +} + +gpg_err_code_t +gnupg_process_get_fds (gnupg_process_t process, unsigned int flags, + int *r_fd_in, int *r_fd_out, int *r_fd_err) +{ + (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; + } + + return 0; +} + +gpg_err_code_t +gnupg_process_get_streams (gnupg_process_t process, unsigned int flags, + estream_t *r_fp_in, estream_t *r_fp_out, + estream_t *r_fp_err) +{ + int nonblock = (flags & GNUPG_PROCESS_STREAM_NONBLOCK)? 1: 0; + es_syshd_t syshd; + + syshd.type = ES_SYSHD_HANDLE; + if (r_fp_in) + { + syshd.u.handle = process->hd_in; + *r_fp_in = es_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 = es_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 = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + process->hd_err = INVALID_HANDLE_VALUE; + } + return 0; +} + +static gpg_err_code_t +process_kill (gnupg_process_t process, unsigned int exitcode) +{ + gpg_err_code_t ec = 0; + + pre_syscall (); + if (TerminateProcess (process->hProcess, exitcode)) + ec = gpg_err_code_from_syserror (); + post_syscall (); + return ec; +} + +static gpg_err_code_t +process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr) +{ + switch (request) + { + case GNUPG_PROCESS_NOP: + return 0; + + case GNUPG_PROCESS_GET_PROC_ID: + { + int *r_id = va_arg (arg_ptr, int *); + + if (r_id == NULL) + return GPG_ERR_INV_VALUE; + + *r_id = (int)GetProcessId (process->hProcess); + return 0; + } + + case GNUPG_PROCESS_GET_EXIT_ID: + { + int *r_exit_status = va_arg (arg_ptr, int *); + unsigned long exit_code; + + *r_exit_status = -1; + + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; + + if (GetExitCodeProcess (process->hProcess, &exit_code) == 0) + return gpg_err_code_from_syserror (); + + *r_exit_status = (int)exit_code; + return 0; + } + + case GNUPG_PROCESS_GET_P_HANDLE: + { + HANDLE *r_hProcess = va_arg (arg_ptr, HANDLE *); + + if (r_hProcess == NULL) + return GPG_ERR_INV_VALUE; + + *r_hProcess = process->hProcess; + process->hProcess = INVALID_HANDLE_VALUE; + return 0; + } + + case GNUPG_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 (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 GNUPG_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 GNUPG_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); + } + + default: + break; + } + + return GPG_ERR_UNKNOWN_COMMAND; +} + +gpg_err_code_t +gnupg_process_ctl (gnupg_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 +gnupg_process_wait (gnupg_process_t process, int hang) +{ + gpg_err_code_t ec; + int code; + + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; + + pre_syscall (); + code = WaitForSingleObject (process->hProcess, hang? INFINITE : 0); + post_syscall (); + + switch (code) + { + case WAIT_TIMEOUT: + ec = GPG_ERR_TIMEOUT; /* Still running. */ + break; + + case WAIT_FAILED: + log_error (_("waiting for process to terminate failed: ec=%d\n"), + (int)GetLastError ()); + ec = GPG_ERR_GENERAL; + break; + + case WAIT_OBJECT_0: + process->terminated = 1; + ec = 0; + break; + + default: + log_debug ("WaitForSingleObject returned unexpected code %d\n", code); + ec = GPG_ERR_GENERAL; + break; + } + + return ec; +} + +gpg_err_code_t +gnupg_process_terminate (gnupg_process_t process) +{ + return process_kill (process, 1); +} + +void +gnupg_process_release (gnupg_process_t process) +{ + if (!process) + return; + + if (process->terminated) + { + gnupg_process_terminate (process); + gnupg_process_wait (process, 1); + } + + xfree (process); +} + +gpg_err_code_t +gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang) +{ + gpg_err_code_t ec = 0; + int i; + + for (i = 0; i < count; i++) + { + if (process_list[i]->terminated) + continue; + + ec = gnupg_process_wait (process_list[i], hang); + if (ec) + break; + } + + return ec; } diff --git a/common/exechelp.h b/common/exechelp.h index 3343fe598..0370b23a4 100644 --- a/common/exechelp.h +++ b/common/exechelp.h @@ -73,140 +73,103 @@ gpg_error_t gnupg_create_pipe (int filedes[2]); void gnupg_close_pipe (int fd); -#define GNUPG_SPAWN_NONBLOCK 16 -#define GNUPG_SPAWN_RUN_ASFW 64 -#define GNUPG_SPAWN_DETACHED 128 -#define GNUPG_SPAWN_KEEP_STDIN 256 -#define GNUPG_SPAWN_KEEP_STDOUT 512 -#define GNUPG_SPAWN_KEEP_STDERR 1024 +/* The opaque type for a subprocess. */ +typedef struct gnupg_process *gnupg_process_t; +#ifdef HAVE_W32_SYSTEM +struct spawn_cb_arg; +#ifdef NEED_STRUCT_SPAWN_CB_ARG +struct spawn_cb_arg { + HANDLE hd[3]; + HANDLE *inherit_hds; + BOOL allow_foreground_window; + void *arg; +}; +#endif +#else +struct spawn_cb_arg { + int fds[3]; + int *except_fds; + void *arg; +}; +#endif -/* Fork and exec the program PGMNAME. +#define GNUPG_PROCESS_DETACHED (1 << 1) - If R_INFP is NULL connect stdin of the new process to /dev/null; if - it is not NULL store the address of a pointer to a new estream - there. If R_OUTFP is NULL connect stdout of the new process to - /dev/null; if it is not NULL store the address of a pointer to a - new estream there. If R_ERRFP is NULL connect stderr of the new - process to /dev/null; if it is not NULL store the address of a - pointer to a new estream there. On success the pid of the new - process is stored at PID. On error -1 is stored at PID and if - R_OUTFP or R_ERRFP are not NULL, NULL is stored there. +/* Specify how to keep/connect standard fds. */ +#define GNUPG_PROCESS_STDIN_PIPE (1 << 8) +#define GNUPG_PROCESS_STDOUT_PIPE (1 << 9) +#define GNUPG_PROCESS_STDERR_PIPE (1 << 10) +#define GNUPG_PROCESS_STDINOUT_SOCKETPAIR (1 << 11) +#define GNUPG_PROCESS_STDIN_KEEP (1 << 12) +#define GNUPG_PROCESS_STDOUT_KEEP (1 << 13) +#define GNUPG_PROCESS_STDERR_KEEP (1 << 14) +#define GNUPG_PROCESS_STDFDS_SETTING ( GNUPG_PROCESS_STDIN_PIPE \ + | GNUPG_PROCESS_STDOUT_PIPE | GNUPG_PROCESS_STDERR_PIPE \ + | GNUPG_PROCESS_STDINOUT_SOCKETPAIR | GNUPG_PROCESS_STDIN_KEEP \ + | GNUPG_PROCESS_STDOUT_KEEP | GNUPG_PROCESS_STDERR_KEEP) - The arguments for the process are expected in the NULL terminated - array ARGV. The program name itself should not be included there. - If PREEXEC is not NULL, the given function will be called right - before the exec. +#define GNUPG_PROCESS_STREAM_NONBLOCK (1 << 16) - IF EXCEPT is not NULL, it is expected to be an ordered list of file - descriptors, terminated by an entry with the value (-1). These - file descriptors won't be closed before spawning a new program. +/* Spawn helper. */ +void gnupg_spawn_helper (struct spawn_cb_arg *sca); - Returns 0 on success or an error code. Calling gnupg_wait_process - and gnupg_release_process is required if the function succeeded. +/* Spawn PGMNAME. */ +gpg_err_code_t gnupg_process_spawn (const char *pgmname, const char *argv[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gnupg_process_t *r_process); - FLAGS is a bit vector: +/* Get FDs for subprocess I/O. It is the caller which should care + FDs (closing FDs). */ +gpg_err_code_t gnupg_process_get_fds (gnupg_process_t process, + unsigned int flags, + int *r_fd_in, int *r_fd_out, + int *r_fd_err); - GNUPG_SPAWN_NONBLOCK - If set the two output streams are created in non-blocking - mode and the input stream is switched to non-blocking mode. - This is merely a convenience feature because the caller - could do the same with gpgrt_set_nonblock. Does not yet - work for Windows. +/* Get STREAMs for subprocess I/O. It is the caller which should care + STREAMs (closing STREAMs). */ +gpg_err_code_t gnupg_process_get_streams (gnupg_process_t process, + unsigned int flags, + gpgrt_stream_t *r_fp_in, + gpgrt_stream_t *r_fp_out, + gpgrt_stream_t *r_fp_err); - GNUPG_SPAWN_DETACHED - If set the process will be started as a background process. - This flag is only useful under W32 (but not W32CE) systems, - so that no new console is created and pops up a console - window when starting the server. Does not work on W32CE. +enum gnupg_process_requests + { + /* Portable requests */ + GNUPG_PROCESS_NOP = 0, + GNUPG_PROCESS_GET_PROC_ID = 1, + GNUPG_PROCESS_GET_EXIT_ID = 2, - GNUPG_SPAWN_RUN_ASFW - On W32 (but not on W32CE) run AllowSetForegroundWindow for - the child. Note that due to unknown problems this actually - allows SetForegroundWindow for all children of this process. + /* POSIX only */ + GNUPG_PROCESS_GET_PID = 16, + GNUPG_PROCESS_GET_WSTATUS = 17, + GNUPG_PROCESS_KILL = 18, - GNUPG_SPAWN_KEEP_STDIN - GNUPG_SPAWN_KEEP_STDOUT - GNUPG_SPAWN_KEEP_STDERR - Do not assign /dev/null to a non-required standard file - descriptor. + /* Windows only */ + GNUPG_PROCESS_GET_P_HANDLE = 32, + GNUPG_PROCESS_GET_HANDLES = 33, + GNUPG_PROCESS_GET_EXIT_CODE = 34, + GNUPG_PROCESS_KILL_WITH_EC = 35 + }; - */ -gpg_error_t -gnupg_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, - pid_t *pid); +/* Control of a process. */ +gpg_err_code_t gnupg_process_ctl (gnupg_process_t process, + unsigned int request, ...); +/* Wait for a single PROCESS. */ +gpg_err_code_t gnupg_process_wait (gnupg_process_t process, int hang); -/* Simplified version of gnupg_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 gnupg_wait_process and - gnupg_release_process is required. Returns 0 on success or an - error code. */ -gpg_error_t gnupg_spawn_process_fd (const char *pgmname, - const char *argv[], - int infd, int outfd, int errfd, - pid_t *pid); +/* Terminate a PROCESS. */ +gpg_err_code_t gnupg_process_terminate (gnupg_process_t process); +/* Release PROCESS resources. */ +void gnupg_process_release (gnupg_process_t process); -/* If HANG is true, waits for the process identified by PID to exit; - if HANG is false, checks whether the process has terminated. - PGMNAME should be the same as supplied to the spawn function and is - only used for diagnostics. Return values: - - 0 - The process exited successful. 0 is stored at R_EXITCODE. - - GPG_ERR_GENERAL - The process exited without success. The exit code of process - is then stored at R_EXITCODE. An exit code of -1 indicates - that the process terminated abnormally (e.g. due to a signal). - - GPG_ERR_TIMEOUT - The process is still running (returned only if HANG is false). - - GPG_ERR_INV_VALUE - An invalid PID has been specified. - - Other error codes may be returned as well. Unless otherwise noted, - -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL - if the exit code is not required (in that case an error message will - be printed). Note that under Windows PID is not the process id but - the handle of the process. */ -gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang, - int *r_exitcode); - -/* Like gnupg_wait_process, but for COUNT processes. */ -gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids, - size_t count, int hang, int *r_exitcodes); - - -/* 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 gnupg_kill_process (pid_t pid); - -/* Release the process identified by PID. This function is actually - only required for Windows but it does not harm to always call it. - It is a nop if PID is invalid. */ -void gnupg_release_process (pid_t pid); - - -/* 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_error_t gnupg_spawn_process_detached (const char *pgmname, - const char *argv[], - const char *envp[] ); - +/* Wait for a multiple processes. */ +gpg_err_code_t gnupg_process_wait_list (gnupg_process_t *process_list, + int count, int hang); #endif /*GNUPG_COMMON_EXECHELP_H*/ diff --git a/common/exectool.c b/common/exectool.c index aaf5898d0..3505c25f1 100644 --- a/common/exectool.c +++ b/common/exectool.c @@ -38,10 +38,14 @@ #include #include + #include "i18n.h" #include "logging.h" #include "membuf.h" #include "mischelp.h" +#ifdef HAVE_W32_SYSTEM +#define NEED_STRUCT_SPAWN_CB_ARG 1 +#endif #include "exechelp.h" #include "sysutils.h" #include "util.h" @@ -301,7 +305,6 @@ copy_buffer_flush (struct copy_buffer *c, estream_t sink) } - /* Run the program PGMNAME with the command line arguments given in * the NULL terminates array ARGV. If INPUT is not NULL it will be * fed to stdin of the process. stderr is logged using log_info and @@ -321,7 +324,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], void *status_cb_value) { gpg_error_t err; - pid_t pid = (pid_t) -1; + gnupg_process_t proc = NULL; estream_t infp = NULL; estream_t extrafp = NULL; estream_t outfp = NULL, errfp = NULL; @@ -335,7 +338,6 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], read_and_log_buffer_t fderrstate; struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL; int quiet = 0; - int dummy_exitcode; memset (fds, 0, sizeof fds); memset (&fderrstate, 0, sizeof fderrstate); @@ -411,10 +413,15 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], else exceptclose[0] = -1; - err = gnupg_spawn_process (pgmname, argv, - exceptclose, GNUPG_SPAWN_NONBLOCK, - input? &infp : NULL, - &outfp, &errfp, &pid); + err = gnupg_process_spawn (pgmname, argv, + ((input + ? GNUPG_PROCESS_STDIN_PIPE + : 0) + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE), + gnupg_spawn_helper, exceptclose, &proc); + gnupg_process_get_streams (proc, GNUPG_PROCESS_STREAM_NONBLOCK, + input? &infp : NULL, &outfp, &errfp); if (extrapipe[0] != -1) close (extrapipe[0]); if (argsave) @@ -546,20 +553,25 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], es_fclose (outfp); outfp = NULL; es_fclose (errfp); errfp = NULL; - err = gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL); - pid = (pid_t)(-1); + err = gnupg_process_wait (proc, 1); + if (!err) + { /* To be compatible to old wait_process. */ + int status; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status); + if (status) + err = gpg_error (GPG_ERR_GENERAL); + } leave: - if (err && pid != (pid_t) -1) - gnupg_kill_process (pid); + if (err && proc) + gnupg_process_terminate (proc); es_fclose (infp); es_fclose (extrafp); es_fclose (outfp); es_fclose (errfp); - if (pid != (pid_t)(-1)) - gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL); - gnupg_release_process (pid); + gnupg_process_release (proc); copy_buffer_shred (cpbuf_in); xfree (cpbuf_in); diff --git a/dirmngr/ldap-wrapper.c b/dirmngr/ldap-wrapper.c index 23d514cf9..2ec944c72 100644 --- a/dirmngr/ldap-wrapper.c +++ b/dirmngr/ldap-wrapper.c @@ -87,7 +87,7 @@ struct wrapper_context_s { struct wrapper_context_s *next; - pid_t pid; /* The pid of the wrapper process. */ + gnupg_process_t proc;/* The wrapper process. */ int printable_pid; /* Helper to print diagnostics after the process has * been cleaned up. */ estream_t fp; /* Connected with stdout of the ldap wrapper. */ @@ -170,10 +170,10 @@ read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) static void destroy_wrapper (struct wrapper_context_s *ctx) { - if (ctx->pid != (pid_t)(-1)) + if (ctx->proc) { - gnupg_kill_process (ctx->pid); - gnupg_release_process (ctx->pid); + gnupg_process_terminate (ctx->proc); + gnupg_process_release (ctx->proc); } ksba_reader_release (ctx->reader); SAFE_CLOSE (ctx->fp); @@ -260,7 +260,7 @@ read_log_data (struct wrapper_context_s *ctx) if (gpg_err_code (err) == GPG_ERR_EAGAIN) return 0; log_error (_("error reading log from ldap wrapper %d: %s\n"), - (int)ctx->pid, gpg_strerror (err)); + ctx->printable_pid, gpg_strerror (err)); } print_log_line (ctx, NULL); /* Flush. */ SAFE_CLOSE (ctx->log_fp); @@ -438,50 +438,44 @@ ldap_reaper_thread (void *dummy) } /* Check whether the process is still running. */ - if (ctx->pid != (pid_t)(-1)) + if (ctx->proc) { - int status; - - err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0, - &status); + err = gnupg_process_wait (ctx->proc, 0); if (!err) { + int status; + + gnupg_process_ctl (ctx->proc, GNUPG_PROCESS_GET_EXIT_ID, + &status); if (DBG_EXTPROG) - log_info (_("ldap wrapper %d ready"), (int)ctx->pid); + log_info (_("ldap wrapper %d ready"), ctx->printable_pid); ctx->ready = 1; - gnupg_release_process (ctx->pid); - ctx->pid = (pid_t)(-1); + gnupg_process_release (ctx->proc); + ctx->proc = NULL; any_action = 1; - } - else if (gpg_err_code (err) == GPG_ERR_GENERAL) - { + if (status == 10) log_info (_("ldap wrapper %d ready: timeout\n"), - (int)ctx->pid); + ctx->printable_pid); else log_info (_("ldap wrapper %d ready: exitcode=%d\n"), - (int)ctx->pid, status); - ctx->ready = 1; - gnupg_release_process (ctx->pid); - ctx->pid = (pid_t)(-1); - any_action = 1; + ctx->printable_pid, status); } else if (gpg_err_code (err) != GPG_ERR_TIMEOUT) { log_error (_("waiting for ldap wrapper %d failed: %s\n"), - (int)ctx->pid, gpg_strerror (err)); + ctx->printable_pid, gpg_strerror (err)); any_action = 1; } } /* Check whether we should terminate the process. */ - if (ctx->pid != (pid_t)(-1) - && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) + if (ctx->proc && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) { - gnupg_kill_process (ctx->pid); + gnupg_process_terminate (ctx->proc); ctx->stamp = (time_t)(-1); log_info (_("ldap wrapper %d stalled - killing\n"), - (int)ctx->pid); + ctx->printable_pid); /* We need to close the log stream because the cleanup * loop waits for it. */ SAFE_CLOSE (ctx->log_fp); @@ -496,10 +490,10 @@ ldap_reaper_thread (void *dummy) { log_debug ("ldap worker states:\n"); for (ctx = reaper_list; ctx; ctx = ctx->next) - log_debug (" c=%p pid=%d/%d rdr=%p logfp=%p" + log_debug (" c=%p pid=%d rdr=%p logfp=%p" " ctrl=%p/%d la=%lu rdy=%d\n", ctx, - (int)ctx->pid, (int)ctx->printable_pid, + ctx->printable_pid, ctx->reader, ctx->log_fp, ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, (unsigned long)ctx->stamp, ctx->ready); @@ -602,9 +596,9 @@ ldap_wrapper_release_context (ksba_reader_t reader) if (ctx->reader == reader) { if (DBG_EXTPROG) - log_debug ("releasing ldap worker c=%p pid=%d/%d rdr=%p" + log_debug ("releasing ldap worker c=%p pid=%d rdr=%p" " ctrl=%p/%d\n", ctx, - (int)ctx->pid, (int)ctx->printable_pid, + ctx->printable_pid, ctx->reader, ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0); @@ -639,8 +633,8 @@ ldap_wrapper_connection_cleanup (ctrl_t ctrl) { ctx->ctrl->refcount--; ctx->ctrl = NULL; - if (ctx->pid != (pid_t)(-1)) - gnupg_kill_process (ctx->pid); + if (ctx->proc) + gnupg_process_terminate (ctx->proc); if (ctx->fp_err) log_info ("%s: reading from ldap wrapper %d failed: %s\n", __func__, ctx->printable_pid, gpg_strerror (ctx->fp_err)); @@ -798,7 +792,7 @@ gpg_error_t ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) { gpg_error_t err; - pid_t pid; + gnupg_process_t process; struct wrapper_context_s *ctx; int i; int j; @@ -854,19 +848,22 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) return err; } - err = gnupg_spawn_process (pgmname, arg_list, - NULL, GNUPG_SPAWN_NONBLOCK, - NULL, &outfp, &errfp, &pid); + err = gnupg_process_spawn (pgmname, arg_list, + (GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE), + NULL, NULL, &process); if (err) { - xfree (arg_list); + xfree (arg_list); xfree (ctx); log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); return err; } + gnupg_process_get_streams (process, GNUPG_PROCESS_STREAM_NONBLOCK, + NULL, &outfp, &errfp); + gnupg_process_ctl (process, GNUPG_PROCESS_GET_PROC_ID, &ctx->printable_pid); - ctx->pid = pid; - ctx->printable_pid = (int) pid; + ctx->proc = process; ctx->fp = outfp; ctx->log_fp = errfp; ctx->ctrl = ctrl; @@ -902,7 +899,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) if (DBG_EXTPROG) { log_debug ("ldap wrapper %d started (%p, %s)", - (int)ctx->pid, ctx->reader, pgmname); + ctx->printable_pid, ctx->reader, pgmname); for (i=0; arg_list[i]; i++) log_printf (" [%s]", arg_list[i]); log_printf ("\n"); diff --git a/g10/photoid.c b/g10/photoid.c index 72e6acf7d..a866eb083 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -594,34 +594,35 @@ run_with_pipe (struct spawn_info *info, const void *image, u32 len) " external programs\n")); return; #else /* !EXEC_TEMPFILE_ONLY */ - int to[2]; - pid_t pid; gpg_error_t err; const char *argv[4]; - - err = gnupg_create_pipe (to); - if (err) - return; + gnupg_process_t proc; fill_command_argv (argv, info->command); - err = gnupg_spawn_process_fd (argv[0], argv+1, to[0], -1, -1, &pid); - - close (to[0]); - + err = gnupg_process_spawn (argv[0], argv+1, GNUPG_PROCESS_STDIN_PIPE, + NULL, NULL, &proc); if (err) - { - log_error (_("unable to execute shell '%s': %s\n"), - argv[0], gpg_strerror (err)); - close (to[1]); - } + log_error (_("unable to execute shell '%s': %s\n"), + argv[0], gpg_strerror (err)); else { - write (to[1], image, len); - close (to[1]); + int fd_in; - err = gnupg_wait_process (argv[0], pid, 1, NULL); + err = gnupg_process_get_fds (proc, 0, &fd_in, NULL, NULL); + if (err) + log_error ("unable to get pipe connection '%s': %s\n", + argv[2], gpg_strerror (err)); + else + { + write (fd_in, image, len); + close (fd_in); + } + + err = gnupg_process_wait (proc, 1); if (err) log_error (_("unnatural exit of external program\n")); + + gnupg_process_release (proc); } #endif /* !EXEC_TEMPFILE_ONLY */ } @@ -689,14 +690,11 @@ show_photo (const char *command, const char *name, const void *image, u32 len) log_error (_("system error while calling external program: %s\n"), strerror (errno)); #else - pid_t pid; gpg_error_t err; const char *argv[4]; fill_command_argv (argv, spawn->command); - err = gnupg_spawn_process_fd (argv[0], argv+1, -1, -1, -1, &pid); - if (!err) - err = gnupg_wait_process (argv[0], pid, 1, NULL); + err = gnupg_process_spawn (argv[0], argv+1, 0, NULL, NULL, NULL); if (err) log_error (_("unnatural exit of external program\n")); #endif diff --git a/g13/be-encfs.c b/g13/be-encfs.c index 0e2c68bf3..ac6d6d6cd 100644 --- a/g13/be-encfs.c +++ b/g13/be-encfs.c @@ -28,10 +28,10 @@ #include "g13.h" #include "../common/i18n.h" #include "keyblob.h" -#include "be-encfs.h" -#include "runner.h" #include "../common/sysutils.h" #include "../common/exechelp.h" +#include "runner.h" +#include "be-encfs.h" /* Command values used to run the encfs tool. */ @@ -81,7 +81,9 @@ run_umount_helper (const char *mountpoint) args[1] = mountpoint; args[2] = NULL; - err = gnupg_spawn_process_detached (pgmname, args, NULL); + err = gnupg_process_spawn (pgmname, args, + GNUPG_PROCESS_DETACHED, + NULL, NULL, NULL); if (err) log_error ("failed to run '%s': %s\n", pgmname, gpg_strerror (err)); @@ -218,12 +220,11 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, gpg_error_t err; encfs_parm_t parm; runner_t runner = NULL; - int outbound[2] = { -1, -1 }; - int inbound[2] = { -1, -1 }; const char *pgmname; const char *argv[10]; - pid_t pid = (pid_t)(-1); int idx; + gnupg_process_t proc; + int inbound, outbound; (void)ctrl; @@ -246,15 +247,6 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, if (err) goto leave; - err = gnupg_create_inbound_pipe (inbound, NULL, 0); - if (!err) - err = gnupg_create_outbound_pipe (outbound, NULL, 0); - if (err) - { - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - goto leave; - } - pgmname = ENCFS; idx = 0; argv[idx++] = "-f"; @@ -267,47 +259,42 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, argv[idx++] = NULL; assert (idx <= DIM (argv)); - err = gnupg_spawn_process_fd (pgmname, argv, - outbound[0], -1, inbound[1], &pid); + err = gnupg_process_spawn (pgmname, argv, + (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDERR_PIPE), + NULL, NULL, &proc); if (err) { log_error ("error spawning '%s': %s\n", pgmname, gpg_strerror (err)); goto leave; } - close (outbound[0]); outbound[0] = -1; - close ( inbound[1]); inbound[1] = -1; - runner_set_fds (runner, inbound[0], outbound[1]); - inbound[0] = -1; /* Now owned by RUNNER. */ - outbound[1] = -1; /* Now owned by RUNNER. */ + err = gnupg_process_get_fds (proc, 0, &outbound, NULL, &inbound); + if (err) + { + log_error ("error get fds '%s': %s\n", pgmname, gpg_strerror (err)); + gnupg_process_release (proc); + goto leave; + } + + runner_set_fds (runner, inbound, outbound); runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm); parm = NULL; /* Now owned by RUNNER. */ - runner_set_pid (runner, pid); - pid = (pid_t)(-1); /* The process is now owned by RUNNER. */ + runner_set_proc (runner, proc); err = runner_spawn (runner); if (err) - goto leave; + { + gnupg_process_release (proc); + goto leave; + } *r_id = runner_get_rid (runner); log_info ("running '%s' in the background\n", pgmname); leave: - if (inbound[0] != -1) - close (inbound[0]); - if (inbound[1] != -1) - close (inbound[1]); - if (outbound[0] != -1) - close (outbound[0]); - if (outbound[1] != -1) - close (outbound[1]); - if (pid != (pid_t)(-1)) - { - gnupg_wait_process (pgmname, pid, 1, NULL); - gnupg_release_process (pid); - } runner_release (runner); encfs_handler_cleanup (parm); return err; diff --git a/g13/g13.c b/g13/g13.c index 2bbb453eb..785c91950 100644 --- a/g13/g13.c +++ b/g13/g13.c @@ -40,6 +40,7 @@ #include "../common/gc-opt-flags.h" #include "../common/asshelp.h" #include "../common/init.h" +#include "../common/exechelp.h" #include "keyblob.h" #include "server.h" #include "runner.h" diff --git a/g13/mount.c b/g13/mount.c index 45b60806c..071b76b67 100644 --- a/g13/mount.c +++ b/g13/mount.c @@ -34,10 +34,11 @@ #include "backend.h" #include "g13tuple.h" #include "mountinfo.h" -#include "runner.h" #include "../common/host2net.h" #include "server.h" /*(g13_keyblob_decrypt)*/ #include "../common/sysutils.h" +#include "../common/exechelp.h" +#include "runner.h" #include "call-syshelp.h" diff --git a/g13/runner.c b/g13/runner.c index b08d99030..c0534fe5d 100644 --- a/g13/runner.c +++ b/g13/runner.c @@ -29,8 +29,8 @@ #include "g13.h" #include "../common/i18n.h" #include "keyblob.h" -#include "runner.h" #include "../common/exechelp.h" +#include "runner.h" #include "mountinfo.h" /* The runner object. */ @@ -55,7 +55,7 @@ struct runner_s 2 = Thread is running and someone is holding a reference. */ int refcount; - pid_t pid; /* PID of the backend's process (the engine). */ + gnupg_process_t proc; /* Process of the backend's process (the engine). */ int in_fd; /* File descriptors to read from the engine. */ int out_fd; /* File descriptors to write to the engine. */ engine_handler_fnc_t handler; /* The handler functions. */ @@ -157,16 +157,16 @@ runner_release (runner_t runner) if (runner->handler_cleanup) runner->handler_cleanup (runner->handler_data); - if (runner->pid != (pid_t)(-1)) + if (runner->proc) { /* The process has not been cleaned up - do it now. */ - gnupg_kill_process (runner->pid); + gnupg_process_terminate (runner->proc); /* (Actually we should use the program name and not the arbitrary NAME of the runner object. However it does not matter because that information is only used for diagnostics.) */ - gnupg_wait_process (runner->name, runner->pid, 1, NULL); - gnupg_release_process (runner->pid); + gnupg_process_wait (runner->proc, 1); + gnupg_process_release (runner->proc); } xfree (runner->name); @@ -212,7 +212,7 @@ runner_new (runner_t *r_runner, const char *name) return gpg_error_from_syserror (); } runner->refcount = 1; - runner->pid = (pid_t)(-1); + runner->proc = NULL; runner->in_fd = -1; runner->out_fd = -1; @@ -266,15 +266,15 @@ runner_set_fds (runner_t runner, int in_fd, int out_fd) } -/* Set the PID of the backend engine. After this call the engine is +/* Set the PROC of the backend engine. After this call the engine is owned by the runner object. */ void -runner_set_pid (runner_t runner, pid_t pid) +runner_set_proc (runner_t runner, gnupg_process_t proc) { - if (check_already_spawned (runner, "runner_set_fds")) + if (check_already_spawned (runner, "runner_set_proc")) return; - runner->pid = pid; + runner->proc = proc; } @@ -366,15 +366,17 @@ runner_thread (void *arg) } /* Now wait for the process to finish. */ - if (!err && runner->pid != (pid_t)(-1)) + if (!err && runner->proc) { int exitcode; log_debug ("runner thread waiting ...\n"); - err = gnupg_wait_process (runner->name, runner->pid, 1, &exitcode); - gnupg_release_process (runner->pid); - runner->pid = (pid_t)(-1); - if (err) + err = gnupg_process_wait (runner->proc, 1); + if (!err) + gnupg_process_ctl (runner->proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + gnupg_process_release (runner->proc); + runner->proc = NULL; + if (exitcode) log_error ("running '%s' failed (exitcode=%d): %s\n", runner->name, exitcode, gpg_strerror (err)); log_debug ("runner thread waiting finished\n"); @@ -473,7 +475,7 @@ runner_cancel (runner_t runner) need to change the thread to wait on an event. */ runner->cancel_flag = 1; /* For now we use the brutal way and kill the process. */ - gnupg_kill_process (runner->pid); + gnupg_process_terminate (runner->proc); } } diff --git a/g13/runner.h b/g13/runner.h index 36181adf9..01c395e02 100644 --- a/g13/runner.h +++ b/g13/runner.h @@ -49,7 +49,7 @@ runner_t runner_find_by_rid (unsigned int rid); /* Functions to set properties of the runner. */ void runner_set_fds (runner_t runner, int in_fd, int out_fd); -void runner_set_pid (runner_t runner, pid_t pid); +void runner_set_proc (runner_t runner, gnupg_process_t proc); /* Register the handler functions with a runner. */ void runner_set_handler (runner_t runner, diff --git a/scd/app.c b/scd/app.c index 3686c0f6c..468fed294 100644 --- a/scd/app.c +++ b/scd/app.c @@ -2333,6 +2333,18 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, } +static void +setup_env (struct spawn_cb_arg *sca) +{ +#ifdef HAVE_W32_SYSTEM + (void)sca; /* Not supported on Windows. */ +#else + char *v = sca->arg; + + putenv (v); +#endif +} + static void report_change (int slot, int old_status, int cur_status) { @@ -2360,12 +2372,9 @@ report_change (int slot, int old_status, int cur_status) else { gpg_error_t err; - const char *args[9], *envs[2]; + const char *args[9]; char numbuf1[30], numbuf2[30], numbuf3[30]; - envs[0] = envstr; - envs[1] = NULL; - sprintf (numbuf1, "%d", slot); sprintf (numbuf2, "0x%04X", old_status); sprintf (numbuf3, "0x%04X", cur_status); @@ -2382,7 +2391,9 @@ report_change (int slot, int old_status, int cur_status) args[8] = NULL; fname = make_filename (gnupg_homedir (), "scd-event", NULL); - err = gnupg_spawn_process_detached (fname, args, envs); + err = gnupg_process_spawn (fname, args, + GNUPG_PROCESS_DETACHED, + setup_env, envstr, NULL); if (err && gpg_err_code (err) != GPG_ERR_ENOENT) log_error ("failed to run event handler '%s': %s\n", fname, gpg_strerror (err)); diff --git a/tests/gpgscm/ffi.c b/tests/gpgscm/ffi.c index ce18e0794..b46d5cb61 100644 --- a/tests/gpgscm/ffi.c +++ b/tests/gpgscm/ffi.c @@ -42,6 +42,9 @@ #endif #include "../../common/util.h" +#ifdef HAVE_W32_SYSTEM +#define NEED_STRUCT_SPAWN_CB_ARG +#endif #include "../../common/exechelp.h" #include "../../common/sysutils.h" @@ -753,25 +756,86 @@ do_es_write (scheme *sc, pointer args) } - /* Process handling. */ +struct proc_object_box +{ + gnupg_process_t proc; +}; + +static void +proc_object_finalize (scheme *sc, void *data) +{ + struct proc_object_box *box = data; + (void) sc; + + if (!box->proc) + gnupg_process_release (box->proc); + xfree (box); +} + +static void +proc_object_to_string (scheme *sc, char *out, size_t size, void *data) +{ + struct proc_object_box *box = data; + (void) sc; + + snprintf (out, size, "#proc %p", box->proc); +} + +static struct foreign_object_vtable proc_object_vtable = + { + proc_object_finalize, + proc_object_to_string, + }; + static pointer -do_spawn_process (scheme *sc, pointer args) +proc_wrap (scheme *sc, gnupg_process_t proc) +{ + struct proc_object_box *box = xmalloc (sizeof *box); + if (box == NULL) + return sc->NIL; + + box->proc = proc; + return sc->vptr->mk_foreign_object (sc, &proc_object_vtable, box); +} + +static struct proc_object_box * +proc_unwrap (scheme *sc, pointer object) +{ + (void) sc; + + if (! is_foreign_object (object)) + return NULL; + + if (sc->vptr->get_foreign_object_vtable (object) != &proc_object_vtable) + return NULL; + + return sc->vptr->get_foreign_object_data (object); +} + +#define CONVERSION_proc(SC, X) proc_unwrap (SC, X) +#define IS_A_proc(SC, X) proc_unwrap (SC, X) + + +static pointer +do_process_spawn (scheme *sc, pointer args) { FFI_PROLOG (); pointer arguments; char **argv; size_t len; unsigned int flags; - + gnupg_process_t proc = NULL; estream_t infp; estream_t outfp; estream_t errfp; - pid_t pid; FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args); FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args); + flags |= (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE); FFI_ARGS_DONE_OR_RETURN (sc, args); err = ffi_list2argv (sc, arguments, &argv, &len); @@ -791,38 +855,55 @@ do_spawn_process (scheme *sc, pointer args) fprintf (stderr, "\n"); } - err = gnupg_spawn_process (argv[0], (const char **) &argv[1], - NULL, - flags, - &infp, &outfp, &errfp, &pid); + err = gnupg_process_spawn (argv[0], (const char **) &argv[1], + flags, NULL, NULL, &proc); + err = gnupg_process_get_streams (proc, 0, &infp, &outfp, &errfp); xfree (argv); -#define IMC(A, B) \ - _cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1) +#define IMP(A, B) \ + _cons (sc, proc_wrap (sc, (A)), (B), 1) #define IMS(A, B) \ _cons (sc, es_wrap (sc, (A)), (B), 1) FFI_RETURN_POINTER (sc, IMS (infp, IMS (outfp, IMS (errfp, - IMC (pid, sc->NIL))))); + IMP (proc, sc->NIL))))); #undef IMS #undef IMC } +static void +setup_std_fds (struct spawn_cb_arg *sca) +{ + int *std_fds = sca->arg; + +#ifdef HAVE_W32_SYSTEM + sca->hd[0] = std_fds[0] == -1? + INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[0]); + sca->hd[1] = std_fds[1] == -1? + INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[1]); + sca->hd[2] = std_fds[2] == -1? + INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[2]); +#else + sca->fds[0] = std_fds[0]; + sca->fds[1] = std_fds[1]; + sca->fds[2] = std_fds[2]; +#endif +} + static pointer -do_spawn_process_fd (scheme *sc, pointer args) +do_process_spawn_fd (scheme *sc, pointer args) { FFI_PROLOG (); pointer arguments; char **argv; size_t len; - int infd, outfd, errfd; - - pid_t pid; + int std_fds[3]; + gnupg_process_t proc = NULL; FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args); - FFI_ARG_OR_RETURN (sc, int, infd, number, args); - FFI_ARG_OR_RETURN (sc, int, outfd, number, args); - FFI_ARG_OR_RETURN (sc, int, errfd, number, args); + FFI_ARG_OR_RETURN (sc, int, std_fds[0], number, args); + FFI_ARG_OR_RETURN (sc, int, std_fds[1], number, args); + FFI_ARG_OR_RETURN (sc, int, std_fds[2], number, args); FFI_ARGS_DONE_OR_RETURN (sc, args); err = ffi_list2argv (sc, arguments, &argv, &len); @@ -839,107 +920,35 @@ do_spawn_process_fd (scheme *sc, pointer args) fprintf (stderr, "Executing:"); for (p = argv; *p; p++) fprintf (stderr, " '%s'", *p); - fprintf (stderr, "\n"); + fprintf (stderr, " (%d %d %d)\n", std_fds[0], std_fds[1], std_fds[2]); } - err = gnupg_spawn_process_fd (argv[0], (const char **) &argv[1], - infd, outfd, errfd, &pid); + err = gnupg_process_spawn (argv[0], (const char **) &argv[1], + 0, setup_std_fds, std_fds, &proc); xfree (argv); - FFI_RETURN_INT (sc, pid); + FFI_RETURN_POINTER (sc, proc_wrap (sc, proc)); } static pointer -do_wait_process (scheme *sc, pointer args) +do_process_wait (scheme *sc, pointer args) { FFI_PROLOG (); - const char *name; - pid_t pid; + struct proc_object_box *box; int hang; + int retcode = -1; - int retcode; - - FFI_ARG_OR_RETURN (sc, const char *, name, string, args); - FFI_ARG_OR_RETURN (sc, pid_t, pid, number, args); + FFI_ARG_OR_RETURN (sc, struct proc_object_box *, box, proc, args); FFI_ARG_OR_RETURN (sc, int, hang, bool, args); FFI_ARGS_DONE_OR_RETURN (sc, args); - err = gnupg_wait_process (name, pid, hang, &retcode); - if (err == GPG_ERR_GENERAL) - err = 0; /* Let the return code speak for itself. */ + err = gnupg_process_wait (box->proc, hang); + if (!err) + err = gnupg_process_ctl (box->proc, GNUPG_PROCESS_GET_EXIT_ID, &retcode); + if (err == GPG_ERR_TIMEOUT) + err = 0; FFI_RETURN_INT (sc, retcode); } - -static pointer -do_wait_processes (scheme *sc, pointer args) -{ - FFI_PROLOG (); - pointer list_names; - char **names; - pointer list_pids; - size_t i, count; - pid_t *pids; - int hang; - int *retcodes; - pointer retcodes_list = sc->NIL; - - FFI_ARG_OR_RETURN (sc, pointer, list_names, list, args); - FFI_ARG_OR_RETURN (sc, pointer, list_pids, list, args); - FFI_ARG_OR_RETURN (sc, int, hang, bool, args); - FFI_ARGS_DONE_OR_RETURN (sc, args); - - if (sc->vptr->list_length (sc, list_names) - != sc->vptr->list_length (sc, list_pids)) - return - sc->vptr->mk_string (sc, "length of first two arguments must match"); - - err = ffi_list2argv (sc, list_names, &names, &count); - if (err == gpg_error (GPG_ERR_INV_VALUE)) - return ffi_sprintf (sc, "%lu%s element of first argument is " - "neither string nor symbol", - (unsigned long) count, - ordinal_suffix ((int) count)); - if (err) - FFI_RETURN_ERR (sc, err); - - err = ffi_list2intv (sc, list_pids, (int **) &pids, &count); - if (err == gpg_error (GPG_ERR_INV_VALUE)) - return ffi_sprintf (sc, "%lu%s element of second argument is " - "not a number", - (unsigned long) count, - ordinal_suffix ((int) count)); - if (err) - FFI_RETURN_ERR (sc, err); - - retcodes = xtrycalloc (sizeof *retcodes, count); - if (retcodes == NULL) - { - xfree (names); - xfree (pids); - FFI_RETURN_ERR (sc, gpg_error_from_syserror ()); - } - - err = gnupg_wait_processes ((const char **) names, pids, count, hang, - retcodes); - if (err == GPG_ERR_GENERAL) - err = 0; /* Let the return codes speak. */ - if (err == GPG_ERR_TIMEOUT) - err = 0; /* We may have got some results. */ - - for (i = 0; i < count; i++) - retcodes_list = - (sc->vptr->cons) (sc, - sc->vptr->mk_integer (sc, - (long) retcodes[count-1-i]), - retcodes_list); - - xfree (names); - xfree (pids); - xfree (retcodes); - FFI_RETURN_POINTER (sc, retcodes_list); -} - - static pointer do_pipe (scheme *sc, pointer args) { @@ -1398,13 +1407,12 @@ ffi_init (scheme *sc, const char *argv0, const char *scriptname, ffi_define_function (sc, make_random_string); /* Process management. */ - ffi_define_function (sc, spawn_process); - ffi_define_function (sc, spawn_process_fd); - ffi_define_function (sc, wait_process); - ffi_define_function (sc, wait_processes); ffi_define_function (sc, pipe); ffi_define_function (sc, inbound_pipe); ffi_define_function (sc, outbound_pipe); + ffi_define_function (sc, process_spawn); + ffi_define_function (sc, process_spawn_fd); + ffi_define_function (sc, process_wait); /* estream functions. */ ffi_define_function_name (sc, "es-fclose", es_fclose); diff --git a/tests/gpgscm/t-child.scm b/tests/gpgscm/t-child.scm index fd1dcc3fe..461413b9c 100644 --- a/tests/gpgscm/t-child.scm +++ b/tests/gpgscm/t-child.scm @@ -69,37 +69,36 @@ (assert (string=? "" (:stderr r)))) (define (spawn what) - (spawn-process-fd what CLOSED_FD STDOUT_FILENO STDERR_FILENO)) + (process-spawn-fd what CLOSED_FD STDOUT_FILENO STDERR_FILENO)) -(let ((pid0 (spawn `(,(qualify "t-child") "return0"))) - (pid1 (spawn `(,(qualify "t-child") "return0")))) - (assert (equal? '(0 0) - (wait-processes '("child0" "child1") (list pid0 pid1) #t)))) +(let ((proc0 (spawn `(,(qualify "t-child") "return0"))) + (proc1 (spawn `(,(qualify "t-child") "return0")))) + (assert (= (process-wait proc0 #t) 0)) + (assert (= (process-wait proc1 #t) 0))) -(let ((pid0 (spawn `(,(qualify "t-child") "return1"))) - (pid1 (spawn `(,(qualify "t-child") "return0")))) - (assert (equal? '(1 0) - (wait-processes '("child0" "child1") (list pid0 pid1) #t)))) +(let ((proc0 (spawn `(,(qualify "t-child") "return1"))) + (proc1 (spawn `(,(qualify "t-child") "return0")))) + (assert (= (process-wait proc0 #t) 1)) + (assert (= (process-wait proc1 #t) 0))) -(let ((pid0 (spawn `(,(qualify "t-child") "return0"))) - (pid1 (spawn `(,(qualify "t-child") "return77"))) - (pid2 (spawn `(,(qualify "t-child") "return1")))) - (assert (equal? '(0 77 1) - (wait-processes '("child0" "child1" "child2") - (list pid0 pid1 pid2) #t)))) +(let ((proc0 (spawn `(,(qualify "t-child") "return0"))) + (proc1 (spawn `(,(qualify "t-child") "return77"))) + (proc2 (spawn `(,(qualify "t-child") "return1")))) + (assert (= (process-wait proc0 #t) 0)) + (assert (= (process-wait proc1 #t) 77)) + (assert (= (process-wait proc2 #t) 1))) (let* ((p (pipe)) - (pid0 (spawn-process-fd + (proc0 (process-spawn-fd `(,(qualify "t-child") "hello_stdout") CLOSED_FD (:write-end p) STDERR_FILENO)) (_ (close (:write-end p))) - (pid1 (spawn-process-fd + (proc1 (process-spawn-fd `(,(qualify "t-child") "cat") (:read-end p) STDOUT_FILENO STDERR_FILENO))) (close (:read-end p)) - (assert - (equal? '(0 0) - (wait-processes '("child0" "child1") (list pid0 pid1) #t)))) + (assert (= (process-wait proc0 #t) 0)) + (assert (= (process-wait proc1 #t) 0))) (echo " world.") (tr:do diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm index db1025bbb..6a11e55f1 100644 --- a/tests/gpgscm/tests.scm +++ b/tests/gpgscm/tests.scm @@ -81,7 +81,7 @@ ;; Process management. (define CLOSED_FD -1) (define (call-with-fds what infd outfd errfd) - (wait-process (stringify what) (spawn-process-fd what infd outfd errfd) #t)) + (process-wait (process-spawn-fd what infd outfd errfd) #t)) (define (call what) (call-with-fds what CLOSED_FD @@ -92,19 +92,19 @@ (define :stdin car) (define :stdout cadr) (define :stderr caddr) -(define :pid cadddr) +(define :proc cadddr) (define (call-with-io what in) - (let ((h (spawn-process what 0))) + (let ((h (process-spawn what 0))) (es-write (:stdin h) in) (es-fclose (:stdin h)) (let* ((out (es-read-all (:stdout h))) (err (es-read-all (:stderr h))) - (result (wait-process (car what) (:pid h) #t))) + (result (process-wait (:proc h) #t))) (es-fclose (:stdout h)) (es-fclose (:stderr h)) (if (> (*verbose*) 2) - (info "Child" (:pid h) "returned:" + (info "Child" (:proc h) "returned:" `((command ,(stringify what)) (status ,result) (stdout ,out) @@ -351,12 +351,8 @@ (define (dump) (write (list procs source sink producer)) (newline)) - (define (add-proc command pid) - (new (cons (list command pid) procs) source sink producer)) - (define (commands) - (map car procs)) - (define (pids) - (map cadr procs)) + (define (add-proc proc) + (new (cons proc procs) source sink producer)) (define (set-source source') (new procs source' sink producer)) (define (set-sink sink') @@ -367,17 +363,19 @@ (new procs source sink producer')))))) +(define (process-wait-list procs hang) + (map (lambda (p) (process-wait p hang)) procs)) + (define (pipe:do . commands) (let loop ((M (pipeM::new '() CLOSED_FD CLOSED_FD #f)) (cmds commands)) (if (null? cmds) (begin (if M::producer (M::producer)) (if (not (null? M::procs)) - (let* ((retcodes (wait-processes (map stringify (M::commands)) - (M::pids) #t)) - (results (map (lambda (p r) (append p (list r))) + (let* ((retcodes (process-wait-list M::procs #t)) + (results (map (lambda (p r) (cons p r)) M::procs retcodes)) - (failed (filter (lambda (x) (not (= 0 (caddr x)))) + (failed (filter (lambda (x) (not (= 0 (cdr x)))) results))) (if (not (null? failed)) (throw failed))))) ; xxx nicer reporting @@ -408,11 +406,11 @@ (define (pipe:spawn command) (lambda (M) (define (do-spawn M new-source) - (let ((pid (spawn-process-fd command M::source M::sink - (if (> (*verbose*) 0) - STDERR_FILENO CLOSED_FD))) + (let ((proc (process-spawn-fd command M::source M::sink + (if (> (*verbose*) 0) + STDERR_FILENO CLOSED_FD))) (M' (M::set-source new-source))) - (M'::add-proc command pid))) + (M'::add-proc proc))) (if (= CLOSED_FD M::sink) (let* ((p (pipe)) (M' (do-spawn (M::set-sink (:write-end p)) (:read-end p)))) @@ -568,8 +566,8 @@ (assert (= (length enqueued) (- i 1))) test))))) - (define (pid->test pid) - (let ((t (filter (lambda (x) (= pid x::pid)) procs))) + (define (proc->test proc) + (let ((t (filter (lambda (x) (eq? proc x::proc)) procs))) (if (null? t) #f (car t)))) (define (wait) (if (null? enqueued) @@ -587,7 +585,7 @@ (if (null? unfinished) (current-environment) (let ((names (map (lambda (t) t::name) unfinished)) - (pids (map (lambda (t) t::pid) unfinished)) + (procs (map (lambda (t) t::proc) unfinished)) (any #f)) (for-each (lambda (test retcode) @@ -597,8 +595,8 @@ (test::report) (sem::release!) (set! any #t))) - (map pid->test pids) - (wait-processes (map stringify names) pids hang)) + (map proc->test procs) + (process-wait-list procs hang)) ;; If some processes finished, try to start new ones. (let loop () @@ -682,7 +680,7 @@ (define (scm setup variant name path . args) ;; Start the process. (define (spawn-scm args' in out err) - (spawn-process-fd `(,*argv0* ,@(verbosity (*verbose*)) + (process-spawn-fd `(,*argv0* ,@(verbosity (*verbose*)) ,(locate-test (test-name path)) ,@(if setup (force setup) '()) ,@args' ,@args) in out err)) @@ -691,12 +689,12 @@ (define (binary setup name path . args) ;; Start the process. (define (spawn-binary args' in out err) - (spawn-process-fd `(,(test-name path) + (process-spawn-fd `(,(test-name path) ,@(if setup (force setup) '()) ,@args' ,@args) in out err)) (new #f name #f spawn-binary #f #f CLOSED_FD (expect-failure? name))) - (define (new variant name directory spawn pid retcode logfd expect-failure) + (define (new variant name directory spawn proc retcode logfd expect-failure) (package ;; XXX: OO glue. @@ -721,7 +719,7 @@ ;; Has the test been started yet? (define (started?) - (number? pid)) + proc) (define (open-log-file) (unless log-file-name @@ -738,26 +736,26 @@ (letfd ((log (open-log-file))) (with-working-directory directory (let* ((p (inbound-pipe)) - (pid' (spawn args 0 (:write-end p) (:write-end p)))) + (proc' (spawn args 0 (:write-end p) (:write-end p)))) (close (:write-end p)) (splice (:read-end p) STDERR_FILENO log) (close (:read-end p)) - (set! pid pid') - (set! retcode (wait-process name pid' #t))))) + (set! proc proc') + (set! retcode (process-wait proc' #t))))) (report) (current-environment)) (define (run-sync-quiet . args) (set-start-time!) (with-working-directory directory - (set! pid (spawn args CLOSED_FD CLOSED_FD CLOSED_FD))) - (set! retcode (wait-process name pid #t)) + (set! proc (spawn args CLOSED_FD CLOSED_FD CLOSED_FD))) + (set! retcode (process-wait proc #t)) (set-end-time!) (current-environment)) (define (run-async . args) (set-start-time!) (let ((log (open-log-file))) (with-working-directory directory - (set! pid (spawn args CLOSED_FD log log))) + (set! proc (spawn args CLOSED_FD log log))) (set! logfd log)) (current-environment)) (define (status) diff --git a/tests/openpgp/defs.scm b/tests/openpgp/defs.scm index bf3714f50..1ac25bf65 100644 --- a/tests/openpgp/defs.scm +++ b/tests/openpgp/defs.scm @@ -268,13 +268,14 @@ (define (gpg-pipe args0 args1 errfd) (lambda (source sink) (let* ((p (pipe)) - (task0 (spawn-process-fd `(,@GPG ,@args0) + (task0 (process-spawn-fd `(,@GPG ,@args0) source (:write-end p) errfd)) (_ (close (:write-end p))) - (task1 (spawn-process-fd `(,@GPG ,@args1) + (task1 (process-spawn-fd `(,@GPG ,@args1) (:read-end p) sink errfd))) (close (:read-end p)) - (wait-processes (list GPG GPG) (list task0 task1) #t)))) + (process-wait task0 #t) + (process-wait task1 #t)))) (setenv "GPG_AGENT_INFO" "" #t) (setenv "GNUPGHOME" (getcwd) #t) diff --git a/tests/tpm2dtests/defs.scm b/tests/tpm2dtests/defs.scm index 0fef71806..a913818f5 100644 --- a/tests/tpm2dtests/defs.scm +++ b/tests/tpm2dtests/defs.scm @@ -217,13 +217,14 @@ (define (gpg-pipe args0 args1 errfd) (lambda (source sink) (let* ((p (pipe)) - (task0 (spawn-process-fd `(,@GPG ,@args0) + (task0 (process-spawn-fd `(,@GPG ,@args0) source (:write-end p) errfd)) (_ (close (:write-end p))) - (task1 (spawn-process-fd `(,@GPG ,@args1) + (task1 (process-spawn-fd `(,@GPG ,@args1) (:read-end p) sink errfd))) (close (:read-end p)) - (wait-processes (list GPG GPG) (list task0 task1) #t)))) + (process-wait task0 #t) + (process-wait task1 #t)))) ;; ;; Do we have a software tpm diff --git a/tools/gpg-card.c b/tools/gpg-card.c index a94aabea9..919e61195 100644 --- a/tools/gpg-card.c +++ b/tools/gpg-card.c @@ -3641,7 +3641,7 @@ cmd_gpg (card_info_t info, char *argstr, int use_gpgsm) char **argarray; ccparray_t ccp; const char **argv = NULL; - pid_t pid; + gnupg_process_t proc; int i; if (!info) @@ -3669,15 +3669,15 @@ cmd_gpg (card_info_t info, char *argstr, int use_gpgsm) goto leave; } - err = gnupg_spawn_process (use_gpgsm? opt.gpgsm_program:opt.gpg_program, - argv, NULL, (GNUPG_SPAWN_KEEP_STDOUT - |GNUPG_SPAWN_KEEP_STDERR), - NULL, NULL, NULL, &pid); + err = gnupg_process_spawn (use_gpgsm? opt.gpgsm_program:opt.gpg_program, + argv, + (GNUPG_PROCESS_STDOUT_KEEP + | GNUPG_PROCESS_STDERR_KEEP), + NULL, NULL, &proc); if (!err) { - err = gnupg_wait_process (use_gpgsm? opt.gpgsm_program:opt.gpg_program, - pid, 1, NULL); - gnupg_release_process (pid); + err = gnupg_process_wait (proc, 1); + gnupg_process_release (proc); } diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 90f2f53d3..d6aa9d61b 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -744,7 +744,7 @@ gpg_agent_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[5]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -761,13 +761,13 @@ gpg_agent_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -777,7 +777,7 @@ scdaemon_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[9]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -805,13 +805,13 @@ scdaemon_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -822,7 +822,7 @@ tpm2daemon_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[9]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -850,13 +850,13 @@ tpm2daemon_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } #endif @@ -867,7 +867,7 @@ dirmngr_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[6]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -885,13 +885,13 @@ dirmngr_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -901,7 +901,7 @@ keyboxd_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[6]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -919,13 +919,13 @@ keyboxd_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -937,7 +937,7 @@ gc_component_launch (int component) const char *pgmname; const char *argv[6]; int i; - pid_t pid; + gnupg_process_t proc = NULL; if (component < 0) { @@ -985,9 +985,9 @@ gc_component_launch (int component) argv[i] = NULL; log_assert (i < DIM(argv)); - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s%s%s': %s", pgmname, @@ -995,7 +995,7 @@ gc_component_launch (int component) : component == GC_COMPONENT_KEYBOXD? " --keyboxd":"", " NOP", gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); return err; } @@ -1336,8 +1336,7 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) const char *pgmname; const char *argv[6]; int i; - pid_t pid; - int exitcode; + gnupg_process_t proc; estream_t errfp; error_line_t errlines; @@ -1370,22 +1369,28 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) result = 0; errlines = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, NULL, &errfp, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDERR_PIPE, + NULL, NULL, &proc); if (err) result |= 1; /* Program could not be run. */ else { + gnupg_process_get_streams (proc, 0, NULL, NULL, &errfp); errlines = collect_error_output (errfp, gc_component[component].name); - if (gnupg_wait_process (pgmname, pid, 1, &exitcode)) + if (!gnupg_process_wait (proc, 1)) { + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); if (exitcode == -1) result |= 1; /* Program could not be run or it terminated abnormally. */ - result |= 2; /* Program returned an error. */ + else if (exitcode) + result |= 2; /* Program returned an error. */ } - gnupg_release_process (pid); + gnupg_process_release (proc); es_fclose (errfp); } @@ -1725,8 +1730,7 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) const char *pgmname; const char *argv[2]; estream_t outfp; - int exitcode; - pid_t pid; + gnupg_process_t proc; known_option_t *known_option; gc_option_t *option; char *line = NULL; @@ -1759,14 +1763,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) /* First we need to read the option table from the program. */ argv[0] = "--dump-option-table"; argv[1] = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, &outfp, NULL, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDOUT_PIPE, + NULL, NULL, &proc); if (err) { gc_error (1, 0, "could not gather option table from '%s': %s", pgmname, gpg_strerror (err)); } + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); + read_line_parm.pgmname = pgmname; read_line_parm.fp = outfp; read_line_parm.line = line; @@ -1925,12 +1932,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) line_len = read_line_parm.line_len; log_assert (opt_table_used + pseudo_count == opt_info_used); + err = gnupg_process_wait (proc, 1); + if (!err) + { + int exitcode; - err = gnupg_wait_process (pgmname, pid, 1, &exitcode); - if (err) - gc_error (1, 0, "running %s failed (exitcode=%d): %s", - pgmname, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) + gc_error (1, 0, "running %s failed (exitcode=%d): %s", + pgmname, exitcode, gpg_strerror (err)); + } + gnupg_process_release (proc); /* Make the gpgrt option table and the internal option table available. */ gc_component[component].opt_table = opt_table; @@ -1940,14 +1952,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) /* Now read the default options. */ argv[0] = "--gpgconf-list"; argv[1] = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, &outfp, NULL, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDOUT_PIPE, + NULL, NULL, &proc); if (err) { gc_error (1, 0, "could not gather active options from '%s': %s", pgmname, gpg_strerror (err)); } + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); + while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0) { char *linep; @@ -2030,11 +2045,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) if (es_fclose (outfp)) gc_error (1, errno, "error closing %s", pgmname); - err = gnupg_wait_process (pgmname, pid, 1, &exitcode); - if (err) - gc_error (1, 0, "running %s failed (exitcode=%d): %s", - pgmname, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); + err = gnupg_process_wait (proc, 1); + if (!err) + { + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) + gc_error (1, 0, "running %s failed (exitcode=%d): %s", + pgmname, exitcode, gpg_strerror (err)); + } + gnupg_process_release (proc); /* At this point, we can parse the configuration file. */ diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 6dcdc9f3c..522ce517b 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -1173,17 +1173,17 @@ show_versions_via_dirmngr (estream_t fp) const char *pgmname; const char *argv[2]; estream_t outfp; - pid_t pid; + gnupg_process_t proc; char *line = NULL; size_t line_len = 0; ssize_t length; - int exitcode; pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR); argv[0] = "--gpgconf-versions"; argv[1] = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, &outfp, NULL, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDOUT_PIPE, + NULL, NULL, &proc); if (err) { log_error ("error spawning %s: %s", pgmname, gpg_strerror (err)); @@ -1191,6 +1191,7 @@ show_versions_via_dirmngr (estream_t fp) return; } + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0) { /* Strip newline and carriage return, if present. */ @@ -1211,14 +1212,17 @@ show_versions_via_dirmngr (estream_t fp) pgmname, gpg_strerror (err)); } - err = gnupg_wait_process (pgmname, pid, 1, &exitcode); - if (err) + err = gnupg_process_wait (proc, 1); + if (!err) { + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); log_error ("running %s failed (exitcode=%d): %s\n", pgmname, exitcode, gpg_strerror (err)); es_fprintf (fp, "[error: can't get further info]\n"); } - gnupg_release_process (pid); + gnupg_process_release (proc); xfree (line); } diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c index 26d37a332..e6f5b55a2 100644 --- a/tools/gpgtar-create.c +++ b/tools/gpgtar-create.c @@ -1069,7 +1069,7 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, estream_t files_from_stream = NULL; estream_t outstream = NULL; int eof_seen = 0; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; unsigned int skipped_open = 0; memset (scanctrl, 0, sizeof *scanctrl); @@ -1284,14 +1284,15 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, goto leave; } - err = gnupg_spawn_process (opt.gpg_program, argv, - except[0] == -1? NULL : except, - (GNUPG_SPAWN_KEEP_STDOUT - | GNUPG_SPAWN_KEEP_STDERR), - &outstream, NULL, NULL, &pid); + err = gnupg_process_spawn (opt.gpg_program, argv, + (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDOUT_KEEP + | GNUPG_PROCESS_STDERR_KEEP), + gnupg_spawn_helper, except, &proc); xfree (argv); if (err) goto leave; + gnupg_process_get_streams (proc, 0, &outstream, NULL, NULL); es_set_binary (outstream); } else if (opt.outfile) /* No crypto */ @@ -1330,23 +1331,25 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, write_progress (1, global_written_files, global_total_files); write_progress (0, global_written_data, global_total_data); - if (pid != (pid_t)(-1)) + if (proc) { - int exitcode; - err = es_fclose (outstream); outstream = NULL; if (err) log_error ("error closing pipe: %s\n", gpg_strerror (err)); - else + + err = gnupg_process_wait (proc, 1); + if (!err) { - err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); - if (err) + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) log_error ("running %s failed (exitcode=%d): %s", opt.gpg_program, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); - pid = (pid_t)(-1); } + gnupg_process_release (proc); + proc = NULL; } if (skipped_open) @@ -1359,7 +1362,7 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, if (!err) { gpg_error_t first_err; - if (outstream != es_stdout || pid != (pid_t)(-1)) + if (outstream != es_stdout) first_err = es_fclose (outstream); else first_err = es_fflush (outstream); diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c index 936d03e3e..be483f87c 100644 --- a/tools/gpgtar-extract.c +++ b/tools/gpgtar-extract.c @@ -339,7 +339,7 @@ gpgtar_extract (const char *filename, int decrypt) char *dirname = NULL; struct tarinfo_s tarinfo_buffer; tarinfo_t tarinfo = &tarinfo_buffer; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc; char *logfilename = NULL; unsigned long long notextracted; @@ -425,14 +425,14 @@ gpgtar_extract (const char *filename, int decrypt) goto leave; } - err = gnupg_spawn_process (opt.gpg_program, argv, - except[0] == -1? NULL : except, - ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN) - | GNUPG_SPAWN_KEEP_STDERR), - NULL, &stream, NULL, &pid); + err = gnupg_process_spawn (opt.gpg_program, argv, + ((filename ? 0 : GNUPG_PROCESS_STDIN_KEEP) + | GNUPG_PROCESS_STDOUT_PIPE), + gnupg_spawn_helper, except, &proc); xfree (argv); if (err) goto leave; + gnupg_process_get_streams (proc, 0, NULL, &stream, NULL); es_set_binary (stream); } else if (filename) @@ -472,23 +472,25 @@ gpgtar_extract (const char *filename, int decrypt) header = NULL; } - if (pid != (pid_t)(-1)) + if (proc) { - int exitcode; - err = es_fclose (stream); stream = NULL; if (err) log_error ("error closing pipe: %s\n", gpg_strerror (err)); - else + + err = gnupg_process_wait (proc, 1); + if (!err) { - err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); - if (err) + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) log_error ("running %s failed (exitcode=%d): %s", opt.gpg_program, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); - pid = (pid_t)(-1); } + gnupg_process_release (proc); + proc = NULL; } leave: diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c index c5bf25825..31bcd8d46 100644 --- a/tools/gpgtar-list.c +++ b/tools/gpgtar-list.c @@ -460,7 +460,7 @@ gpgtar_list (const char *filename, int decrypt) strlist_t extheader = NULL; struct tarinfo_s tarinfo_buffer; tarinfo_t tarinfo = &tarinfo_buffer; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer); @@ -503,14 +503,14 @@ gpgtar_list (const char *filename, int decrypt) goto leave; } - err = gnupg_spawn_process (opt.gpg_program, argv, - except[0] == -1? NULL : except, - ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN) - | GNUPG_SPAWN_KEEP_STDERR), - NULL, &stream, NULL, &pid); + err = gnupg_process_spawn (opt.gpg_program, argv, + ((filename ? 0 : GNUPG_PROCESS_STDIN_KEEP) + | GNUPG_PROCESS_STDOUT_PIPE), + gnupg_spawn_helper, except, &proc); xfree (argv); if (err) goto leave; + gnupg_process_get_streams (proc, 0, NULL, &stream, NULL); es_set_binary (stream); } else if (filename) /* No decryption requested. */ @@ -550,23 +550,24 @@ gpgtar_list (const char *filename, int decrypt) header = NULL; } - if (pid != (pid_t)(-1)) + if (proc) { - int exitcode; - err = es_fclose (stream); stream = NULL; if (err) log_error ("error closing pipe: %s\n", gpg_strerror (err)); - else + + err = gnupg_process_wait (proc, 1); + if (!err) { - err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); - if (err) - log_error ("running %s failed (exitcode=%d): %s", - opt.gpg_program, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); - pid = (pid_t)(-1); + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + log_error ("running %s failed (exitcode=%d): %s", + opt.gpg_program, exitcode, gpg_strerror (err)); } + gnupg_process_release (proc); + proc = NULL; } leave: