aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/genkey.c18
-rw-r--r--common/asshelp.c14
-rw-r--r--common/exechelp-posix.c993
-rw-r--r--common/exechelp-w32.c1206
-rw-r--r--common/exechelp.h231
-rw-r--r--common/exectool.c40
-rw-r--r--dirmngr/ldap-wrapper.c79
-rw-r--r--g10/photoid.c42
-rw-r--r--g13/be-encfs.c63
-rw-r--r--g13/g13.c1
-rw-r--r--g13/mount.c3
-rw-r--r--g13/runner.c36
-rw-r--r--g13/runner.h2
-rw-r--r--scd/app.c21
-rw-r--r--tests/gpgscm/ffi.c222
-rw-r--r--tests/gpgscm/t-child.scm39
-rw-r--r--tests/gpgscm/tests.scm66
-rw-r--r--tests/openpgp/defs.scm7
-rw-r--r--tests/tpm2dtests/defs.scm7
-rw-r--r--tools/gpg-card.c16
-rw-r--r--tools/gpgconf-comp.c115
-rw-r--r--tools/gpgconf.c18
-rw-r--r--tools/gpgtar-create.c33
-rw-r--r--tools/gpgtar-extract.c30
-rw-r--r--tools/gpgtar-list.c33
25 files changed, 1825 insertions, 1510 deletions
diff --git a/agent/genkey.c b/agent/genkey.c
index 7660443ca..563407253 100644
--- a/agent/genkey.c
+++ b/agent/genkey.c
@@ -99,7 +99,7 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags)
const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN);
estream_t stream_to_check_pattern = NULL;
const char *argv[10];
- pid_t pid;
+ gnupg_process_t proc;
int result, i;
const char *pattern;
char *patternfname;
@@ -142,11 +142,17 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags)
argv[i] = NULL;
log_assert (i < sizeof argv);
- if (gnupg_spawn_process (pgmname, argv, NULL, 0,
- &stream_to_check_pattern, NULL, NULL, &pid))
+ if (gnupg_process_spawn (pgmname, argv,
+ GNUPG_PROCESS_STDIN_PIPE,
+ NULL, NULL, &proc))
result = 1; /* Execute error - assume password should no be used. */
else
{
+ int status;
+
+ gnupg_process_get_streams (proc, 0, &stream_to_check_pattern,
+ NULL, NULL);
+
es_set_binary (stream_to_check_pattern);
if (es_fwrite (pw, strlen (pw), 1, stream_to_check_pattern) != 1)
{
@@ -157,11 +163,13 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags)
else
es_fflush (stream_to_check_pattern);
es_fclose (stream_to_check_pattern);
- if (gnupg_wait_process (pgmname, pid, 1, NULL))
+ gnupg_process_wait (proc, 1);
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status);
+ if (status)
result = 1; /* Helper returned an error - probably a match. */
else
result = 0; /* Success; i.e. no match. */
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
}
xfree (patternfname);
diff --git a/common/asshelp.c b/common/asshelp.c
index eb3e41bf5..8b3df2a4b 100644
--- a/common/asshelp.c
+++ b/common/asshelp.c
@@ -523,16 +523,12 @@ start_new_service (assuan_context_t *r_ctx,
&& assuan_socket_connect (ctx, sockname, 0, connect_flags))
{
#ifdef HAVE_W32_SYSTEM
- err = gnupg_spawn_process_detached (program? program : program_name,
- argv, NULL);
+ err = gnupg_process_spawn (program? program : program_name, argv,
+ GNUPG_PROCESS_DETACHED,
+ NULL, NULL, NULL);
#else /*!W32*/
- pid_t pid;
-
- err = gnupg_spawn_process_fd (program? program : program_name,
- argv, -1, -1, -1, &pid);
- if (!err)
- err = gnupg_wait_process (program? program : program_name,
- pid, 1, NULL);
+ err = gnupg_process_spawn (program? program : program_name, argv,
+ 0, NULL, NULL, NULL);
#endif /*!W32*/
if (err)
log_error ("failed to start %s '%s': %s\n",
diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c
index fa613449d..7b20a3796 100644
--- a/common/exechelp-posix.c
+++ b/common/exechelp-posix.c
@@ -273,73 +273,6 @@ get_all_open_fds (void)
}
-/* The exec core used right after the fork. This will never return. */
-static void
-do_exec (const char *pgmname, const char *argv[],
- int fd_in, int fd_out, int fd_err,
- int *except, unsigned int flags)
-{
- char **arg_list;
- int i, j;
- int fds[3];
- int nodevnull[3];
-
- fds[0] = fd_in;
- fds[1] = fd_out;
- fds[2] = fd_err;
-
- nodevnull[0] = !!(flags & GNUPG_SPAWN_KEEP_STDIN);
- nodevnull[1] = !!(flags & GNUPG_SPAWN_KEEP_STDOUT);
- nodevnull[2] = !!(flags & GNUPG_SPAWN_KEEP_STDERR);
-
- /* Create the command line argument array. */
- i = 0;
- if (argv)
- while (argv[i])
- i++;
- arg_list = xcalloc (i+2, sizeof *arg_list);
- arg_list[0] = strrchr (pgmname, '/');
- if (arg_list[0])
- arg_list[0]++;
- else
- arg_list[0] = xstrdup (pgmname);
- if (argv)
- for (i=0,j=1; argv[i]; i++, j++)
- arg_list[j] = (char*)argv[i];
-
- /* Assign /dev/null to unused FDs. */
- for (i=0; i <= 2; i++)
- {
- if (nodevnull[i])
- continue;
- if (fds[i] == -1)
- {
- fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY);
- if (fds[i] == -1)
- log_fatal ("failed to open '%s': %s\n",
- "/dev/null", strerror (errno));
- }
- }
-
- /* Connect the standard files. */
- for (i=0; i <= 2; i++)
- {
- if (nodevnull[i])
- continue;
- if (fds[i] != i && dup2 (fds[i], i) == -1)
- log_fatal ("dup2 std%s failed: %s\n",
- i==0?"in":i==1?"out":"err", strerror (errno));
- }
-
- /* Close all other files. */
- close_all_fds (3, except);
-
- execv (pgmname, arg_list);
- /* No way to print anything, as we have closed all streams. */
- _exit (127);
-}
-
-
static gpg_error_t
do_create_pipe (int filedes[2])
{
@@ -431,487 +364,669 @@ gnupg_close_pipe (int fd)
close (fd);
}
+#include <sys/socket.h>
-/* Fork and exec the PGMNAME, see exechelp.h for details. */
-gpg_error_t
-gnupg_spawn_process (const char *pgmname, const char *argv[],
- int *except, unsigned int flags,
- estream_t *r_infp,
- estream_t *r_outfp,
- estream_t *r_errfp,
- pid_t *pid)
-{
- gpg_error_t err;
- int inpipe[2] = {-1, -1};
- int outpipe[2] = {-1, -1};
- int errpipe[2] = {-1, -1};
- estream_t infp = NULL;
- estream_t outfp = NULL;
- estream_t errfp = NULL;
- int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+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;
+};
- if (r_infp)
- *r_infp = NULL;
- if (r_outfp)
- *r_outfp = NULL;
- if (r_errfp)
- *r_errfp = NULL;
- *pid = (pid_t)(-1); /* Always required. */
+static int gnupg_process_syscall_func_initialized;
- if (r_infp)
- {
- err = create_pipe_and_estream (inpipe, &infp, 1, nonblock);
- if (err)
- return err;
- }
+/* Functions called before and after blocking syscalls. */
+static void (*pre_syscall_func) (void);
+static void (*post_syscall_func) (void);
- if (r_outfp)
+static void
+check_syscall_func (void)
+{
+ if (!gnupg_process_syscall_func_initialized)
{
- err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock);
- if (err)
- {
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != -1)
- close (inpipe[1]);
- if (inpipe[0] != -1)
- close (inpipe[0]);
-
- return err;
- }
+ gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func);
+ gnupg_process_syscall_func_initialized = 1;
}
+}
- if (r_errfp)
- {
- err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock);
- if (err)
- {
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != -1)
- close (inpipe[1]);
- if (inpipe[0] != -1)
- close (inpipe[0]);
-
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != -1)
- close (outpipe[0]);
- if (outpipe[1] != -1)
- close (outpipe[1]);
-
- return err;
- }
- }
+static void
+pre_syscall (void)
+{
+ if (pre_syscall_func)
+ pre_syscall_func ();
+}
+
+static void
+post_syscall (void)
+{
+ if (post_syscall_func)
+ post_syscall_func ();
+}
- *pid = fork ();
- if (*pid == (pid_t)(-1))
- {
- err = my_error_from_syserror ();
- log_error (_("error forking process: %s\n"), gpg_strerror (err));
-
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != -1)
- close (inpipe[1]);
- if (inpipe[0] != -1)
- close (inpipe[0]);
-
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != -1)
- close (outpipe[0]);
- if (outpipe[1] != -1)
- close (outpipe[1]);
-
- if (errfp)
- es_fclose (errfp);
- else if (errpipe[0] != -1)
- close (errpipe[0]);
- if (errpipe[1] != -1)
- close (errpipe[1]);
- return err;
- }
+static gpg_err_code_t
+do_create_socketpair (int filedes[2])
+{
+ gpg_error_t err = 0;
- if (!*pid)
+ pre_syscall ();
+ if (socketpair (AF_LOCAL, SOCK_STREAM, 0, filedes) == -1)
{
- /* This is the child. */
- gcry_control (GCRYCTL_TERM_SECMEM);
- es_fclose (infp);
- es_fclose (outfp);
- es_fclose (errfp);
- do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
- except, flags);
- /*NOTREACHED*/
+ err = gpg_err_code_from_syserror ();
+ filedes[0] = filedes[1] = -1;
}
+ post_syscall ();
- /* This is the parent. */
- if (inpipe[0] != -1)
- close (inpipe[0]);
- if (outpipe[1] != -1)
- close (outpipe[1]);
- if (errpipe[1] != -1)
- close (errpipe[1]);
+ return err;
+}
- if (r_infp)
- *r_infp = infp;
- if (r_outfp)
- *r_outfp = outfp;
- if (r_errfp)
- *r_errfp = errfp;
+static int
+posix_open_null (int for_write)
+{
+ int fd;
- return 0;
+ 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;
-/* Simplified version of gnupg_spawn_process. This function forks and
- then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
- and ERRFD to stderr (any of them may be -1 to connect them to
- /dev/null). The arguments for the process are expected in the NULL
- terminated array ARGV. The program name itself should not be
- included there. Calling gnupg_wait_process is required.
+ /* Assign /dev/null to unused FDs. */
+ for (i = 0; i <= 2; i++)
+ if (sca->fds[i] == -1)
+ sca->fds[i] = posix_open_null (i);
- Returns 0 on success or an error code. */
-gpg_error_t
-gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
- int infd, int outfd, int errfd, pid_t *pid)
+ /* 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 (const char *pgmname, const char *argv[],
+ void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg)
{
- gpg_error_t err;
+ gpg_err_code_t ec;
+ pid_t pid;
- *pid = fork ();
- if (*pid == (pid_t)(-1))
+ /* FIXME: Is this GnuPG specific or should we keep it. */
+ if (getuid() != geteuid())
{
- err = my_error_from_syserror ();
- log_error (_("error forking process: %s\n"), strerror (errno));
- return err;
+ xfree (argv);
+ return GPG_ERR_BUG;
}
- if (!*pid)
+ if (access (pgmname, X_OK))
{
- gcry_control (GCRYCTL_TERM_SECMEM);
- /* Run child. */
- do_exec (pgmname, argv, infd, outfd, errfd, NULL, 0);
- /*NOTREACHED*/
+ ec = gpg_err_code_from_syserror ();
+ xfree (argv);
+ return ec;
}
- return 0;
-}
-
-
-
+ 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 (argv);
+ return ec;
+ }
-/* Waiting for child processes.
+ if (!pid)
+ {
+ pid_t pid2;
+ struct spawn_cb_arg sca;
- waitpid(2) may return information about terminated children that we
- did not yet request, and there is no portable way to wait for a
- specific set of children.
+ if (setsid() == -1 || chdir ("/"))
+ _exit (1);
- As a workaround, we store the results of children for later use.
+ 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. */
- XXX: This assumes that PIDs are not reused too quickly. */
+ call_spawn_cb (&sca, -1, -1, -1, spawn_cb, spawn_cb_arg);
-struct terminated_child
-{
- pid_t pid;
- int exitcode;
- struct terminated_child *next;
-};
+ my_exec (pgmname, argv, &sca);
+ /*NOTREACHED*/
+ }
-struct terminated_child *terminated_children;
+ 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 ();
+ return 0;
+}
-static gpg_error_t
-store_result (pid_t pid, int exitcode)
+void
+gnupg_spawn_helper (struct spawn_cb_arg *sca)
{
- struct terminated_child *c;
+ int *user_except = sca->arg;
+ sca->except_fds = user_except;
+}
- c = xtrymalloc (sizeof *c);
- if (c == NULL)
- return gpg_err_code_from_syserror ();
+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;
- c->pid = pid;
- c->exitcode = exitcode;
- c->next = terminated_children;
- terminated_children = c;
+ check_syscall_func ();
- return 0;
-}
+ 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;
-static int
-get_result (pid_t pid, int *r_exitcode)
-{
- struct terminated_child *c, **prevp;
+ if (argv1)
+ for (i=0, j=1; argv1[i]; i++, j++)
+ argv[j] = argv1[i];
- for (prevp = &terminated_children, c = terminated_children;
- c;
- prevp = &c->next, c = c->next)
- if (c->pid == pid)
- {
- *prevp = c->next;
- *r_exitcode = c->exitcode;
- xfree (c);
- return 1;
- }
+ if ((flags & GNUPG_PROCESS_DETACHED))
+ {
+ if ((flags & GNUPG_PROCESS_STDFDS_SETTING))
+ {
+ xfree (argv);
+ return GPG_ERR_INV_FLAG;
+ }
- return 0;
-}
+ /* In detached case, it must be no R_PROCESS. */
+ if (r_process)
+ {
+ xfree (argv);
+ return GPG_ERR_INV_ARG;
+ }
+ return spawn_detached (pgmname, argv, spawn_cb, spawn_cb_arg);
+ }
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
-{
- gpg_err_code_t ec;
- int i, status;
+ process = xtrycalloc (1, sizeof (struct gnupg_process));
+ if (process == NULL)
+ {
+ xfree (argv);
+ return gpg_err_code_from_syserror ();
+ }
- if (r_exitcode)
- *r_exitcode = -1;
+ process->pgmname = pgmname;
+ process->flags = flags;
- if (pid == (pid_t)(-1))
- return gpg_error (GPG_ERR_INV_VALUE);
+ if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR))
+ {
+ ec = do_create_socketpair (fd_in);
+ if (ec)
+ {
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
+ fd_out[0] = dup (fd_in[0]);
+ fd_out[1] = dup (fd_in[1]);
+ }
+ else
+ {
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ {
+ ec = do_create_pipe (fd_in);
+ if (ec)
+ {
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDIN_KEEP))
+ {
+ fd_in[0] = 0;
+ fd_in[1] = -1;
+ }
+ else
+ {
+ fd_in[0] = -1;
+ fd_in[1] = -1;
+ }
-#ifdef USE_NPTH
- i = npth_waitpid (pid, &status, hang? 0:WNOHANG);
-#else
- while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
- && errno == EINTR);
-#endif
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ {
+ ec = do_create_pipe (fd_out);
+ if (ec)
+ {
+ if (fd_in[0] >= 0 && fd_in[0] != 0)
+ close (fd_in[0]);
+ if (fd_in[1] >= 0)
+ close (fd_in[1]);
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDOUT_KEEP))
+ {
+ fd_out[0] = -1;
+ fd_out[1] = 1;
+ }
+ else
+ {
+ fd_out[0] = -1;
+ fd_out[1] = -1;
+ }
+ }
- if (i == (pid_t)(-1))
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
{
- ec = gpg_err_code_from_errno (errno);
- log_error (_("waiting for process %d to terminate failed: %s\n"),
- (int)pid, strerror (errno));
+ 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 (!i)
+ else if ((flags & GNUPG_PROCESS_STDERR_KEEP))
{
- ec = GPG_ERR_TIMEOUT; /* Still running. */
+ fd_err[0] = -1;
+ fd_err[1] = 2;
}
- else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
+ else
{
- log_error (_("error running '%s': probably not installed\n"), pgmname);
- ec = GPG_ERR_CONFIGURATION;
+ fd_err[0] = -1;
+ fd_err[1] = -1;
}
- else if (WIFEXITED (status) && WEXITSTATUS (status))
+
+ pre_syscall ();
+ pid = fork ();
+ post_syscall ();
+ if (pid == (pid_t)(-1))
{
- if (!r_exitcode)
- log_error (_("error running '%s': exit status %d\n"), pgmname,
- WEXITSTATUS (status));
- else
- *r_exitcode = WEXITSTATUS (status);
- ec = GPG_ERR_GENERAL;
+ 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;
}
- else if (!WIFEXITED (status))
+
+ if (!pid)
{
- log_error (_("error running '%s': terminated\n"), pgmname);
- ec = GPG_ERR_GENERAL;
+ 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*/
}
- else
+
+ 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)
{
- if (r_exitcode)
- *r_exitcode = 0;
- ec = 0;
+ ec = gnupg_process_wait (process, 1);
+ gnupg_process_release (process);
+ return ec;
}
- return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+ *r_process = process;
+ return 0;
}
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
- int hang, int *r_exitcodes)
+static gpg_err_code_t
+process_kill (gnupg_process_t process, int sig)
{
gpg_err_code_t ec = 0;
- size_t i, left;
- int *dummy = NULL;
+ pid_t pid = process->pid;
- if (r_exitcodes == NULL)
+ 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)
{
- dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count);
- if (dummy == NULL)
- return gpg_err_code_from_syserror ();
+ *r_fd_in = process->fd_in;
+ process->fd_in = -1;
}
-
- for (i = 0, left = count; i < count; i++)
+ if (r_fd_out)
{
- int status = -1;
+ *r_fd_out = process->fd_out;
+ process->fd_out = -1;
+ }
+ if (r_fd_err)
+ {
+ *r_fd_err = process->fd_err;
+ process->fd_err = -1;
+ }
- /* Skip invalid PID. */
- if (pids[i] == (pid_t)(-1))
- {
- r_exitcodes[i] = -1;
- left -= 1;
- continue;
- }
+ return 0;
+}
- /* See if there was a previously stored result for this pid. */
- if (get_result (pids[i], &status))
- left -= 1;
+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;
- r_exitcodes[i] = status;
+ 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;
+}
- while (left > 0)
+static gpg_err_code_t
+process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr)
+{
+ switch (request)
{
- pid_t pid;
- int status;
+ case GNUPG_PROCESS_NOP:
+ return 0;
-#ifdef USE_NPTH
- pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG);
-#else
- while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1)
- && errno == EINTR);
-#endif
+ case GNUPG_PROCESS_GET_PROC_ID:
+ {
+ int *r_id = va_arg (arg_ptr, int *);
- if (pid == (pid_t)(-1))
- {
- ec = gpg_err_code_from_errno (errno);
- log_error (_("waiting for processes to terminate failed: %s\n"),
- strerror (errno));
- break;
- }
- else if (!pid)
- {
- ec = GPG_ERR_TIMEOUT; /* Still running. */
- break;
- }
- else
- {
- for (i = 0; i < count; i++)
- if (pid == pids[i])
- break;
+ if (r_id == NULL)
+ return GPG_ERR_INV_VALUE;
- if (i == count)
- {
- /* No match, store this result. */
- ec = store_result (pid, status);
- if (ec)
- break;
- continue;
- }
+ *r_id = (int)process->pid;
+ return 0;
+ }
- /* Process PIDS[i] died. */
- if (r_exitcodes[i] != (pid_t) -1)
- {
- log_error ("PID %d was reused", pid);
- ec = GPG_ERR_GENERAL;
- break;
- }
+ case GNUPG_PROCESS_GET_EXIT_ID:
+ {
+ int status = process->wstatus;
+ int *r_exit_status = va_arg (arg_ptr, int *);
- left -= 1;
- r_exitcodes[i] = status;
- }
- }
+ if (!process->terminated)
+ return GPG_ERR_UNFINISHED;
- for (i = 0; i < count; i++)
- {
- if (r_exitcodes[i] == -1)
- continue;
+ if (WIFEXITED (status))
+ {
+ if (r_exit_status)
+ *r_exit_status = WEXITSTATUS (status);
+ }
+ else
+ *r_exit_status = -1;
- if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
- {
- log_error (_("error running '%s': probably not installed\n"),
- pgmnames[i]);
- ec = GPG_ERR_CONFIGURATION;
- }
- else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
- {
- if (dummy)
- log_error (_("error running '%s': exit status %d\n"),
- pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
- else
- r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
- ec = GPG_ERR_GENERAL;
- }
- else if (!WIFEXITED (r_exitcodes[i]))
- {
- log_error (_("error running '%s': terminated\n"), pgmnames[i]);
- ec = GPG_ERR_GENERAL;
- }
+ 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;
}
- xfree (dummy);
- return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+ return GPG_ERR_UNKNOWN_COMMAND;
}
-
-
-void
-gnupg_release_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_ctl (gnupg_process_t process, unsigned int request, ...)
{
- (void)pid;
-}
+ 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;
+}
-/* Spawn a new process and immediately detach from it. The name of
- the program to exec is PGMNAME and its arguments are in ARGV (the
- programname is automatically passed as first argument).
- Environment strings in ENVP are set. An error is returned if
- pgmname is not executable; to make this work it is necessary to
- provide an absolute file name. All standard file descriptors are
- connected to /dev/null. */
-gpg_error_t
-gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
- const char *envp[] )
+gpg_err_code_t
+gnupg_process_wait (gnupg_process_t process, int hang)
{
gpg_err_code_t ec;
+ int status;
pid_t pid;
- int i;
- if (getuid() != geteuid())
- return my_error (GPG_ERR_BUG);
+ if (process->terminated)
+ /* Already terminated. */
+ return 0;
- if ((ec = gnupg_access (pgmname, X_OK)))
- return gpg_err_make (default_errsource, ec);
+ pre_syscall ();
+ while ((pid = waitpid (process->pid, &status, hang? 0: WNOHANG))
+ == (pid_t)(-1) && errno == EINTR);
+ post_syscall ();
- pid = fork ();
if (pid == (pid_t)(-1))
{
- log_error (_("error forking process: %s\n"), strerror (errno));
- return my_error_from_syserror ();
+ ec = gpg_err_code_from_syserror ();
+ log_error (_("waiting for process %d to terminate failed: %s\n"),
+ (int)pid, gpg_strerror (ec));
}
- if (!pid)
+ else if (!pid)
{
- pid_t pid2;
-
- gcry_control (GCRYCTL_TERM_SECMEM);
- if (setsid() == -1 || chdir ("/"))
- _exit (1);
-
- pid2 = fork (); /* Double fork to let init take over the new child. */
- if (pid2 == (pid_t)(-1))
- _exit (1);
- if (pid2)
- _exit (0); /* Let the parent exit immediately. */
+ ec = GPG_ERR_TIMEOUT; /* Still running. */
+ }
+ else
+ {
+ process->terminated = 1;
+ process->wstatus = status;
+ ec = 0;
+ }
- if (envp)
- for (i=0; envp[i]; i++)
- putenv (xstrdup (envp[i]));
+ return ec;
+}
- do_exec (pgmname, argv, -1, -1, -1, NULL, 0);
+void
+gnupg_process_release (gnupg_process_t process)
+{
+ if (!process)
+ return;
- /*NOTREACHED*/
+ if (process->terminated)
+ {
+ gnupg_process_terminate (process);
+ gnupg_process_wait (process, 1);
}
- if (waitpid (pid, NULL, 0) == -1)
- log_error ("waitpid failed in gnupg_spawn_process_detached: %s",
- strerror (errno));
-
- return 0;
+ xfree (process);
}
-
-/* Kill a process; that is send an appropriate signal to the process.
- gnupg_wait_process must be called to actually remove the process
- from the system. An invalid PID is ignored. */
-void
-gnupg_kill_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang)
{
- if (pid != (pid_t)(-1))
+ gpg_err_code_t ec = 0;
+ int i;
+
+ for (i = 0; i < count; i++)
{
- kill (pid, SIGTERM);
+ if (process_list[i]->terminated)
+ continue;
+
+ ec = gnupg_process_wait (process_list[i], hang);
+ if (ec)
+ break;
}
+
+ return ec;
}
diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c
index 0034e03f2..8057cc3b4 100644
--- a/common/exechelp-w32.c
+++ b/common/exechelp-w32.c
@@ -33,6 +33,8 @@
#if !defined(HAVE_W32_SYSTEM)
#error This code is only used on W32.
+#else
+#define _WIN32_WINNT 0x600
#endif
#include <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
@@ -405,143 +409,145 @@ gnupg_close_pipe (int fd)
if (fd != -1)
close (fd);
}
+
+struct gnupg_process {
+ const char *pgmname;
+ unsigned int terminated :1; /* or detached */
+ unsigned int flags;
+ HANDLE hProcess;
+ HANDLE hd_in;
+ HANDLE hd_out;
+ HANDLE hd_err;
+ int exitcode;
+};
+
+static int gnupg_process_syscall_func_initialized;
+
+/* 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 ();
+}
-/* Fork and exec the PGMNAME, see exechelp.h for details. */
-gpg_error_t
-gnupg_spawn_process (const char *pgmname, const char *argv[],
- int *except, unsigned int flags,
- estream_t *r_infp,
- estream_t *r_outfp,
- estream_t *r_errfp,
- pid_t *pid)
+static void
+post_syscall (void)
{
- gpg_error_t err;
- SECURITY_ATTRIBUTES sec_attr;
- PROCESS_INFORMATION pi =
- {
- NULL, /* Returns process handle. */
- 0, /* Returns primary thread handle. */
- 0, /* Returns pid. */
- 0 /* Returns tid. */
- };
- STARTUPINFOW si;
- int cr_flags;
- char *cmdline;
- wchar_t *wcmdline = NULL;
- wchar_t *wpgmname = NULL;
- HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- estream_t infp = NULL;
- estream_t outfp = NULL;
- estream_t errfp = NULL;
- HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
- INVALID_HANDLE_VALUE,
- INVALID_HANDLE_VALUE};
- int i, rc;
- es_syshd_t syshd;
- gpg_err_source_t errsource = default_errsource;
- int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+ if (post_syscall_func)
+ post_syscall_func ();
+}
- (void)except; /* Not yet used. */
- if (r_infp)
- *r_infp = NULL;
- if (r_outfp)
- *r_outfp = NULL;
- if (r_errfp)
- *r_errfp = NULL;
- *pid = (pid_t)(-1); /* Always required. */
+/*
+ * Check if STARTUPINFOEXW supports PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ */
+static int
+check_windows_version (void)
+{
+ static int is_vista_or_later = -1;
+
+ OSVERSIONINFO osvi;
- if (r_infp)
+ if (is_vista_or_later == -1)
{
- if (create_inheritable_pipe (inpipe, INHERIT_READ))
- {
- err = gpg_err_make (errsource, GPG_ERR_GENERAL);
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- return err;
- }
+ memset (&osvi,0,sizeof(osvi));
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ GetVersionEx (&osvi);
- syshd.type = ES_SYSHD_HANDLE;
- syshd.u.handle = inpipe[1];
- infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
- if (!infp)
- {
- err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
- log_error (_("error creating a stream for a pipe: %s\n"),
- gpg_strerror (err));
- CloseHandle (inpipe[0]);
- CloseHandle (inpipe[1]);
- inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
- return err;
- }
+ /* The feature is available on Vista or later. */
+ is_vista_or_later = (osvi.dwMajorVersion >= 6);
}
- if (r_outfp)
- {
- if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
- {
- err = gpg_err_make (errsource, GPG_ERR_GENERAL);
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- return err;
- }
+ return is_vista_or_later;
+}
- syshd.type = ES_SYSHD_HANDLE;
- syshd.u.handle = outpipe[0];
- outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
- if (!outfp)
- {
- err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
- log_error (_("error creating a stream for a pipe: %s\n"),
- gpg_strerror (err));
- CloseHandle (outpipe[0]);
- CloseHandle (outpipe[1]);
- outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[1]);
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- return err;
- }
+
+static gpg_err_code_t
+spawn_detached (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;
+ BOOL ask_inherit = FALSE;
+
+ ec = gnupg_access (pgmname, X_OK);
+ if (ec)
+ {
+ xfree (cmdline);
+ return ec;
}
- if (r_errfp)
+ memset (&si, 0, sizeof si);
+
+ sca.allow_foreground_window = FALSE;
+ sca.hd[0] = INVALID_HANDLE_VALUE;
+ sca.hd[1] = INVALID_HANDLE_VALUE;
+ sca.hd[2] = INVALID_HANDLE_VALUE;
+ sca.inherit_hds = NULL;
+ sca.arg = spawn_cb_arg;
+ if (spawn_cb)
+ (*spawn_cb) (&sca);
+
+ if (sca.inherit_hds)
{
- if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
- {
- err = gpg_err_make (errsource, GPG_ERR_GENERAL);
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- return err;
- }
+ SIZE_T attr_list_size = 0;
+ HANDLE hd[16];
+ HANDLE *hd_p = sca.inherit_hds;
+ int j = 0;
- syshd.type = ES_SYSHD_HANDLE;
- syshd.u.handle = errpipe[0];
- errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
- if (!errfp)
+ if (hd_p)
{
- err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
- log_error (_("error creating a stream for a pipe: %s\n"),
- gpg_strerror (err));
- CloseHandle (errpipe[0]);
- CloseHandle (errpipe[1]);
- errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[0]);
- if (outpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[1]);
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- return err;
+ while (*hd_p != INVALID_HANDLE_VALUE)
+ if (j < DIM (hd))
+ hd[j++] = *hd_p++;
+ else
+ {
+ log_error ("Too much handles\n");
+ break;
+ }
}
+
+ if (j)
+ {
+ if (check_windows_version ())
+ {
+ InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size);
+ si.lpAttributeList = xtrymalloc (attr_list_size);
+ if (si.lpAttributeList == NULL)
+ {
+ xfree (cmdline);
+ return gpg_err_code_from_syserror ();
+ }
+ InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0,
+ &attr_list_size);
+ UpdateProcThreadAttribute (si.lpAttributeList, 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ hd, sizeof (HANDLE) * j, NULL, NULL);
+ }
+
+ ask_inherit = TRUE;
+ }
}
/* Prepare security attributes. */
@@ -549,502 +555,682 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
- /* Build the command line. */
- err = build_w32_commandline (pgmname, argv, &cmdline);
- if (err)
- return err;
-
- if (inpipe[0] == INVALID_HANDLE_VALUE)
- nullhd[0] = ((flags & GNUPG_SPAWN_KEEP_STDIN)?
- GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0));
- if (outpipe[1] == INVALID_HANDLE_VALUE)
- nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)?
- GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1));
- if (errpipe[1] == INVALID_HANDLE_VALUE)
- nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)?
- GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1));
-
- memset (&si, 0, sizeof si);
- si.cb = sizeof (si);
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE;
- si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
- si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
- si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
+ /* 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
- | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
| GetPriorityClass (GetCurrentProcess ())
- | CREATE_SUSPENDED);
- /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", */
- /* pgmname, cmdline); */
+ | CREATE_NEW_PROCESS_GROUP
+ | DETACHED_PROCESS);
+
/* Take care: CreateProcessW may modify wpgmname */
if (!(wpgmname = utf8_to_wchar (pgmname)))
- rc = 0;
+ ret = 0;
else if (!(wcmdline = utf8_to_wchar (cmdline)))
- rc = 0;
+ ret = 0;
else
- rc = CreateProcessW (wpgmname, /* Program to start. */
- wcmdline, /* Command line arguments. */
- &sec_attr, /* Process security attributes. */
- &sec_attr, /* Thread security attributes. */
- TRUE, /* Inherit handles. */
- cr_flags, /* Creation flags. */
- NULL, /* Environment. */
- NULL, /* Use current drive/directory. */
- &si, /* Startup information. */
- &pi /* Returns process information. */
- );
- if (!rc)
+ ret = CreateProcessW (wpgmname, /* Program to start. */
+ wcmdline, /* Command line arguments. */
+ &sec_attr, /* Process security attributes. */
+ &sec_attr, /* Thread security attributes. */
+ ask_inherit, /* Inherit handles. */
+ cr_flags, /* Creation flags. */
+ NULL, /* Environment. */
+ NULL, /* Use current drive/directory. */
+ (STARTUPINFOW *)&si, /* Startup information. */
+ &pi /* Returns process information. */
+ );
+ if (!ret)
{
if (!wpgmname || !wcmdline)
log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
strerror (errno));
else
- log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
+ log_error ("CreateProcess(detached) failed: %d\n",
+ (int)GetLastError ());
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[0]);
- if (outpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (errfp)
- es_fclose (errfp);
- else if (errpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (errpipe[0]);
- if (errpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (errpipe[1]);
- return gpg_err_make (errsource, GPG_ERR_GENERAL);
+ return GPG_ERR_GENERAL;
}
+ if (si.lpAttributeList)
+ DeleteProcThreadAttributeList (si.lpAttributeList);
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
- cmdline = NULL;
-
- /* Close the inherited handles to /dev/null. */
- for (i=0; i < DIM (nullhd); i++)
- if (nullhd[i] != INVALID_HANDLE_VALUE)
- CloseHandle (nullhd[i]);
- /* Close the inherited ends of the pipes. */
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- if (outpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (errpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (errpipe[1]);
+ /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
+ /* " dwProcessID=%d dwThreadId=%d\n", */
+ /* pi.hProcess, pi.hThread, */
+ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
- /* " dwProcessID=%d dwThreadId=%d\n", */
- /* pi.hProcess, pi.hThread, */
- /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */
+ /* Note: AllowSetForegroundWindow doesn't make sense for background
+ process. */
- /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
- invalid argument error if we pass it the correct processID. As a
- workaround we use -1 (ASFW_ANY). */
- if ((flags & GNUPG_SPAWN_RUN_ASFW))
- gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
-
- /* Process has been created suspended; resume it now. */
- ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
-
- if (r_infp)
- *r_infp = infp;
- if (r_outfp)
- *r_outfp = outfp;
- if (r_errfp)
- *r_errfp = errfp;
-
- *pid = handle_to_pid (pi.hProcess);
+ CloseHandle (pi.hProcess);
return 0;
-
}
+void
+gnupg_spawn_helper (struct spawn_cb_arg *sca)
+{
+ HANDLE *user_except = sca->arg;
+ sca->inherit_hds = user_except;
+}
-/* Simplified version of gnupg_spawn_process. This function forks and
- then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
- and ERRFD to stderr (any of them may be -1 to connect them to
- /dev/null). The arguments for the process are expected in the NULL
- terminated array ARGV. The program name itself should not be
- included there. Calling gnupg_wait_process is required.
-
- Returns 0 on success or an error code. */
-gpg_error_t
-gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
- int infd, int outfd, int errfd, pid_t *pid)
+gpg_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_error_t err;
+ gpg_err_code_t ec;
+ gnupg_process_t process;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
- STARTUPINFOW si;
+ STARTUPINFOEXW si;
+ int cr_flags;
char *cmdline;
wchar_t *wcmdline = NULL;
wchar_t *wpgmname = NULL;
- int i, rc;
- HANDLE stdhd[3];
+ int ret;
+ HANDLE hd_in[2];
+ HANDLE hd_out[2];
+ HANDLE hd_err[2];
+ struct spawn_cb_arg sca;
+ int i;
+ BOOL ask_inherit = FALSE;
+
+ check_syscall_func ();
+
+ /* Build the command line. */
+ ec = build_w32_commandline (pgmname, argv, &cmdline);
+ if (ec)
+ return ec;
+
+ if ((flags & GNUPG_PROCESS_DETACHED))
+ {
+ if ((flags & GNUPG_PROCESS_STDFDS_SETTING))
+ {
+ xfree (cmdline);
+ return GPG_ERR_INV_FLAG;
+ }
+
+ /* In detached case, it must be no R_PROCESS. */
+ if (r_process)
+ {
+ xfree (cmdline);
+ return GPG_ERR_INV_ARG;
+ }
+
+ return spawn_detached (pgmname, cmdline, spawn_cb, spawn_cb_arg);
+ }
+
+ if (r_process)
+ *r_process = NULL;
+
+ process = xtrymalloc (sizeof (struct gnupg_process));
+ if (process == NULL)
+ {
+ xfree (cmdline);
+ return gpg_err_code_from_syserror ();
+ }
+
+ process->pgmname = pgmname;
+ process->flags = flags;
+
+ if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR))
+ {
+ xfree (process);
+ xfree (cmdline);
+ return GPG_ERR_NOT_SUPPORTED;
+ }
+
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ {
+ ec = create_inheritable_pipe (hd_in, INHERIT_READ);
+ if (ec)
+ {
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDIN_KEEP))
+ {
+ hd_in[0] = GetStdHandle (STD_INPUT_HANDLE);
+ hd_in[1] = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ hd_in[0] = w32_open_null (0);
+ hd_in[1] = INVALID_HANDLE_VALUE;
+ }
+
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ {
+ ec = create_inheritable_pipe (hd_out, INHERIT_WRITE);
+ if (ec)
+ {
+ if (hd_in[0] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[0]);
+ if (hd_in[1] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[1]);
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDOUT_KEEP))
+ {
+ hd_out[0] = INVALID_HANDLE_VALUE;
+ hd_out[1] = GetStdHandle (STD_OUTPUT_HANDLE);
+ }
+ else
+ {
+ hd_out[0] = INVALID_HANDLE_VALUE;
+ hd_out[1] = w32_open_null (1);
+ }
- /* Setup return values. */
- *pid = (pid_t)(-1);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
+ {
+ ec = create_inheritable_pipe (hd_err, INHERIT_WRITE);
+ if (ec)
+ {
+ if (hd_in[0] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[0]);
+ if (hd_in[1] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[1]);
+ if (hd_out[0] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_out[0]);
+ if (hd_out[1] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_out[1]);
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDERR_KEEP))
+ {
+ hd_err[0] = INVALID_HANDLE_VALUE;
+ hd_err[1] = GetStdHandle (STD_ERROR_HANDLE);
+ }
+ else
+ {
+ hd_err[0] = INVALID_HANDLE_VALUE;
+ hd_err[1] = w32_open_null (1);
+ }
+
+ memset (&si, 0, sizeof si);
+
+ sca.allow_foreground_window = FALSE;
+ sca.hd[0] = hd_in[0];
+ sca.hd[1] = hd_out[1];
+ sca.hd[2] = hd_err[1];
+ sca.inherit_hds = NULL;
+ sca.arg = spawn_cb_arg;
+ if (spawn_cb)
+ (*spawn_cb) (&sca);
+
+ i = 0;
+ if (sca.hd[0] != INVALID_HANDLE_VALUE)
+ i++;
+ if (sca.hd[1] != INVALID_HANDLE_VALUE)
+ i++;
+ if (sca.hd[2] != INVALID_HANDLE_VALUE)
+ i++;
+
+ if (i != 0 || sca.inherit_hds)
+ {
+ SIZE_T attr_list_size = 0;
+ HANDLE hd[16];
+ HANDLE *hd_p = sca.inherit_hds;
+ int j = 0;
+
+ if (sca.hd[0] != INVALID_HANDLE_VALUE)
+ hd[j++] = sca.hd[0];
+ if (sca.hd[1] != INVALID_HANDLE_VALUE)
+ hd[j++] = sca.hd[1];
+ if (sca.hd[1] != INVALID_HANDLE_VALUE)
+ hd[j++] = sca.hd[2];
+ if (hd_p)
+ {
+ while (*hd_p != INVALID_HANDLE_VALUE)
+ if (j < DIM (hd))
+ hd[j++] = *hd_p++;
+ else
+ {
+ log_error ("Too much handles\n");
+ break;
+ }
+ }
+
+ if (j)
+ {
+ if (check_windows_version ())
+ {
+ InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size);
+ si.lpAttributeList = xtrymalloc (attr_list_size);
+ if (si.lpAttributeList == NULL)
+ {
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE)
+ || !(flags & GNUPG_PROCESS_STDIN_KEEP))
+ CloseHandle (hd_in[0]);
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ CloseHandle (hd_in[1]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ CloseHandle (hd_out[0]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE)
+ || !(flags & GNUPG_PROCESS_STDOUT_KEEP))
+ CloseHandle (hd_out[1]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
+ CloseHandle (hd_err[0]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE)
+ || !(flags & GNUPG_PROCESS_STDERR_KEEP))
+ CloseHandle (hd_err[1]);
+ xfree (process);
+ xfree (cmdline);
+ return gpg_err_code_from_syserror ();
+ }
+ InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0,
+ &attr_list_size);
+ UpdateProcThreadAttribute (si.lpAttributeList, 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ hd, sizeof (HANDLE) * j, NULL, NULL);
+ }
+ ask_inherit = TRUE;
+ }
+ }
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
- /* Build the command line. */
- err = build_w32_commandline (pgmname, argv, &cmdline);
- if (err)
- return err;
-
- memset (&si, 0, sizeof si);
- si.cb = sizeof (si);
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
- stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
- stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
- stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
- si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
- si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
- si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
-
-/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
- /* Take care: CreateProcessW may modify wpgmname */
+ /* 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)))
- rc = 0;
+ ret = 0;
else if (!(wcmdline = utf8_to_wchar (cmdline)))
- rc = 0;
+ ret = 0;
else
- rc = CreateProcessW (wpgmname, /* Program to start. */
- wcmdline, /* Command line arguments. */
- &sec_attr, /* Process security attributes. */
- &sec_attr, /* Thread security attributes. */
- TRUE, /* Inherit handles. */
- (CREATE_DEFAULT_ERROR_MODE
- | GetPriorityClass (GetCurrentProcess ())
- | CREATE_SUSPENDED | DETACHED_PROCESS),
- NULL, /* Environment. */
- NULL, /* Use current drive/directory. */
- &si, /* Startup information. */
- &pi /* Returns process information. */
- );
- if (!rc)
+ ret = CreateProcessW (wpgmname, /* Program to start. */
+ wcmdline, /* Command line arguments. */
+ &sec_attr, /* Process security attributes. */
+ &sec_attr, /* Thread security attributes. */
+ ask_inherit, /* Inherit handles. */
+ cr_flags, /* Creation flags. */
+ NULL, /* Environment. */
+ NULL, /* Use current drive/directory. */
+ (STARTUPINFOW *)&si, /* Startup information. */
+ &pi /* Returns process information. */
+ );
+ if (!ret)
{
if (!wpgmname || !wcmdline)
log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
- strerror (errno));
+ strerror (errno));
else
- log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
- err = my_error (GPG_ERR_GENERAL);
+ log_error ("CreateProcess failed: ec=%d\n",
+ (int)GetLastError ());
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE)
+ || !(flags & GNUPG_PROCESS_STDIN_KEEP))
+ CloseHandle (hd_in[0]);
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ CloseHandle (hd_in[1]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ CloseHandle (hd_out[0]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE)
+ || !(flags & GNUPG_PROCESS_STDOUT_KEEP))
+ CloseHandle (hd_out[1]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
+ CloseHandle (hd_err[0]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE)
+ || !(flags & GNUPG_PROCESS_STDERR_KEEP))
+ CloseHandle (hd_err[1]);
+ xfree (wpgmname);
+ xfree (wcmdline);
+ xfree (process);
+ xfree (cmdline);
+ return GPG_ERR_GENERAL;
}
- else
- err = 0;
+
+ if (si.lpAttributeList)
+ DeleteProcThreadAttributeList (si.lpAttributeList);
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
- for (i=0; i < 3; i++)
- if (stdhd[i] != INVALID_HANDLE_VALUE)
- CloseHandle (stdhd[i]);
- if (err)
- return err;
-/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
-/* " dwProcessID=%d dwThreadId=%d\n", */
-/* pi.hProcess, pi.hThread, */
-/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE)
+ || !(flags & GNUPG_PROCESS_STDIN_KEEP))
+ CloseHandle (hd_in[0]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE)
+ || !(flags & GNUPG_PROCESS_STDOUT_KEEP))
+ CloseHandle (hd_out[1]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE)
+ || !(flags & GNUPG_PROCESS_STDERR_KEEP))
+ CloseHandle (hd_err[1]);
+
+ /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
+ /* " dwProcessID=%d dwThreadId=%d\n", */
+ /* pi.hProcess, pi.hThread, */
+ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
+
+ if (sca.allow_foreground_window)
+ {
+ /* Fixme: For unknown reasons AllowSetForegroundWindow returns
+ * an invalid argument error if we pass it the correct
+ * processID. As a workaround we use -1 (ASFW_ANY). */
+ if (!AllowSetForegroundWindow (ASFW_ANY /*pi.dwProcessId*/))
+ log_info ("AllowSetForegroundWindow() failed: ec=%d\n",
+ (int)GetLastError ());
+ }
/* Process has been created suspended; resume it now. */
+ pre_syscall ();
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
+ post_syscall ();
- *pid = handle_to_pid (pi.hProcess);
- return 0;
+ 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;
+ }
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
+ 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)
{
- return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
+ 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;
}
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
- int hang, int *r_exitcodes)
+static gpg_err_code_t
+process_kill (gnupg_process_t process, unsigned int exitcode)
{
gpg_err_code_t ec = 0;
- size_t i;
- HANDLE *procs;
- int code;
- procs = xtrycalloc (count, sizeof *procs);
- if (procs == NULL)
- return my_error_from_syserror ();
+ pre_syscall ();
+ if (TerminateProcess (process->hProcess, exitcode))
+ ec = gpg_err_code_from_syserror ();
+ post_syscall ();
+ return ec;
+}
- for (i = 0; i < count; i++)
+static gpg_err_code_t
+process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr)
+{
+ switch (request)
{
- if (r_exitcodes)
- r_exitcodes[i] = -1;
+ case GNUPG_PROCESS_NOP:
+ return 0;
- if (pids[i] == (pid_t)(-1))
- return my_error (GPG_ERR_INV_VALUE);
+ case GNUPG_PROCESS_GET_PROC_ID:
+ {
+ int *r_id = va_arg (arg_ptr, int *);
- procs[i] = pid_to_handle (pids[i]);
- }
+ if (r_id == NULL)
+ return GPG_ERR_INV_VALUE;
- /* FIXME: We should do a pth_waitpid here. However this has not yet
- been implemented. A special W32 pth system call would even be
- better. */
- code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
- switch (code)
- {
- case WAIT_TIMEOUT:
- ec = GPG_ERR_TIMEOUT;
- goto leave;
+ *r_id = (int)GetProcessId (process->hProcess);
+ return 0;
+ }
- case WAIT_FAILED:
- log_error (_("waiting for processes to terminate failed: %s\n"),
- w32_strerror (-1));
- ec = GPG_ERR_GENERAL;
- goto leave;
+ case GNUPG_PROCESS_GET_EXIT_ID:
+ {
+ int *r_exit_status = va_arg (arg_ptr, int *);
+ unsigned long exit_code;
- case WAIT_OBJECT_0:
- for (i = 0; i < count; i++)
- {
- DWORD exc;
+ *r_exit_status = -1;
- if (! GetExitCodeProcess (procs[i], &exc))
- {
- log_error (_("error getting exit code of process %d: %s\n"),
- (int) pids[i], w32_strerror (-1) );
- ec = GPG_ERR_GENERAL;
- }
- else if (exc)
- {
- if (!r_exitcodes)
- log_error (_("error running '%s': exit status %d\n"),
- pgmnames[i], (int)exc);
- else
- r_exitcodes[i] = (int)exc;
- ec = GPG_ERR_GENERAL;
- }
- else
- {
- if (r_exitcodes)
- r_exitcodes[i] = 0;
- }
- }
- break;
+ 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:
- log_error ("WaitForMultipleObjects returned unexpected "
- "code %d\n", code);
- ec = GPG_ERR_GENERAL;
break;
}
- leave:
- return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+ return GPG_ERR_UNKNOWN_COMMAND;
}
-
-
-void
-gnupg_release_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_ctl (gnupg_process_t process, unsigned int request, ...)
{
- if (pid != (pid_t)INVALID_HANDLE_VALUE)
- {
- HANDLE process = (HANDLE)pid;
+ va_list arg_ptr;
+ gpg_err_code_t ec;
- CloseHandle (process);
- }
+ va_start (arg_ptr, request);
+ ec = process_vctl (process, request, arg_ptr);
+ va_end (arg_ptr);
+ return ec;
}
-
-/* Spawn a new process and immediately detach from it. The name of
- the program to exec is PGMNAME and its arguments are in ARGV (the
- programname is automatically passed as first argument).
- Environment strings in ENVP are set. An error is returned if
- pgmname is not executable; to make this work it is necessary to
- provide an absolute file name. All standard file descriptors are
- connected to /dev/null. */
-gpg_error_t
-gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
- const char *envp[] )
+gpg_err_code_t
+gnupg_process_wait (gnupg_process_t process, int hang)
{
- gpg_error_t err;
- SECURITY_ATTRIBUTES sec_attr;
- PROCESS_INFORMATION pi =
- {
- NULL, /* Returns process handle. */
- 0, /* Returns primary thread handle. */
- 0, /* Returns pid. */
- 0 /* Returns tid. */
- };
- STARTUPINFOW si;
- int cr_flags;
- char *cmdline;
- wchar_t *wcmdline = NULL;
- wchar_t *wpgmname = NULL;
- BOOL in_job = FALSE;
gpg_err_code_t ec;
- int rc;
- int jobdebug;
+ int code;
- /* We don't use ENVP. */
- (void)envp;
+ if (process->hProcess == INVALID_HANDLE_VALUE)
+ return 0;
- cmdline = getenv ("GNUPG_EXEC_DEBUG_FLAGS");
- jobdebug = (cmdline && (atoi (cmdline) & 1));
+ pre_syscall ();
+ code = WaitForSingleObject (process->hProcess, hang? INFINITE : 0);
+ post_syscall ();
- if ((ec = gnupg_access (pgmname, X_OK)))
- return gpg_err_make (default_errsource, ec);
+ switch (code)
+ {
+ case WAIT_TIMEOUT:
+ ec = GPG_ERR_TIMEOUT; /* Still running. */
+ break;
- /* Prepare security attributes. */
- memset (&sec_attr, 0, sizeof sec_attr );
- sec_attr.nLength = sizeof sec_attr;
- sec_attr.bInheritHandle = FALSE;
+ case WAIT_FAILED:
+ log_error (_("waiting for process to terminate failed: ec=%d\n"),
+ (int)GetLastError ());
+ ec = GPG_ERR_GENERAL;
+ break;
- /* Build the command line. */
- err = build_w32_commandline (pgmname, argv, &cmdline);
- if (err)
- return err;
+ case WAIT_OBJECT_0:
+ process->terminated = 1;
+ ec = 0;
+ break;
- /* Start the process. */
- memset (&si, 0, sizeof si);
- si.cb = sizeof (si);
- si.dwFlags = STARTF_USESHOWWINDOW;
- si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
+ default:
+ log_debug ("WaitForSingleObject returned unexpected code %d\n", code);
+ ec = GPG_ERR_GENERAL;
+ break;
+ }
- cr_flags = (CREATE_DEFAULT_ERROR_MODE
- | GetPriorityClass (GetCurrentProcess ())
- | CREATE_NEW_PROCESS_GROUP
- | DETACHED_PROCESS);
+ return ec;
+}
- /* Check if we were spawned as part of a Job.
- * In a job we need to add CREATE_BREAKAWAY_FROM_JOB
- * to the cr_flags, otherwise our child processes
- * are killed when we terminate. */
- if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job))
- {
- log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1));
- in_job = FALSE;
- }
+gpg_err_code_t
+gnupg_process_terminate (gnupg_process_t process)
+{
+ return process_kill (process, 1);
+}
- if (in_job)
- {
- /* Only try to break away from job if it is allowed, otherwise
- * CreateProcess() would fail with an "Access is denied" error. */
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
- if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation,
- &info, sizeof info, NULL))
- {
- log_error ("QueryInformationJobObject() failed: %s\n",
- w32_strerror (-1));
- }
- else if ((info.BasicLimitInformation.LimitFlags &
- JOB_OBJECT_LIMIT_BREAKAWAY_OK))
- {
- if (jobdebug)
- log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n");
- cr_flags |= CREATE_BREAKAWAY_FROM_JOB;
- }
- else if ((info.BasicLimitInformation.LimitFlags &
- JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))
- {
- /* The child process should automatically detach from the job. */
- if (jobdebug)
- log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; "
- "JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n");
- }
- else
- {
- /* It seems that the child process must remain in the job.
- * This is not necessarily an error, although it can cause premature
- * termination of the child process when the job is closed. */
- if (jobdebug)
- log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n");
- }
- }
- else
- {
- if (jobdebug)
- log_debug ("Process is not in a Job\n");
- }
+void
+gnupg_process_release (gnupg_process_t process)
+{
+ if (!process)
+ return;
- /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
- /* pgmname, cmdline); */
- /* Take care: CreateProcessW may modify wpgmname */
- if (!(wpgmname = utf8_to_wchar (pgmname)))
- rc = 0;
- else if (!(wcmdline = utf8_to_wchar (cmdline)))
- rc = 0;
- else
- rc = CreateProcessW (wpgmname, /* Program to start. */
- wcmdline, /* Command line arguments. */
- &sec_attr, /* Process security attributes. */
- &sec_attr, /* Thread security attributes. */
- FALSE, /* Inherit handles. */
- cr_flags, /* Creation flags. */
- NULL, /* Environment. */
- NULL, /* Use current drive/directory. */
- &si, /* Startup information. */
- &pi /* Returns process information. */
- );
- if (!rc)
+ if (process->terminated)
{
- if (!wpgmname || !wcmdline)
- log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
- strerror (errno));
- else
- log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
- xfree (wpgmname);
- xfree (wcmdline);
- xfree (cmdline);
- return my_error (GPG_ERR_GENERAL);
+ gnupg_process_terminate (process);
+ gnupg_process_wait (process, 1);
}
- xfree (wpgmname);
- xfree (wcmdline);
- xfree (cmdline);
- cmdline = NULL;
-
-/* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
-/* " dwProcessID=%d dwThreadId=%d\n", */
-/* pi.hProcess, pi.hThread, */
-/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- CloseHandle (pi.hThread);
- CloseHandle (pi.hProcess);
-
- return 0;
+ xfree (process);
}
-
-/* Kill a process; that is send an appropriate signal to the process.
- gnupg_wait_process must be called to actually remove the process
- from the system. An invalid PID is ignored. */
-void
-gnupg_kill_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang)
{
- if (pid != (pid_t) INVALID_HANDLE_VALUE)
+ gpg_err_code_t ec = 0;
+ int i;
+
+ for (i = 0; i < count; i++)
{
- HANDLE process = (HANDLE) pid;
+ if (process_list[i]->terminated)
+ continue;
- /* Arbitrary error code. */
- TerminateProcess (process, 1);
+ ec = gnupg_process_wait (process_list[i], hang);
+ if (ec)
+ break;
}
+
+ return ec;
}
diff --git a/common/exechelp.h b/common/exechelp.h
index 3343fe598..0370b23a4 100644
--- a/common/exechelp.h
+++ b/common/exechelp.h
@@ -73,140 +73,103 @@ gpg_error_t gnupg_create_pipe (int filedes[2]);
void gnupg_close_pipe (int fd);
-#define GNUPG_SPAWN_NONBLOCK 16
-#define GNUPG_SPAWN_RUN_ASFW 64
-#define GNUPG_SPAWN_DETACHED 128
-#define GNUPG_SPAWN_KEEP_STDIN 256
-#define GNUPG_SPAWN_KEEP_STDOUT 512
-#define GNUPG_SPAWN_KEEP_STDERR 1024
-
-/* Fork and exec the program PGMNAME.
-
- If R_INFP is NULL connect stdin of the new process to /dev/null; if
- it is not NULL store the address of a pointer to a new estream
- there. If R_OUTFP is NULL connect stdout of the new process to
- /dev/null; if it is not NULL store the address of a pointer to a
- new estream there. If R_ERRFP is NULL connect stderr of the new
- process to /dev/null; if it is not NULL store the address of a
- pointer to a new estream there. On success the pid of the new
- process is stored at PID. On error -1 is stored at PID and if
- R_OUTFP or R_ERRFP are not NULL, NULL is stored there.
-
- The arguments for the process are expected in the NULL terminated
- array ARGV. The program name itself should not be included there.
- If PREEXEC is not NULL, the given function will be called right
- before the exec.
-
- IF EXCEPT is not NULL, it is expected to be an ordered list of file
- descriptors, terminated by an entry with the value (-1). These
- file descriptors won't be closed before spawning a new program.
-
- Returns 0 on success or an error code. Calling gnupg_wait_process
- and gnupg_release_process is required if the function succeeded.
-
- FLAGS is a bit vector:
-
- GNUPG_SPAWN_NONBLOCK
- If set the two output streams are created in non-blocking
- mode and the input stream is switched to non-blocking mode.
- This is merely a convenience feature because the caller
- could do the same with gpgrt_set_nonblock. Does not yet
- work for Windows.
-
- GNUPG_SPAWN_DETACHED
- If set the process will be started as a background process.
- This flag is only useful under W32 (but not W32CE) systems,
- so that no new console is created and pops up a console
- window when starting the server. Does not work on W32CE.
-
- GNUPG_SPAWN_RUN_ASFW
- On W32 (but not on W32CE) run AllowSetForegroundWindow for
- the child. Note that due to unknown problems this actually
- allows SetForegroundWindow for all children of this process.
-
- GNUPG_SPAWN_KEEP_STDIN
- GNUPG_SPAWN_KEEP_STDOUT
- GNUPG_SPAWN_KEEP_STDERR
- Do not assign /dev/null to a non-required standard file
- descriptor.
-
- */
-gpg_error_t
-gnupg_spawn_process (const char *pgmname, const char *argv[],
- int *execpt, unsigned int flags,
- estream_t *r_infp,
- estream_t *r_outfp,
- estream_t *r_errfp,
- pid_t *pid);
-
-
-/* Simplified version of gnupg_spawn_process. This function forks and
- then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
- and ERRFD to stderr (any of them may be -1 to connect them to
- /dev/null). The arguments for the process are expected in the NULL
- terminated array ARGV. The program name itself should not be
- included there. Calling gnupg_wait_process and
- gnupg_release_process is required. Returns 0 on success or an
- error code. */
-gpg_error_t gnupg_spawn_process_fd (const char *pgmname,
- const char *argv[],
- int infd, int outfd, int errfd,
- pid_t *pid);
-
-
-/* If HANG is true, waits for the process identified by PID to exit;
- if HANG is false, checks whether the process has terminated.
- PGMNAME should be the same as supplied to the spawn function and is
- only used for diagnostics. Return values:
-
- 0
- The process exited successful. 0 is stored at R_EXITCODE.
-
- GPG_ERR_GENERAL
- The process exited without success. The exit code of process
- is then stored at R_EXITCODE. An exit code of -1 indicates
- that the process terminated abnormally (e.g. due to a signal).
-
- GPG_ERR_TIMEOUT
- The process is still running (returned only if HANG is false).
-
- GPG_ERR_INV_VALUE
- An invalid PID has been specified.
-
- Other error codes may be returned as well. Unless otherwise noted,
- -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL
- if the exit code is not required (in that case an error message will
- be printed). Note that under Windows PID is not the process id but
- the handle of the process. */
-gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang,
- int *r_exitcode);
-
-/* Like gnupg_wait_process, but for COUNT processes. */
-gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids,
- size_t count, int hang, int *r_exitcodes);
-
-
-/* Kill a process; that is send an appropriate signal to the process.
- gnupg_wait_process must be called to actually remove the process
- from the system. An invalid PID is ignored. */
-void gnupg_kill_process (pid_t pid);
-
-/* Release the process identified by PID. This function is actually
- only required for Windows but it does not harm to always call it.
- It is a nop if PID is invalid. */
-void gnupg_release_process (pid_t pid);
-
-
-/* Spawn a new process and immediately detach from it. The name of
- the program to exec is PGMNAME and its arguments are in ARGV (the
- programname is automatically passed as first argument).
- Environment strings in ENVP are set. An error is returned if
- pgmname is not executable; to make this work it is necessary to
- provide an absolute file name. */
-gpg_error_t gnupg_spawn_process_detached (const char *pgmname,
- const char *argv[],
- const char *envp[] );
-
+/* The opaque type for a subprocess. */
+typedef struct gnupg_process *gnupg_process_t;
+#ifdef HAVE_W32_SYSTEM
+struct spawn_cb_arg;
+#ifdef NEED_STRUCT_SPAWN_CB_ARG
+struct spawn_cb_arg {
+ HANDLE hd[3];
+ HANDLE *inherit_hds;
+ BOOL allow_foreground_window;
+ void *arg;
+};
+#endif
+#else
+struct spawn_cb_arg {
+ int fds[3];
+ int *except_fds;
+ void *arg;
+};
+#endif
+
+#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_KEEP (1 << 12)
+#define GNUPG_PROCESS_STDOUT_KEEP (1 << 13)
+#define GNUPG_PROCESS_STDERR_KEEP (1 << 14)
+#define GNUPG_PROCESS_STDFDS_SETTING ( GNUPG_PROCESS_STDIN_PIPE \
+ | GNUPG_PROCESS_STDOUT_PIPE | GNUPG_PROCESS_STDERR_PIPE \
+ | GNUPG_PROCESS_STDINOUT_SOCKETPAIR | GNUPG_PROCESS_STDIN_KEEP \
+ | GNUPG_PROCESS_STDOUT_KEEP | GNUPG_PROCESS_STDERR_KEEP)
+
+#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_PROC_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..3505c25f1 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
+ : 0)
+ | GNUPG_PROCESS_STDOUT_PIPE
+ | GNUPG_PROCESS_STDERR_PIPE),
+ gnupg_spawn_helper, exceptclose, &proc);
+ gnupg_process_get_streams (proc, GNUPG_PROCESS_STREAM_NONBLOCK,
+ input? &infp : NULL, &outfp, &errfp);
if (extrapipe[0] != -1)
close (extrapipe[0]);
if (argsave)
@@ -546,20 +553,25 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
es_fclose (outfp); outfp = NULL;
es_fclose (errfp); errfp = NULL;
- err = gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL);
- pid = (pid_t)(-1);
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
+ { /* To be compatible to old wait_process. */
+ int status;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status);
+ if (status)
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
leave:
- if (err && pid != (pid_t) -1)
- gnupg_kill_process (pid);
+ if (err && proc)
+ gnupg_process_terminate (proc);
es_fclose (infp);
es_fclose (extrafp);
es_fclose (outfp);
es_fclose (errfp);
- if (pid != (pid_t)(-1))
- gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL);
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
copy_buffer_shred (cpbuf_in);
xfree (cpbuf_in);
diff --git a/dirmngr/ldap-wrapper.c b/dirmngr/ldap-wrapper.c
index 23d514cf9..2ec944c72 100644
--- a/dirmngr/ldap-wrapper.c
+++ b/dirmngr/ldap-wrapper.c
@@ -87,7 +87,7 @@ struct wrapper_context_s
{
struct wrapper_context_s *next;
- pid_t pid; /* The pid of the wrapper process. */
+ gnupg_process_t proc;/* The wrapper process. */
int printable_pid; /* Helper to print diagnostics after the process has
* been cleaned up. */
estream_t fp; /* Connected with stdout of the ldap wrapper. */
@@ -170,10 +170,10 @@ read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
static void
destroy_wrapper (struct wrapper_context_s *ctx)
{
- if (ctx->pid != (pid_t)(-1))
+ if (ctx->proc)
{
- gnupg_kill_process (ctx->pid);
- gnupg_release_process (ctx->pid);
+ gnupg_process_terminate (ctx->proc);
+ gnupg_process_release (ctx->proc);
}
ksba_reader_release (ctx->reader);
SAFE_CLOSE (ctx->fp);
@@ -260,7 +260,7 @@ read_log_data (struct wrapper_context_s *ctx)
if (gpg_err_code (err) == GPG_ERR_EAGAIN)
return 0;
log_error (_("error reading log from ldap wrapper %d: %s\n"),
- (int)ctx->pid, gpg_strerror (err));
+ ctx->printable_pid, gpg_strerror (err));
}
print_log_line (ctx, NULL); /* Flush. */
SAFE_CLOSE (ctx->log_fp);
@@ -438,50 +438,44 @@ ldap_reaper_thread (void *dummy)
}
/* Check whether the process is still running. */
- if (ctx->pid != (pid_t)(-1))
+ if (ctx->proc)
{
- int status;
-
- err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0,
- &status);
+ err = gnupg_process_wait (ctx->proc, 0);
if (!err)
{
+ int status;
+
+ gnupg_process_ctl (ctx->proc, GNUPG_PROCESS_GET_EXIT_ID,
+ &status);
if (DBG_EXTPROG)
- log_info (_("ldap wrapper %d ready"), (int)ctx->pid);
+ log_info (_("ldap wrapper %d ready"), ctx->printable_pid);
ctx->ready = 1;
- gnupg_release_process (ctx->pid);
- ctx->pid = (pid_t)(-1);
+ gnupg_process_release (ctx->proc);
+ ctx->proc = NULL;
any_action = 1;
- }
- else if (gpg_err_code (err) == GPG_ERR_GENERAL)
- {
+
if (status == 10)
log_info (_("ldap wrapper %d ready: timeout\n"),
- (int)ctx->pid);
+ ctx->printable_pid);
else
log_info (_("ldap wrapper %d ready: exitcode=%d\n"),
- (int)ctx->pid, status);
- ctx->ready = 1;
- gnupg_release_process (ctx->pid);
- ctx->pid = (pid_t)(-1);
- any_action = 1;
+ ctx->printable_pid, status);
}
else if (gpg_err_code (err) != GPG_ERR_TIMEOUT)
{
log_error (_("waiting for ldap wrapper %d failed: %s\n"),
- (int)ctx->pid, gpg_strerror (err));
+ ctx->printable_pid, gpg_strerror (err));
any_action = 1;
}
}
/* Check whether we should terminate the process. */
- if (ctx->pid != (pid_t)(-1)
- && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime)
+ if (ctx->proc && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime)
{
- gnupg_kill_process (ctx->pid);
+ gnupg_process_terminate (ctx->proc);
ctx->stamp = (time_t)(-1);
log_info (_("ldap wrapper %d stalled - killing\n"),
- (int)ctx->pid);
+ ctx->printable_pid);
/* We need to close the log stream because the cleanup
* loop waits for it. */
SAFE_CLOSE (ctx->log_fp);
@@ -496,10 +490,10 @@ ldap_reaper_thread (void *dummy)
{
log_debug ("ldap worker states:\n");
for (ctx = reaper_list; ctx; ctx = ctx->next)
- log_debug (" c=%p pid=%d/%d rdr=%p logfp=%p"
+ log_debug (" c=%p pid=%d rdr=%p logfp=%p"
" ctrl=%p/%d la=%lu rdy=%d\n",
ctx,
- (int)ctx->pid, (int)ctx->printable_pid,
+ ctx->printable_pid,
ctx->reader, ctx->log_fp,
ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0,
(unsigned long)ctx->stamp, ctx->ready);
@@ -602,9 +596,9 @@ ldap_wrapper_release_context (ksba_reader_t reader)
if (ctx->reader == reader)
{
if (DBG_EXTPROG)
- log_debug ("releasing ldap worker c=%p pid=%d/%d rdr=%p"
+ log_debug ("releasing ldap worker c=%p pid=%d rdr=%p"
" ctrl=%p/%d\n", ctx,
- (int)ctx->pid, (int)ctx->printable_pid,
+ ctx->printable_pid,
ctx->reader,
ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0);
@@ -639,8 +633,8 @@ ldap_wrapper_connection_cleanup (ctrl_t ctrl)
{
ctx->ctrl->refcount--;
ctx->ctrl = NULL;
- if (ctx->pid != (pid_t)(-1))
- gnupg_kill_process (ctx->pid);
+ if (ctx->proc)
+ gnupg_process_terminate (ctx->proc);
if (ctx->fp_err)
log_info ("%s: reading from ldap wrapper %d failed: %s\n",
__func__, ctx->printable_pid, gpg_strerror (ctx->fp_err));
@@ -798,7 +792,7 @@ gpg_error_t
ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
{
gpg_error_t err;
- pid_t pid;
+ gnupg_process_t process;
struct wrapper_context_s *ctx;
int i;
int j;
@@ -854,19 +848,22 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
return err;
}
- err = gnupg_spawn_process (pgmname, arg_list,
- NULL, GNUPG_SPAWN_NONBLOCK,
- NULL, &outfp, &errfp, &pid);
+ err = gnupg_process_spawn (pgmname, arg_list,
+ (GNUPG_PROCESS_STDOUT_PIPE
+ | GNUPG_PROCESS_STDERR_PIPE),
+ NULL, NULL, &process);
if (err)
{
- xfree (arg_list);
+ xfree (arg_list);
xfree (ctx);
log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
return err;
}
+ gnupg_process_get_streams (process, GNUPG_PROCESS_STREAM_NONBLOCK,
+ NULL, &outfp, &errfp);
+ gnupg_process_ctl (process, GNUPG_PROCESS_GET_PROC_ID, &ctx->printable_pid);
- ctx->pid = pid;
- ctx->printable_pid = (int) pid;
+ ctx->proc = process;
ctx->fp = outfp;
ctx->log_fp = errfp;
ctx->ctrl = ctrl;
@@ -902,7 +899,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
if (DBG_EXTPROG)
{
log_debug ("ldap wrapper %d started (%p, %s)",
- (int)ctx->pid, ctx->reader, pgmname);
+ ctx->printable_pid, ctx->reader, pgmname);
for (i=0; arg_list[i]; i++)
log_printf (" [%s]", arg_list[i]);
log_printf ("\n");
diff --git a/g10/photoid.c b/g10/photoid.c
index 72e6acf7d..a866eb083 100644
--- a/g10/photoid.c
+++ b/g10/photoid.c
@@ -594,34 +594,35 @@ run_with_pipe (struct spawn_info *info, const void *image, u32 len)
" external programs\n"));
return;
#else /* !EXEC_TEMPFILE_ONLY */
- int to[2];
- pid_t pid;
gpg_error_t err;
const char *argv[4];
-
- err = gnupg_create_pipe (to);
- if (err)
- return;
+ gnupg_process_t proc;
fill_command_argv (argv, info->command);
- err = gnupg_spawn_process_fd (argv[0], argv+1, to[0], -1, -1, &pid);
-
- close (to[0]);
-
+ err = gnupg_process_spawn (argv[0], argv+1, GNUPG_PROCESS_STDIN_PIPE,
+ NULL, NULL, &proc);
if (err)
- {
- log_error (_("unable to execute shell '%s': %s\n"),
- argv[0], gpg_strerror (err));
- close (to[1]);
- }
+ log_error (_("unable to execute shell '%s': %s\n"),
+ argv[0], gpg_strerror (err));
else
{
- write (to[1], image, len);
- close (to[1]);
+ int fd_in;
- err = gnupg_wait_process (argv[0], pid, 1, NULL);
+ err = gnupg_process_get_fds (proc, 0, &fd_in, NULL, NULL);
+ if (err)
+ log_error ("unable to get pipe connection '%s': %s\n",
+ argv[2], gpg_strerror (err));
+ else
+ {
+ write (fd_in, image, len);
+ close (fd_in);
+ }
+
+ err = gnupg_process_wait (proc, 1);
if (err)
log_error (_("unnatural exit of external program\n"));
+
+ gnupg_process_release (proc);
}
#endif /* !EXEC_TEMPFILE_ONLY */
}
@@ -689,14 +690,11 @@ show_photo (const char *command, const char *name, const void *image, u32 len)
log_error (_("system error while calling external program: %s\n"),
strerror (errno));
#else
- pid_t pid;
gpg_error_t err;
const char *argv[4];
fill_command_argv (argv, spawn->command);
- err = gnupg_spawn_process_fd (argv[0], argv+1, -1, -1, -1, &pid);
- if (!err)
- err = gnupg_wait_process (argv[0], pid, 1, NULL);
+ err = gnupg_process_spawn (argv[0], argv+1, 0, NULL, NULL, NULL);
if (err)
log_error (_("unnatural exit of external program\n"));
#endif
diff --git a/g13/be-encfs.c b/g13/be-encfs.c
index 0e2c68bf3..ac6d6d6cd 100644
--- a/g13/be-encfs.c
+++ b/g13/be-encfs.c
@@ -28,10 +28,10 @@
#include "g13.h"
#include "../common/i18n.h"
#include "keyblob.h"
-#include "be-encfs.h"
-#include "runner.h"
#include "../common/sysutils.h"
#include "../common/exechelp.h"
+#include "runner.h"
+#include "be-encfs.h"
/* Command values used to run the encfs tool. */
@@ -81,7 +81,9 @@ run_umount_helper (const char *mountpoint)
args[1] = mountpoint;
args[2] = NULL;
- err = gnupg_spawn_process_detached (pgmname, args, NULL);
+ err = gnupg_process_spawn (pgmname, args,
+ GNUPG_PROCESS_DETACHED,
+ NULL, NULL, NULL);
if (err)
log_error ("failed to run '%s': %s\n",
pgmname, gpg_strerror (err));
@@ -218,12 +220,11 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
gpg_error_t err;
encfs_parm_t parm;
runner_t runner = NULL;
- int outbound[2] = { -1, -1 };
- int inbound[2] = { -1, -1 };
const char *pgmname;
const char *argv[10];
- pid_t pid = (pid_t)(-1);
int idx;
+ gnupg_process_t proc;
+ int inbound, outbound;
(void)ctrl;
@@ -246,15 +247,6 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
if (err)
goto leave;
- err = gnupg_create_inbound_pipe (inbound, NULL, 0);
- if (!err)
- err = gnupg_create_outbound_pipe (outbound, NULL, 0);
- if (err)
- {
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- goto leave;
- }
-
pgmname = ENCFS;
idx = 0;
argv[idx++] = "-f";
@@ -267,47 +259,42 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
argv[idx++] = NULL;
assert (idx <= DIM (argv));
- err = gnupg_spawn_process_fd (pgmname, argv,
- outbound[0], -1, inbound[1], &pid);
+ err = gnupg_process_spawn (pgmname, argv,
+ (GNUPG_PROCESS_STDIN_PIPE
+ | GNUPG_PROCESS_STDERR_PIPE),
+ NULL, NULL, &proc);
if (err)
{
log_error ("error spawning '%s': %s\n", pgmname, gpg_strerror (err));
goto leave;
}
- close (outbound[0]); outbound[0] = -1;
- close ( inbound[1]); inbound[1] = -1;
- runner_set_fds (runner, inbound[0], outbound[1]);
- inbound[0] = -1; /* Now owned by RUNNER. */
- outbound[1] = -1; /* Now owned by RUNNER. */
+ err = gnupg_process_get_fds (proc, 0, &outbound, NULL, &inbound);
+ if (err)
+ {
+ log_error ("error get fds '%s': %s\n", pgmname, gpg_strerror (err));
+ gnupg_process_release (proc);
+ goto leave;
+ }
+
+ runner_set_fds (runner, inbound, outbound);
runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
parm = NULL; /* Now owned by RUNNER. */
- runner_set_pid (runner, pid);
- pid = (pid_t)(-1); /* The process is now owned by RUNNER. */
+ runner_set_proc (runner, proc);
err = runner_spawn (runner);
if (err)
- goto leave;
+ {
+ gnupg_process_release (proc);
+ goto leave;
+ }
*r_id = runner_get_rid (runner);
log_info ("running '%s' in the background\n", pgmname);
leave:
- if (inbound[0] != -1)
- close (inbound[0]);
- if (inbound[1] != -1)
- close (inbound[1]);
- if (outbound[0] != -1)
- close (outbound[0]);
- if (outbound[1] != -1)
- close (outbound[1]);
- if (pid != (pid_t)(-1))
- {
- gnupg_wait_process (pgmname, pid, 1, NULL);
- gnupg_release_process (pid);
- }
runner_release (runner);
encfs_handler_cleanup (parm);
return err;
diff --git a/g13/g13.c b/g13/g13.c
index 2bbb453eb..785c91950 100644
--- a/g13/g13.c
+++ b/g13/g13.c
@@ -40,6 +40,7 @@
#include "../common/gc-opt-flags.h"
#include "../common/asshelp.h"
#include "../common/init.h"
+#include "../common/exechelp.h"
#include "keyblob.h"
#include "server.h"
#include "runner.h"
diff --git a/g13/mount.c b/g13/mount.c
index 45b60806c..071b76b67 100644
--- a/g13/mount.c
+++ b/g13/mount.c
@@ -34,10 +34,11 @@
#include "backend.h"
#include "g13tuple.h"
#include "mountinfo.h"
-#include "runner.h"
#include "../common/host2net.h"
#include "server.h" /*(g13_keyblob_decrypt)*/
#include "../common/sysutils.h"
+#include "../common/exechelp.h"
+#include "runner.h"
#include "call-syshelp.h"
diff --git a/g13/runner.c b/g13/runner.c
index b08d99030..c0534fe5d 100644
--- a/g13/runner.c
+++ b/g13/runner.c
@@ -29,8 +29,8 @@
#include "g13.h"
#include "../common/i18n.h"
#include "keyblob.h"
-#include "runner.h"
#include "../common/exechelp.h"
+#include "runner.h"
#include "mountinfo.h"
/* The runner object. */
@@ -55,7 +55,7 @@ struct runner_s
2 = Thread is running and someone is holding a reference. */
int refcount;
- pid_t pid; /* PID of the backend's process (the engine). */
+ gnupg_process_t proc; /* Process of the backend's process (the engine). */
int in_fd; /* File descriptors to read from the engine. */
int out_fd; /* File descriptors to write to the engine. */
engine_handler_fnc_t handler; /* The handler functions. */
@@ -157,16 +157,16 @@ runner_release (runner_t runner)
if (runner->handler_cleanup)
runner->handler_cleanup (runner->handler_data);
- if (runner->pid != (pid_t)(-1))
+ if (runner->proc)
{
/* The process has not been cleaned up - do it now. */
- gnupg_kill_process (runner->pid);
+ gnupg_process_terminate (runner->proc);
/* (Actually we should use the program name and not the
arbitrary NAME of the runner object. However it does not
matter because that information is only used for
diagnostics.) */
- gnupg_wait_process (runner->name, runner->pid, 1, NULL);
- gnupg_release_process (runner->pid);
+ gnupg_process_wait (runner->proc, 1);
+ gnupg_process_release (runner->proc);
}
xfree (runner->name);
@@ -212,7 +212,7 @@ runner_new (runner_t *r_runner, const char *name)
return gpg_error_from_syserror ();
}
runner->refcount = 1;
- runner->pid = (pid_t)(-1);
+ runner->proc = NULL;
runner->in_fd = -1;
runner->out_fd = -1;
@@ -266,15 +266,15 @@ runner_set_fds (runner_t runner, int in_fd, int out_fd)
}
-/* Set the PID of the backend engine. After this call the engine is
+/* Set the PROC of the backend engine. After this call the engine is
owned by the runner object. */
void
-runner_set_pid (runner_t runner, pid_t pid)
+runner_set_proc (runner_t runner, gnupg_process_t proc)
{
- if (check_already_spawned (runner, "runner_set_fds"))
+ if (check_already_spawned (runner, "runner_set_proc"))
return;
- runner->pid = pid;
+ runner->proc = proc;
}
@@ -366,15 +366,17 @@ runner_thread (void *arg)
}
/* Now wait for the process to finish. */
- if (!err && runner->pid != (pid_t)(-1))
+ if (!err && runner->proc)
{
int exitcode;
log_debug ("runner thread waiting ...\n");
- err = gnupg_wait_process (runner->name, runner->pid, 1, &exitcode);
- gnupg_release_process (runner->pid);
- runner->pid = (pid_t)(-1);
- if (err)
+ err = gnupg_process_wait (runner->proc, 1);
+ if (!err)
+ gnupg_process_ctl (runner->proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
+ gnupg_process_release (runner->proc);
+ runner->proc = NULL;
+ if (exitcode)
log_error ("running '%s' failed (exitcode=%d): %s\n",
runner->name, exitcode, gpg_strerror (err));
log_debug ("runner thread waiting finished\n");
@@ -473,7 +475,7 @@ runner_cancel (runner_t runner)
need to change the thread to wait on an event. */
runner->cancel_flag = 1;
/* For now we use the brutal way and kill the process. */
- gnupg_kill_process (runner->pid);
+ gnupg_process_terminate (runner->proc);
}
}
diff --git a/g13/runner.h b/g13/runner.h
index 36181adf9..01c395e02 100644
--- a/g13/runner.h
+++ b/g13/runner.h
@@ -49,7 +49,7 @@ runner_t runner_find_by_rid (unsigned int rid);
/* Functions to set properties of the runner. */
void runner_set_fds (runner_t runner, int in_fd, int out_fd);
-void runner_set_pid (runner_t runner, pid_t pid);
+void runner_set_proc (runner_t runner, gnupg_process_t proc);
/* Register the handler functions with a runner. */
void runner_set_handler (runner_t runner,
diff --git a/scd/app.c b/scd/app.c
index 3686c0f6c..468fed294 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -2334,6 +2334,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;
@@ -2360,12 +2372,9 @@ report_change (int slot, int old_status, int cur_status)
else
{
gpg_error_t err;
- const char *args[9], *envs[2];
+ const char *args[9];
char numbuf1[30], numbuf2[30], numbuf3[30];
- envs[0] = envstr;
- envs[1] = NULL;
-
sprintf (numbuf1, "%d", slot);
sprintf (numbuf2, "0x%04X", old_status);
sprintf (numbuf3, "0x%04X", cur_status);
@@ -2382,7 +2391,9 @@ report_change (int slot, int old_status, int cur_status)
args[8] = NULL;
fname = make_filename (gnupg_homedir (), "scd-event", NULL);
- err = gnupg_spawn_process_detached (fname, args, envs);
+ err = gnupg_process_spawn (fname, args,
+ GNUPG_PROCESS_DETACHED,
+ setup_env, envstr, NULL);
if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
log_error ("failed to run event handler '%s': %s\n",
fname, gpg_strerror (err));
diff --git a/tests/gpgscm/ffi.c b/tests/gpgscm/ffi.c
index ce18e0794..b46d5cb61 100644
--- a/tests/gpgscm/ffi.c
+++ b/tests/gpgscm/ffi.c
@@ -42,6 +42,9 @@
#endif
#include "../../common/util.h"
+#ifdef HAVE_W32_SYSTEM
+#define NEED_STRUCT_SPAWN_CB_ARG
+#endif
#include "../../common/exechelp.h"
#include "../../common/sysutils.h"
@@ -753,25 +756,86 @@ do_es_write (scheme *sc, pointer args)
}
-
/* Process handling. */
+struct proc_object_box
+{
+ gnupg_process_t proc;
+};
+
+static void
+proc_object_finalize (scheme *sc, void *data)
+{
+ struct proc_object_box *box = data;
+ (void) sc;
+
+ if (!box->proc)
+ gnupg_process_release (box->proc);
+ xfree (box);
+}
+
+static void
+proc_object_to_string (scheme *sc, char *out, size_t size, void *data)
+{
+ struct proc_object_box *box = data;
+ (void) sc;
+
+ snprintf (out, size, "#proc %p", box->proc);
+}
+
+static struct foreign_object_vtable proc_object_vtable =
+ {
+ proc_object_finalize,
+ proc_object_to_string,
+ };
+
static pointer
-do_spawn_process (scheme *sc, pointer args)
+proc_wrap (scheme *sc, gnupg_process_t proc)
+{
+ struct proc_object_box *box = xmalloc (sizeof *box);
+ if (box == NULL)
+ return sc->NIL;
+
+ box->proc = proc;
+ return sc->vptr->mk_foreign_object (sc, &proc_object_vtable, box);
+}
+
+static struct proc_object_box *
+proc_unwrap (scheme *sc, pointer object)
+{
+ (void) sc;
+
+ if (! is_foreign_object (object))
+ return NULL;
+
+ if (sc->vptr->get_foreign_object_vtable (object) != &proc_object_vtable)
+ return NULL;
+
+ return sc->vptr->get_foreign_object_data (object);
+}
+
+#define CONVERSION_proc(SC, X) proc_unwrap (SC, X)
+#define IS_A_proc(SC, X) proc_unwrap (SC, X)
+
+
+static pointer
+do_process_spawn (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer arguments;
char **argv;
size_t len;
unsigned int flags;
-
+ gnupg_process_t proc = NULL;
estream_t infp;
estream_t outfp;
estream_t errfp;
- pid_t pid;
FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args);
FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args);
+ flags |= (GNUPG_PROCESS_STDIN_PIPE
+ | GNUPG_PROCESS_STDOUT_PIPE
+ | GNUPG_PROCESS_STDERR_PIPE);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = ffi_list2argv (sc, arguments, &argv, &len);
@@ -791,38 +855,55 @@ do_spawn_process (scheme *sc, pointer args)
fprintf (stderr, "\n");
}
- err = gnupg_spawn_process (argv[0], (const char **) &argv[1],
- NULL,
- flags,
- &infp, &outfp, &errfp, &pid);
+ err = gnupg_process_spawn (argv[0], (const char **) &argv[1],
+ flags, NULL, NULL, &proc);
+ err = gnupg_process_get_streams (proc, 0, &infp, &outfp, &errfp);
xfree (argv);
-#define IMC(A, B) \
- _cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
+#define IMP(A, B) \
+ _cons (sc, proc_wrap (sc, (A)), (B), 1)
#define IMS(A, B) \
_cons (sc, es_wrap (sc, (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMS (infp,
IMS (outfp,
IMS (errfp,
- IMC (pid, sc->NIL)))));
+ IMP (proc, sc->NIL)))));
#undef IMS
#undef IMC
}
+static void
+setup_std_fds (struct spawn_cb_arg *sca)
+{
+ int *std_fds = sca->arg;
+
+#ifdef HAVE_W32_SYSTEM
+ sca->hd[0] = std_fds[0] == -1?
+ INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[0]);
+ sca->hd[1] = std_fds[1] == -1?
+ INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[1]);
+ sca->hd[2] = std_fds[2] == -1?
+ INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[2]);
+#else
+ sca->fds[0] = std_fds[0];
+ sca->fds[1] = std_fds[1];
+ sca->fds[2] = std_fds[2];
+#endif
+}
+
static pointer
-do_spawn_process_fd (scheme *sc, pointer args)
+do_process_spawn_fd (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer arguments;
char **argv;
size_t len;
- int infd, outfd, errfd;
-
- pid_t pid;
+ int std_fds[3];
+ gnupg_process_t proc = NULL;
FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args);
- FFI_ARG_OR_RETURN (sc, int, infd, number, args);
- FFI_ARG_OR_RETURN (sc, int, outfd, number, args);
- FFI_ARG_OR_RETURN (sc, int, errfd, number, args);
+ FFI_ARG_OR_RETURN (sc, int, std_fds[0], number, args);
+ FFI_ARG_OR_RETURN (sc, int, std_fds[1], number, args);
+ FFI_ARG_OR_RETURN (sc, int, std_fds[2], number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = ffi_list2argv (sc, arguments, &argv, &len);
@@ -839,107 +920,35 @@ do_spawn_process_fd (scheme *sc, pointer args)
fprintf (stderr, "Executing:");
for (p = argv; *p; p++)
fprintf (stderr, " '%s'", *p);
- fprintf (stderr, "\n");
+ fprintf (stderr, " (%d %d %d)\n", std_fds[0], std_fds[1], std_fds[2]);
}
- err = gnupg_spawn_process_fd (argv[0], (const char **) &argv[1],
- infd, outfd, errfd, &pid);
+ err = gnupg_process_spawn (argv[0], (const char **) &argv[1],
+ 0, setup_std_fds, std_fds, &proc);
xfree (argv);
- FFI_RETURN_INT (sc, pid);
+ FFI_RETURN_POINTER (sc, proc_wrap (sc, proc));
}
static pointer
-do_wait_process (scheme *sc, pointer args)
+do_process_wait (scheme *sc, pointer args)
{
FFI_PROLOG ();
- const char *name;
- pid_t pid;
+ struct proc_object_box *box;
int hang;
+ int retcode = -1;
- int retcode;
-
- FFI_ARG_OR_RETURN (sc, const char *, name, string, args);
- FFI_ARG_OR_RETURN (sc, pid_t, pid, number, args);
+ FFI_ARG_OR_RETURN (sc, struct proc_object_box *, box, proc, args);
FFI_ARG_OR_RETURN (sc, int, hang, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
- err = gnupg_wait_process (name, pid, hang, &retcode);
- if (err == GPG_ERR_GENERAL)
- err = 0; /* Let the return code speak for itself. */
-
- 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..919e61195 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -3641,7 +3641,7 @@ cmd_gpg (card_info_t info, char *argstr, int use_gpgsm)
char **argarray;
ccparray_t ccp;
const char **argv = NULL;
- pid_t pid;
+ gnupg_process_t proc;
int i;
if (!info)
@@ -3669,15 +3669,15 @@ cmd_gpg (card_info_t info, char *argstr, int use_gpgsm)
goto leave;
}
- err = gnupg_spawn_process (use_gpgsm? opt.gpgsm_program:opt.gpg_program,
- argv, NULL, (GNUPG_SPAWN_KEEP_STDOUT
- |GNUPG_SPAWN_KEEP_STDERR),
- NULL, NULL, NULL, &pid);
+ err = gnupg_process_spawn (use_gpgsm? opt.gpgsm_program:opt.gpg_program,
+ argv,
+ (GNUPG_PROCESS_STDOUT_KEEP
+ | GNUPG_PROCESS_STDERR_KEEP),
+ NULL, NULL, &proc);
if (!err)
{
- err = gnupg_wait_process (use_gpgsm? opt.gpgsm_program:opt.gpg_program,
- pid, 1, NULL);
- gnupg_release_process (pid);
+ err = gnupg_process_wait (proc, 1);
+ gnupg_process_release (proc);
}
diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c
index 90f2f53d3..d6aa9d61b 100644
--- a/tools/gpgconf-comp.c
+++ b/tools/gpgconf-comp.c
@@ -744,7 +744,7 @@ gpg_agent_runtime_change (int killflag)
gpg_error_t err = 0;
const char *pgmname;
const char *argv[5];
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
int i = 0;
int cmdidx;
@@ -761,13 +761,13 @@ gpg_agent_runtime_change (int killflag)
log_assert (i < DIM(argv));
if (!err)
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc);
if (!err)
- err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ err = gnupg_process_wait (proc, 1);
if (err)
gc_error (0, 0, "error running '%s %s': %s",
pgmname, argv[cmdidx], gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
}
@@ -777,7 +777,7 @@ scdaemon_runtime_change (int killflag)
gpg_error_t err = 0;
const char *pgmname;
const char *argv[9];
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
int i = 0;
int cmdidx;
@@ -805,13 +805,13 @@ scdaemon_runtime_change (int killflag)
log_assert (i < DIM(argv));
if (!err)
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc);
if (!err)
- err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ err = gnupg_process_wait (proc, 1);
if (err)
gc_error (0, 0, "error running '%s %s': %s",
pgmname, argv[cmdidx], gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
}
@@ -822,7 +822,7 @@ tpm2daemon_runtime_change (int killflag)
gpg_error_t err = 0;
const char *pgmname;
const char *argv[9];
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
int i = 0;
int cmdidx;
@@ -850,13 +850,13 @@ tpm2daemon_runtime_change (int killflag)
log_assert (i < DIM(argv));
if (!err)
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc);
if (!err)
- err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ err = gnupg_process_wait (proc, 1);
if (err)
gc_error (0, 0, "error running '%s %s': %s",
pgmname, argv[cmdidx], gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
}
#endif
@@ -867,7 +867,7 @@ dirmngr_runtime_change (int killflag)
gpg_error_t err = 0;
const char *pgmname;
const char *argv[6];
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
int i = 0;
int cmdidx;
@@ -885,13 +885,13 @@ dirmngr_runtime_change (int killflag)
log_assert (i < DIM(argv));
if (!err)
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc);
if (!err)
- err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ err = gnupg_process_wait (proc, 1);
if (err)
gc_error (0, 0, "error running '%s %s': %s",
pgmname, argv[cmdidx], gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
}
@@ -901,7 +901,7 @@ keyboxd_runtime_change (int killflag)
gpg_error_t err = 0;
const char *pgmname;
const char *argv[6];
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
int i = 0;
int cmdidx;
@@ -919,13 +919,13 @@ keyboxd_runtime_change (int killflag)
log_assert (i < DIM(argv));
if (!err)
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc);
if (!err)
- err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ err = gnupg_process_wait (proc, 1);
if (err)
gc_error (0, 0, "error running '%s %s': %s",
pgmname, argv[cmdidx], gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
}
@@ -937,7 +937,7 @@ gc_component_launch (int component)
const char *pgmname;
const char *argv[6];
int i;
- pid_t pid;
+ gnupg_process_t proc = NULL;
if (component < 0)
{
@@ -985,9 +985,9 @@ gc_component_launch (int component)
argv[i] = NULL;
log_assert (i < DIM(argv));
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc);
if (!err)
- err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ err = gnupg_process_wait (proc, 1);
if (err)
gc_error (0, 0, "error running '%s%s%s': %s",
pgmname,
@@ -995,7 +995,7 @@ gc_component_launch (int component)
: component == GC_COMPONENT_KEYBOXD? " --keyboxd":"",
" NOP",
gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
return err;
}
@@ -1336,8 +1336,7 @@ gc_component_check_options (int component, estream_t out, const char *conf_file)
const char *pgmname;
const char *argv[6];
int i;
- pid_t pid;
- int exitcode;
+ gnupg_process_t proc;
estream_t errfp;
error_line_t errlines;
@@ -1370,22 +1369,28 @@ gc_component_check_options (int component, estream_t out, const char *conf_file)
result = 0;
errlines = NULL;
- err = gnupg_spawn_process (pgmname, argv, NULL, 0,
- NULL, NULL, &errfp, &pid);
+ err = gnupg_process_spawn (pgmname, argv,
+ GNUPG_PROCESS_STDERR_PIPE,
+ NULL, NULL, &proc);
if (err)
result |= 1; /* Program could not be run. */
else
{
+ gnupg_process_get_streams (proc, 0, NULL, NULL, &errfp);
errlines = collect_error_output (errfp,
gc_component[component].name);
- if (gnupg_wait_process (pgmname, pid, 1, &exitcode))
+ if (!gnupg_process_wait (proc, 1))
{
+ int exitcode;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
if (exitcode == -1)
result |= 1; /* Program could not be run or it
terminated abnormally. */
- result |= 2; /* Program returned an error. */
+ else if (exitcode)
+ result |= 2; /* Program returned an error. */
}
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
es_fclose (errfp);
}
@@ -1725,8 +1730,7 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
const char *pgmname;
const char *argv[2];
estream_t outfp;
- int exitcode;
- pid_t pid;
+ gnupg_process_t proc;
known_option_t *known_option;
gc_option_t *option;
char *line = NULL;
@@ -1759,14 +1763,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
/* First we need to read the option table from the program. */
argv[0] = "--dump-option-table";
argv[1] = NULL;
- err = gnupg_spawn_process (pgmname, argv, NULL, 0,
- NULL, &outfp, NULL, &pid);
+ err = gnupg_process_spawn (pgmname, argv,
+ GNUPG_PROCESS_STDOUT_PIPE,
+ NULL, NULL, &proc);
if (err)
{
gc_error (1, 0, "could not gather option table from '%s': %s",
pgmname, gpg_strerror (err));
}
+ gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL);
+
read_line_parm.pgmname = pgmname;
read_line_parm.fp = outfp;
read_line_parm.line = line;
@@ -1925,12 +1932,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
line_len = read_line_parm.line_len;
log_assert (opt_table_used + pseudo_count == opt_info_used);
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
+ {
+ int exitcode;
- err = gnupg_wait_process (pgmname, pid, 1, &exitcode);
- if (err)
- gc_error (1, 0, "running %s failed (exitcode=%d): %s",
- pgmname, exitcode, gpg_strerror (err));
- gnupg_release_process (pid);
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
+ if (exitcode)
+ gc_error (1, 0, "running %s failed (exitcode=%d): %s",
+ pgmname, exitcode, gpg_strerror (err));
+ }
+ gnupg_process_release (proc);
/* Make the gpgrt option table and the internal option table available. */
gc_component[component].opt_table = opt_table;
@@ -1940,14 +1952,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
/* Now read the default options. */
argv[0] = "--gpgconf-list";
argv[1] = NULL;
- err = gnupg_spawn_process (pgmname, argv, NULL, 0,
- NULL, &outfp, NULL, &pid);
+ err = gnupg_process_spawn (pgmname, argv,
+ GNUPG_PROCESS_STDOUT_PIPE,
+ NULL, NULL, &proc);
if (err)
{
gc_error (1, 0, "could not gather active options from '%s': %s",
pgmname, gpg_strerror (err));
}
+ gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL);
+
while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0)
{
char *linep;
@@ -2030,11 +2045,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
if (es_fclose (outfp))
gc_error (1, errno, "error closing %s", pgmname);
- err = gnupg_wait_process (pgmname, pid, 1, &exitcode);
- if (err)
- gc_error (1, 0, "running %s failed (exitcode=%d): %s",
- pgmname, exitcode, gpg_strerror (err));
- gnupg_release_process (pid);
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
+ {
+ int exitcode;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
+ if (exitcode)
+ gc_error (1, 0, "running %s failed (exitcode=%d): %s",
+ pgmname, exitcode, gpg_strerror (err));
+ }
+ gnupg_process_release (proc);
/* At this point, we can parse the configuration file. */
diff --git a/tools/gpgconf.c b/tools/gpgconf.c
index 6dcdc9f3c..522ce517b 100644
--- a/tools/gpgconf.c
+++ b/tools/gpgconf.c
@@ -1173,17 +1173,17 @@ show_versions_via_dirmngr (estream_t fp)
const char *pgmname;
const char *argv[2];
estream_t outfp;
- pid_t pid;
+ gnupg_process_t proc;
char *line = NULL;
size_t line_len = 0;
ssize_t length;
- int exitcode;
pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);
argv[0] = "--gpgconf-versions";
argv[1] = NULL;
- err = gnupg_spawn_process (pgmname, argv, NULL, 0,
- NULL, &outfp, NULL, &pid);
+ err = gnupg_process_spawn (pgmname, argv,
+ GNUPG_PROCESS_STDOUT_PIPE,
+ NULL, NULL, &proc);
if (err)
{
log_error ("error spawning %s: %s", pgmname, gpg_strerror (err));
@@ -1191,6 +1191,7 @@ show_versions_via_dirmngr (estream_t fp)
return;
}
+ gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL);
while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0)
{
/* Strip newline and carriage return, if present. */
@@ -1211,14 +1212,17 @@ show_versions_via_dirmngr (estream_t fp)
pgmname, gpg_strerror (err));
}
- err = gnupg_wait_process (pgmname, pid, 1, &exitcode);
- if (err)
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
{
+ int exitcode;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
log_error ("running %s failed (exitcode=%d): %s\n",
pgmname, exitcode, gpg_strerror (err));
es_fprintf (fp, "[error: can't get further info]\n");
}
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
xfree (line);
}
diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c
index 26d37a332..e6f5b55a2 100644
--- a/tools/gpgtar-create.c
+++ b/tools/gpgtar-create.c
@@ -1069,7 +1069,7 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names,
estream_t files_from_stream = NULL;
estream_t outstream = NULL;
int eof_seen = 0;
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
unsigned int skipped_open = 0;
memset (scanctrl, 0, sizeof *scanctrl);
@@ -1284,14 +1284,15 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names,
goto leave;
}
- err = gnupg_spawn_process (opt.gpg_program, argv,
- except[0] == -1? NULL : except,
- (GNUPG_SPAWN_KEEP_STDOUT
- | GNUPG_SPAWN_KEEP_STDERR),
- &outstream, NULL, NULL, &pid);
+ err = gnupg_process_spawn (opt.gpg_program, argv,
+ (GNUPG_PROCESS_STDIN_PIPE
+ | GNUPG_PROCESS_STDOUT_KEEP
+ | GNUPG_PROCESS_STDERR_KEEP),
+ gnupg_spawn_helper, except, &proc);
xfree (argv);
if (err)
goto leave;
+ gnupg_process_get_streams (proc, 0, &outstream, NULL, NULL);
es_set_binary (outstream);
}
else if (opt.outfile) /* No crypto */
@@ -1330,23 +1331,25 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names,
write_progress (1, global_written_files, global_total_files);
write_progress (0, global_written_data, global_total_data);
- if (pid != (pid_t)(-1))
+ if (proc)
{
- int exitcode;
-
err = es_fclose (outstream);
outstream = NULL;
if (err)
log_error ("error closing pipe: %s\n", gpg_strerror (err));
- else
+
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
{
- err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode);
- if (err)
+ int exitcode;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
+ if (exitcode)
log_error ("running %s failed (exitcode=%d): %s",
opt.gpg_program, exitcode, gpg_strerror (err));
- gnupg_release_process (pid);
- pid = (pid_t)(-1);
}
+ gnupg_process_release (proc);
+ proc = NULL;
}
if (skipped_open)
@@ -1359,7 +1362,7 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names,
if (!err)
{
gpg_error_t first_err;
- if (outstream != es_stdout || pid != (pid_t)(-1))
+ if (outstream != es_stdout)
first_err = es_fclose (outstream);
else
first_err = es_fflush (outstream);
diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c
index 936d03e3e..be483f87c 100644
--- a/tools/gpgtar-extract.c
+++ b/tools/gpgtar-extract.c
@@ -339,7 +339,7 @@ gpgtar_extract (const char *filename, int decrypt)
char *dirname = NULL;
struct tarinfo_s tarinfo_buffer;
tarinfo_t tarinfo = &tarinfo_buffer;
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc;
char *logfilename = NULL;
unsigned long long notextracted;
@@ -425,14 +425,14 @@ gpgtar_extract (const char *filename, int decrypt)
goto leave;
}
- err = gnupg_spawn_process (opt.gpg_program, argv,
- except[0] == -1? NULL : except,
- ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN)
- | GNUPG_SPAWN_KEEP_STDERR),
- NULL, &stream, NULL, &pid);
+ err = gnupg_process_spawn (opt.gpg_program, argv,
+ ((filename ? 0 : GNUPG_PROCESS_STDIN_KEEP)
+ | GNUPG_PROCESS_STDOUT_PIPE),
+ gnupg_spawn_helper, except, &proc);
xfree (argv);
if (err)
goto leave;
+ gnupg_process_get_streams (proc, 0, NULL, &stream, NULL);
es_set_binary (stream);
}
else if (filename)
@@ -472,23 +472,25 @@ gpgtar_extract (const char *filename, int decrypt)
header = NULL;
}
- if (pid != (pid_t)(-1))
+ if (proc)
{
- int exitcode;
-
err = es_fclose (stream);
stream = NULL;
if (err)
log_error ("error closing pipe: %s\n", gpg_strerror (err));
- else
+
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
{
- err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode);
- if (err)
+ int exitcode;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
+ if (exitcode)
log_error ("running %s failed (exitcode=%d): %s",
opt.gpg_program, exitcode, gpg_strerror (err));
- gnupg_release_process (pid);
- pid = (pid_t)(-1);
}
+ gnupg_process_release (proc);
+ proc = NULL;
}
leave:
diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c
index c5bf25825..31bcd8d46 100644
--- a/tools/gpgtar-list.c
+++ b/tools/gpgtar-list.c
@@ -460,7 +460,7 @@ gpgtar_list (const char *filename, int decrypt)
strlist_t extheader = NULL;
struct tarinfo_s tarinfo_buffer;
tarinfo_t tarinfo = &tarinfo_buffer;
- pid_t pid = (pid_t)(-1);
+ gnupg_process_t proc = NULL;
memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer);
@@ -503,14 +503,14 @@ gpgtar_list (const char *filename, int decrypt)
goto leave;
}
- err = gnupg_spawn_process (opt.gpg_program, argv,
- except[0] == -1? NULL : except,
- ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN)
- | GNUPG_SPAWN_KEEP_STDERR),
- NULL, &stream, NULL, &pid);
+ err = gnupg_process_spawn (opt.gpg_program, argv,
+ ((filename ? 0 : GNUPG_PROCESS_STDIN_KEEP)
+ | GNUPG_PROCESS_STDOUT_PIPE),
+ gnupg_spawn_helper, except, &proc);
xfree (argv);
if (err)
goto leave;
+ gnupg_process_get_streams (proc, 0, NULL, &stream, NULL);
es_set_binary (stream);
}
else if (filename) /* No decryption requested. */
@@ -550,23 +550,24 @@ gpgtar_list (const char *filename, int decrypt)
header = NULL;
}
- if (pid != (pid_t)(-1))
+ if (proc)
{
- int exitcode;
-
err = es_fclose (stream);
stream = NULL;
if (err)
log_error ("error closing pipe: %s\n", gpg_strerror (err));
- else
+
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
{
- err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode);
- if (err)
- log_error ("running %s failed (exitcode=%d): %s",
- opt.gpg_program, exitcode, gpg_strerror (err));
- gnupg_release_process (pid);
- pid = (pid_t)(-1);
+ int exitcode;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode);
+ log_error ("running %s failed (exitcode=%d): %s",
+ opt.gpg_program, exitcode, gpg_strerror (err));
}
+ gnupg_process_release (proc);
+ proc = NULL;
}
leave: