diff options
-rw-r--r-- | agent/genkey.c | 20 | ||||
-rw-r--r-- | common/asshelp.c | 12 | ||||
-rw-r--r-- | common/exechelp-posix.c | 679 | ||||
-rw-r--r-- | common/exechelp-w32.c | 757 | ||||
-rw-r--r-- | common/exechelp.h | 102 | ||||
-rw-r--r-- | common/exectool.c | 40 | ||||
-rw-r--r-- | dirmngr/ldap-wrapper.c | 80 | ||||
-rw-r--r-- | g10/photoid.c | 48 | ||||
-rw-r--r-- | g13/be-encfs.c | 65 | ||||
-rw-r--r-- | g13/g13.c | 1 | ||||
-rw-r--r-- | g13/mount.c | 3 | ||||
-rw-r--r-- | g13/runner.c | 36 | ||||
-rw-r--r-- | g13/runner.h | 2 | ||||
-rw-r--r-- | scd/app.c | 24 | ||||
-rw-r--r-- | tests/gpgscm/ffi.c | 222 | ||||
-rw-r--r-- | tests/gpgscm/t-child.scm | 39 | ||||
-rw-r--r-- | tests/gpgscm/tests.scm | 66 | ||||
-rw-r--r-- | tests/openpgp/defs.scm | 7 | ||||
-rw-r--r-- | tests/tpm2dtests/defs.scm | 7 | ||||
-rw-r--r-- | tools/gpg-card.c | 14 | ||||
-rw-r--r-- | tools/gpgconf-comp.c | 145 | ||||
-rw-r--r-- | tools/gpgconf.c | 20 | ||||
-rw-r--r-- | tools/gpgtar-create.c | 31 | ||||
-rw-r--r-- | tools/gpgtar-extract.c | 31 | ||||
-rw-r--r-- | tools/gpgtar-list.c | 33 |
25 files changed, 2058 insertions, 426 deletions
diff --git a/agent/genkey.c b/agent/genkey.c index eb6791dca..fb0084193 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,19 @@ 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 + | GNUPG_PROCESS_STDOUT_NULL + | GNUPG_PROCESS_STDERR_NULL), + 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 +165,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..b65a3686f 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -526,13 +526,11 @@ start_new_service (assuan_context_t *r_ctx, err = gnupg_spawn_process_detached (program? program : program_name, argv, 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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..b29e2f124 100644 --- a/common/exechelp-posix.c +++ b/common/exechelp-posix.c @@ -915,3 +915,682 @@ gnupg_kill_process (pid_t pid) kill (pid, SIGTERM); } } + +#include <sys/socket.h> + +struct gnupg_process { + const char *pgmname; + unsigned int terminated :1; /* or detached */ + unsigned int flags; + pid_t pid; + int fd_in; + int fd_out; + int fd_err; + int wstatus; +}; + +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 void +check_syscall_func (void) +{ + 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 +posix_open_null (int for_write) +{ + int fd; + + 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) + { + 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]. + */ + } + + /* 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); +} + +static gpg_err_code_t +spawn_detached (gnupg_process_t process, + 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; + + /* FIXME: Is this GnuPG specific or should we keep it. */ + if (getuid() != geteuid()) + { + xfree (process); + xfree (argv); + return GPG_ERR_BUG; + } + + if (access (pgmname, X_OK)) + { + ec = gpg_err_code_from_syserror (); + xfree (process); + xfree (argv); + return ec; + } + + 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)); + xfree (process); + xfree (argv); + return ec; + } + + if (!pid) + { + pid_t pid2; + struct spawn_cb_arg sca; + + if (setsid() == -1 || chdir ("/")) + _exit (1); + + pid2 = fork (); /* Double fork to let init take over the new child. */ + if (pid2 == (pid_t)(-1)) + _exit (1); + if (pid2) + _exit (0); /* Let the parent exit immediately. */ + + call_spawn_cb (&sca, -1, -1, -1, spawn_cb, spawn_cb_arg); + + my_exec (pgmname, argv, &sca); + /*NOTREACHED*/ + } + + pre_syscall (); + if (waitpid (pid, NULL, 0) == -1) + { + 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 (); + + process->pid = (pid_t)-1; + process->fd_in = -1; + process->fd_out = -1; + process->fd_err = -1; + process->wstatus = -1; + process->terminated = 1; + return 0; +} + +void +gnupg_spawn_helper (struct spawn_cb_arg *sca) +{ + int *user_except = sca->arg; +#ifdef HAVE_W32_SYSTEM + if (user_except[0] == -1) + sca->ask_inherit = 0; + else + sca->ask_inherit = 1; +#else + sca->except_fds = user_except; +#endif +} + +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]; + + 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_DETACHED)) + { + if ((flags & GNUPG_PROCESS_STDFDS_SETTING)) + { + xfree (process); + xfree (argv); + return GPG_ERR_INV_FLAG; + } + + *r_process = process; + return spawn_detached (process, pgmname, argv, spawn_cb, spawn_cb_arg); + } + + 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_NULL)) + { + fd_in[0] = -1; + fd_in[1] = -1; + } + else + { + fd_in[0] = 0; + 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_NULL)) + { + 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_NULL)) + { + fd_err[0] = -1; + fd_err[1] = -1; + } + else + { + fd_err[0] = -1; + fd_err[1] = 2; + } + + 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_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..219b7fe75 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 0x602 #endif #include <stdio.h> @@ -63,9 +65,11 @@ #include "util.h" #include "i18n.h" #include "sysutils.h" +#define NEED_STRUCT_SPAWN_CB_ARG #include "exechelp.h" #include <windows.h> +#include <processthreadsapi.h> /* Define to 1 do enable debugging. */ #define DEBUG_W32_SPAWN 0 @@ -1048,3 +1052,756 @@ gnupg_kill_process (pid_t pid) TerminateProcess (process, 1); } } + +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; + +/* 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) +{ + 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 +spawn_detached (gnupg_process_t process, + const char *pgmname, char *cmdline, + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) +{ + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFOEXW si; + int cr_flags; + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; + gpg_err_code_t ec; + int ret; + struct spawn_cb_arg sca; + + ec = gnupg_access (pgmname, X_OK); + if (ec) + { + xfree (process); + xfree (cmdline); + return ec; + } + + memset (&si, 0, sizeof si); + + sca.ask_inherit = FALSE; + 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); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Start the process. */ + si.StartupInfo.cb = sizeof (si); + si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_NEW_PROCESS_GROUP + | DETACHED_PROCESS); + + /* Take care: CreateProcessW may modify wpgmname */ + if (!(wpgmname = 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. */ + sca.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: %d\n", + (int)GetLastError ()); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + return GPG_ERR_GENERAL; + } + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + + /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + /* Note: AllowSetForegroundWindow doesn't make sense for background + process. */ + + CloseHandle (pi.hThread); + CloseHandle (pi.hProcess); + + process->hProcess = INVALID_HANDLE_VALUE; + process->hd_in = INVALID_HANDLE_VALUE; + process->hd_out = INVALID_HANDLE_VALUE; + process->hd_err = INVALID_HANDLE_VALUE; + process->exitcode = -1; + process->terminated = 1; + return 0; +} + + +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; + + check_syscall_func (); + + if (r_process) + *r_process = NULL; + + /* Build the command line. */ + ec = build_w32_commandline (pgmname, argv, &cmdline); + if (ec) + return ec; + + 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_DETACHED)) + { + if ((flags & GNUPG_PROCESS_STDFDS_SETTING)) + { + xfree (process); + xfree (cmdline); + return GPG_ERR_INV_FLAG; + } + + *r_process = process; + return spawn_detached (process, pgmname, cmdline, spawn_cb, spawn_cb_arg); + } + + 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_NULL)) + { + hd_in[0] = w32_open_null (0); + hd_in[1] = INVALID_HANDLE_VALUE; + } + else + { + hd_in[0] = GetStdHandle (STD_INPUT_HANDLE); + 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_NULL)) + { + hd_out[0] = INVALID_HANDLE_VALUE; + hd_out[1] = w32_open_null (1); + } + else + { + hd_out[0] = INVALID_HANDLE_VALUE; + hd_out[1] = GetStdHandle (STD_OUTPUT_HANDLE); + } + + 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_NULL)) + { + hd_err[0] = INVALID_HANDLE_VALUE; + hd_err[1] = w32_open_null (1); + } + else + { + hd_err[0] = INVALID_HANDLE_VALUE; + hd_err[1] = GetStdHandle (STD_ERROR_HANDLE); + } + + memset (&si, 0, sizeof si); + + sca.ask_inherit = FALSE; + 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; + } + } + + 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_NULL)) + 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_NULL)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || (flags & GNUPG_PROCESS_STDERR_NULL)) + CloseHandle (hd_err[1]); + xfree (wpgmname); + xfree (wcmdline); + 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); + sca.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. */ + sca.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_NULL)) + 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_NULL)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || (flags & GNUPG_PROCESS_STDERR_NULL)) + 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_NULL)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || (flags & GNUPG_PROCESS_STDOUT_NULL)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || (flags & GNUPG_PROCESS_STDERR_NULL)) + 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_ID: + { + int *r_id = va_arg (arg_ptr, int *); + + if (r_id == NULL) + return GPG_ERR_INV_VALUE; + + *r_id = (int)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..cd91d3e39 100644 --- a/common/exechelp.h +++ b/common/exechelp.h @@ -207,6 +207,108 @@ gpg_error_t gnupg_spawn_process_detached (const char *pgmname, const char *argv[], const char *envp[] ); + +/* 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 ask_inherit; + BOOL allow_foreground_window; + void *arg; +}; +#endif +#else +struct spawn_cb_arg { + int fds[3]; + int *except_fds; + void *arg; +}; +#endif + +/* Internal flag to ihnerit file descriptor/handle */ +#define GNUPG_PROCESS_INHERIT_FILE (1 << 0) + +#define GNUPG_PROCESS_DETACHED (1 << 1) + +/* 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_NULL (1 << 12) +#define GNUPG_PROCESS_STDOUT_NULL (1 << 13) +#define GNUPG_PROCESS_STDERR_NULL (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_NULL \ + | GNUPG_PROCESS_STDOUT_NULL | GNUPG_PROCESS_STDERR_NULL) + +#define GNUPG_PROCESS_STREAM_NONBLOCK (1 << 16) + +/* Spawn helper. */ +void gnupg_spawn_helper (struct spawn_cb_arg *sca); + +/* 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); + +/* 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); + +/* 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); + +enum gnupg_process_requests + { + /* Portable requests */ + GNUPG_PROCESS_NOP = 0, + GNUPG_PROCESS_GET_ID = 1, + GNUPG_PROCESS_GET_EXIT_ID = 2, + + /* POSIX only */ + GNUPG_PROCESS_GET_PID = 16, + GNUPG_PROCESS_GET_WSTATUS = 17, + GNUPG_PROCESS_KILL = 18, + + /* 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 + }; + +/* 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); + +/* 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); + +/* 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..9a8e61c20 100644 --- a/common/exectool.c +++ b/common/exectool.c @@ -38,10 +38,14 @@ #include <gpg-error.h> #include <assuan.h> + #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 + : GNUPG_PROCESS_STDIN_NULL) + | 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..096642fda 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)); + (int)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"), (int)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); + (int)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; + (int)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)); + (int)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); + (int)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, + (int)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, + (int)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,23 @@ 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_STDIN_NULL + | 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_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 +900,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); + (int)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..6f04f5ea2 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -594,34 +594,37 @@ 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 | GNUPG_PROCESS_STDOUT_NULL + | GNUPG_PROCESS_STDERR_NULL), + 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 +692,15 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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..9adb1e092 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,11 @@ 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 | GNUPG_PROCESS_STDIN_NULL + | GNUPG_PROCESS_STDOUT_NULL + | GNUPG_PROCESS_STDERR_NULL), + NULL, NULL, NULL); if (err) log_error ("failed to run '%s': %s\n", pgmname, gpg_strerror (err)); @@ -218,12 +222,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 +249,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 +261,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_STDOUT_NULL + | 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; @@ -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, @@ -2316,6 +2316,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) { char *homestr, *envstr; @@ -2342,12 +2354,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); @@ -2364,7 +2373,12 @@ 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 + | GNUPG_PROCESS_STDIN_NULL + | GNUPG_PROCESS_STDOUT_NULL + | GNUPG_PROCESS_STDERR_NULL), + 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. */ - - 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. */ + 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; /* 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); + err = 0; - xfree (names); - xfree (pids); - xfree (retcodes); - FFI_RETURN_POINTER (sc, retcodes_list); + FFI_RETURN_INT (sc, retcode); } - 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..3034a14c5 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,13 @@ 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_STDIN_NULL, 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..f89ab3b9c 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,17 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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 +781,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 +809,17 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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 +830,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 +858,17 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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 +879,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 +897,17 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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 +917,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 +935,17 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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 +957,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 +1005,13 @@ 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, + (GNUPG_PROCESS_STDIN_NULL + |GNUPG_PROCESS_STDOUT_NULL + |GNUPG_PROCESS_STDERR_NULL), + 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 +1019,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 +1360,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 +1393,30 @@ 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_STDIN_NULL + | GNUPG_PROCESS_STDOUT_NULL + | 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 +1756,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 +1789,19 @@ 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_STDIN_NULL + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_NULL), + 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 +1960,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 +1980,19 @@ 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_STDIN_NULL + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_NULL), + 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 +2075,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 4afc4e9fd..bb9ac5419 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -1173,17 +1173,19 @@ 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_STDIN_NULL + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_NULL), + NULL, NULL, &proc); if (err) { log_error ("error spawning %s: %s", pgmname, gpg_strerror (err)); @@ -1191,6 +1193,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 +1214,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 f52295b5c..ebcfc5229 100644 --- a/tools/gpgtar-create.c +++ b/tools/gpgtar-create.c @@ -994,7 +994,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; memset (scanctrl, 0, sizeof *scanctrl); scanctrl->flist_tail = &scanctrl->flist; @@ -1197,14 +1197,13 @@ 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_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 */ @@ -1240,30 +1239,32 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, goto leave; - 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; } leave: 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 bc2e672de..9d5abd33d 100644 --- a/tools/gpgtar-extract.c +++ b/tools/gpgtar-extract.c @@ -324,7 +324,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; @@ -410,14 +410,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 ? GNUPG_PROCESS_STDIN_NULL : 0) + | 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) @@ -457,26 +457,27 @@ 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: free_strlist (extheader); xfree (header); diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c index c5bf25825..6d824d35c 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 ? GNUPG_PROCESS_STDIN_NULL : 0) + | 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: |