aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/asshelp.c12
-rw-r--r--common/exechelp-posix.c672
-rw-r--r--common/exechelp-w32.c768
-rw-r--r--common/exechelp.h102
-rw-r--r--common/exectool.c40
5 files changed, 1573 insertions, 21 deletions
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..af79aab3e 100644
--- a/common/exechelp-posix.c
+++ b/common/exechelp-posix.c
@@ -915,3 +915,675 @@ 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;
+ sca->except_fds = user_except;
+}
+
+gpg_err_code_t
+gnupg_process_spawn (const char *pgmname, const char *argv1[],
+ unsigned int flags,
+ void (*spawn_cb) (struct spawn_cb_arg *),
+ void *spawn_cb_arg,
+ gnupg_process_t *r_process)
+{
+ gpg_err_code_t ec;
+ gnupg_process_t process;
+ int fd_in[2];
+ int fd_out[2];
+ int fd_err[2];
+ pid_t pid;
+ const char **argv;
+ int i, j;
+
+ check_syscall_func ();
+
+ if (r_process)
+ *r_process = NULL;
+
+ /* Create the command line argument array. */
+ i = 0;
+ if (argv1)
+ while (argv1[i])
+ i++;
+ argv = xtrycalloc (i+2, sizeof *argv);
+ if (!argv)
+ return gpg_err_code_from_syserror ();
+ argv[0] = strrchr (pgmname, '/');
+ if (argv[0])
+ argv[0]++;
+ else
+ argv[0] = pgmname;
+
+ if (argv1)
+ for (i=0, j=1; argv1[i]; i++, j++)
+ argv[j] = argv1[i];
+
+ 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..adde71862 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,767 @@ 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;
+}
+
+
+void
+gnupg_spawn_helper (struct spawn_cb_arg *sca)
+{
+ int *user_except = sca->arg;
+
+ if (user_except[0] == -1)
+ sca->ask_inherit = 0;
+ else
+ sca->ask_inherit = 1;
+}
+
+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);