1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-02 12:01:32 +01:00

Remove old API implementation for spawn functions.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2023-04-17 14:14:16 +09:00
parent df357a0335
commit 5717d9ec1e
No known key found for this signature in database
GPG Key ID: 640114AF89DE6054
3 changed files with 0 additions and 1323 deletions

View File

@ -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,491 +364,6 @@ gnupg_close_pipe (int fd)
close (fd);
}
/* Fork and exec the PGMNAME, see exechelp.h for details. */
gpg_error_t
gnupg_spawn_process (const char *pgmname, const char *argv[],
int *except, unsigned int flags,
estream_t *r_infp,
estream_t *r_outfp,
estream_t *r_errfp,
pid_t *pid)
{
gpg_error_t err;
int inpipe[2] = {-1, -1};
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
estream_t infp = NULL;
estream_t outfp = NULL;
estream_t errfp = NULL;
int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
if (r_infp)
*r_infp = NULL;
if (r_outfp)
*r_outfp = NULL;
if (r_errfp)
*r_errfp = NULL;
*pid = (pid_t)(-1); /* Always required. */
if (r_infp)
{
err = create_pipe_and_estream (inpipe, &infp, 1, nonblock);
if (err)
return err;
}
if (r_outfp)
{
err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock);
if (err)
{
if (infp)
es_fclose (infp);
else if (inpipe[1] != -1)
close (inpipe[1]);
if (inpipe[0] != -1)
close (inpipe[0]);
return err;
}
}
if (r_errfp)
{
err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock);
if (err)
{
if (infp)
es_fclose (infp);
else if (inpipe[1] != -1)
close (inpipe[1]);
if (inpipe[0] != -1)
close (inpipe[0]);
if (outfp)
es_fclose (outfp);
else if (outpipe[0] != -1)
close (outpipe[0]);
if (outpipe[1] != -1)
close (outpipe[1]);
return err;
}
}
*pid = fork ();
if (*pid == (pid_t)(-1))
{
err = my_error_from_syserror ();
log_error (_("error forking process: %s\n"), gpg_strerror (err));
if (infp)
es_fclose (infp);
else if (inpipe[1] != -1)
close (inpipe[1]);
if (inpipe[0] != -1)
close (inpipe[0]);
if (outfp)
es_fclose (outfp);
else if (outpipe[0] != -1)
close (outpipe[0]);
if (outpipe[1] != -1)
close (outpipe[1]);
if (errfp)
es_fclose (errfp);
else if (errpipe[0] != -1)
close (errpipe[0]);
if (errpipe[1] != -1)
close (errpipe[1]);
return err;
}
if (!*pid)
{
/* This is the child. */
gcry_control (GCRYCTL_TERM_SECMEM);
es_fclose (infp);
es_fclose (outfp);
es_fclose (errfp);
do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
except, flags);
/*NOTREACHED*/
}
/* This is the parent. */
if (inpipe[0] != -1)
close (inpipe[0]);
if (outpipe[1] != -1)
close (outpipe[1]);
if (errpipe[1] != -1)
close (errpipe[1]);
if (r_infp)
*r_infp = infp;
if (r_outfp)
*r_outfp = outfp;
if (r_errfp)
*r_errfp = errfp;
return 0;
}
/* Simplified version of gnupg_spawn_process. This function forks and
then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
and ERRFD to stderr (any of them may be -1 to connect them to
/dev/null). The arguments for the process are expected in the NULL
terminated array ARGV. The program name itself should not be
included there. Calling gnupg_wait_process is required.
Returns 0 on success or an error code. */
gpg_error_t
gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
int infd, int outfd, int errfd, pid_t *pid)
{
gpg_error_t err;
*pid = fork ();
if (*pid == (pid_t)(-1))
{
err = my_error_from_syserror ();
log_error (_("error forking process: %s\n"), strerror (errno));
return err;
}
if (!*pid)
{
gcry_control (GCRYCTL_TERM_SECMEM);
/* Run child. */
do_exec (pgmname, argv, infd, outfd, errfd, NULL, 0);
/*NOTREACHED*/
}
return 0;
}
/* Waiting for child processes.
waitpid(2) may return information about terminated children that we
did not yet request, and there is no portable way to wait for a
specific set of children.
As a workaround, we store the results of children for later use.
XXX: This assumes that PIDs are not reused too quickly. */
struct terminated_child
{
pid_t pid;
int exitcode;
struct terminated_child *next;
};
struct terminated_child *terminated_children;
static gpg_error_t
store_result (pid_t pid, int exitcode)
{
struct terminated_child *c;
c = xtrymalloc (sizeof *c);
if (c == NULL)
return gpg_err_code_from_syserror ();
c->pid = pid;
c->exitcode = exitcode;
c->next = terminated_children;
terminated_children = c;
return 0;
}
static int
get_result (pid_t pid, int *r_exitcode)
{
struct terminated_child *c, **prevp;
for (prevp = &terminated_children, c = terminated_children;
c;
prevp = &c->next, c = c->next)
if (c->pid == pid)
{
*prevp = c->next;
*r_exitcode = c->exitcode;
xfree (c);
return 1;
}
return 0;
}
/* See exechelp.h for a description. */
gpg_error_t
gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
{
gpg_err_code_t ec;
int i, status;
if (r_exitcode)
*r_exitcode = -1;
if (pid == (pid_t)(-1))
return gpg_error (GPG_ERR_INV_VALUE);
#ifdef USE_NPTH
i = npth_waitpid (pid, &status, hang? 0:WNOHANG);
#else
while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
&& errno == EINTR);
#endif
if (i == (pid_t)(-1))
{
ec = gpg_err_code_from_errno (errno);
log_error (_("waiting for process %d to terminate failed: %s\n"),
(int)pid, strerror (errno));
}
else if (!i)
{
ec = GPG_ERR_TIMEOUT; /* Still running. */
}
else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
{
log_error (_("error running '%s': probably not installed\n"), pgmname);
ec = GPG_ERR_CONFIGURATION;
}
else if (WIFEXITED (status) && WEXITSTATUS (status))
{
if (!r_exitcode)
log_error (_("error running '%s': exit status %d\n"), pgmname,
WEXITSTATUS (status));
else
*r_exitcode = WEXITSTATUS (status);
ec = GPG_ERR_GENERAL;
}
else if (!WIFEXITED (status))
{
log_error (_("error running '%s': terminated\n"), pgmname);
ec = GPG_ERR_GENERAL;
}
else
{
if (r_exitcode)
*r_exitcode = 0;
ec = 0;
}
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
}
/* See exechelp.h for a description. */
gpg_error_t
gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
int hang, int *r_exitcodes)
{
gpg_err_code_t ec = 0;
size_t i, left;
int *dummy = NULL;
if (r_exitcodes == NULL)
{
dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count);
if (dummy == NULL)
return gpg_err_code_from_syserror ();
}
for (i = 0, left = count; i < count; i++)
{
int status = -1;
/* Skip invalid PID. */
if (pids[i] == (pid_t)(-1))
{
r_exitcodes[i] = -1;
left -= 1;
continue;
}
/* See if there was a previously stored result for this pid. */
if (get_result (pids[i], &status))
left -= 1;
r_exitcodes[i] = status;
}
while (left > 0)
{
pid_t pid;
int status;
#ifdef USE_NPTH
pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG);
#else
while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1)
&& errno == EINTR);
#endif
if (pid == (pid_t)(-1))
{
ec = gpg_err_code_from_errno (errno);
log_error (_("waiting for processes to terminate failed: %s\n"),
strerror (errno));
break;
}
else if (!pid)
{
ec = GPG_ERR_TIMEOUT; /* Still running. */
break;
}
else
{
for (i = 0; i < count; i++)
if (pid == pids[i])
break;
if (i == count)
{
/* No match, store this result. */
ec = store_result (pid, status);
if (ec)
break;
continue;
}
/* Process PIDS[i] died. */
if (r_exitcodes[i] != (pid_t) -1)
{
log_error ("PID %d was reused", pid);
ec = GPG_ERR_GENERAL;
break;
}
left -= 1;
r_exitcodes[i] = status;
}
}
for (i = 0; i < count; i++)
{
if (r_exitcodes[i] == -1)
continue;
if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
{
log_error (_("error running '%s': probably not installed\n"),
pgmnames[i]);
ec = GPG_ERR_CONFIGURATION;
}
else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
{
if (dummy)
log_error (_("error running '%s': exit status %d\n"),
pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
else
r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
ec = GPG_ERR_GENERAL;
}
else if (!WIFEXITED (r_exitcodes[i]))
{
log_error (_("error running '%s': terminated\n"), pgmnames[i]);
ec = GPG_ERR_GENERAL;
}
}
xfree (dummy);
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
}
void
gnupg_release_process (pid_t pid)
{
(void)pid;
}
/* Spawn a new process and immediately detach from it. The name of
the program to exec is PGMNAME and its arguments are in ARGV (the
programname is automatically passed as first argument).
Environment strings in ENVP are set. An error is returned if
pgmname is not executable; to make this work it is necessary to
provide an absolute file name. All standard file descriptors are
connected to /dev/null. */
gpg_error_t
gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
const char *envp[] )
{
gpg_err_code_t ec;
pid_t pid;
int i;
if (getuid() != geteuid())
return my_error (GPG_ERR_BUG);
if ((ec = gnupg_access (pgmname, X_OK)))
return gpg_err_make (default_errsource, ec);
pid = fork ();
if (pid == (pid_t)(-1))
{
log_error (_("error forking process: %s\n"), strerror (errno));
return my_error_from_syserror ();
}
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. */
if (envp)
for (i=0; envp[i]; i++)
putenv (xstrdup (envp[i]));
do_exec (pgmname, argv, -1, -1, -1, NULL, 0);
/*NOTREACHED*/
}
if (waitpid (pid, NULL, 0) == -1)
log_error ("waitpid failed in gnupg_spawn_process_detached: %s",
strerror (errno));
return 0;
}
/* Kill a process; that is send an appropriate signal to the process.
gnupg_wait_process must be called to actually remove the process
from the system. An invalid PID is ignored. */
void
gnupg_kill_process (pid_t pid)
{
if (pid != (pid_t)(-1))
{
kill (pid, SIGTERM);
}
}
#include <sys/socket.h>
struct gnupg_process {

View File

@ -409,649 +409,6 @@ gnupg_close_pipe (int fd)
if (fd != -1)
close (fd);
}
/* 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;
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);
(void)except; /* Not yet used. */
if (r_infp)
*r_infp = NULL;
if (r_outfp)
*r_outfp = NULL;
if (r_errfp)
*r_errfp = NULL;
*pid = (pid_t)(-1); /* Always required. */
if (r_infp)
{
if (create_inheritable_pipe (inpipe, INHERIT_READ))
{
err = gpg_err_make (errsource, GPG_ERR_GENERAL);
log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
return err;
}
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = inpipe[1];
infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
if (!infp)
{
err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
log_error (_("error creating a stream for a pipe: %s\n"),
gpg_strerror (err));
CloseHandle (inpipe[0]);
CloseHandle (inpipe[1]);
inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
return err;
}
}
if (r_outfp)
{
if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
{
err = gpg_err_make (errsource, GPG_ERR_GENERAL);
log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
return err;
}
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = outpipe[0];
outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
if (!outfp)
{
err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
log_error (_("error creating a stream for a pipe: %s\n"),
gpg_strerror (err));
CloseHandle (outpipe[0]);
CloseHandle (outpipe[1]);
outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
if (infp)
es_fclose (infp);
else if (inpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[1]);
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
return err;
}
}
if (r_errfp)
{
if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
{
err = gpg_err_make (errsource, GPG_ERR_GENERAL);
log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
return err;
}
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = errpipe[0];
errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
if (!errfp)
{
err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
log_error (_("error creating a stream for a pipe: %s\n"),
gpg_strerror (err));
CloseHandle (errpipe[0]);
CloseHandle (errpipe[1]);
errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
if (outfp)
es_fclose (outfp);
else if (outpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[0]);
if (outpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (infp)
es_fclose (infp);
else if (inpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[1]);
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
return err;
}
}
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
if (inpipe[0] == INVALID_HANDLE_VALUE)
nullhd[0] = ((flags & GNUPG_SPAWN_KEEP_STDIN)?
GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0));
if (outpipe[1] == INVALID_HANDLE_VALUE)
nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)?
GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1));
if (errpipe[1] == INVALID_HANDLE_VALUE)
nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)?
GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1));
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE;
si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED);
/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", */
/* pgmname, cmdline); */
/* Take care: CreateProcessW may modify wpgmname */
if (!(wpgmname = utf8_to_wchar (pgmname)))
rc = 0;
else if (!(wcmdline = utf8_to_wchar (cmdline)))
rc = 0;
else
rc = CreateProcessW (wpgmname, /* Program to start. */
wcmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
cr_flags, /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
);
if (!rc)
{
if (!wpgmname || !wcmdline)
log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
strerror (errno));
else
log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
if (infp)
es_fclose (infp);
else if (inpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
if (outfp)
es_fclose (outfp);
else if (outpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[0]);
if (outpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (errfp)
es_fclose (errfp);
else if (errpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (errpipe[0]);
if (errpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (errpipe[1]);
return gpg_err_make (errsource, GPG_ERR_GENERAL);
}
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
cmdline = NULL;
/* Close the inherited handles to /dev/null. */
for (i=0; i < DIM (nullhd); i++)
if (nullhd[i] != INVALID_HANDLE_VALUE)
CloseHandle (nullhd[i]);
/* Close the inherited ends of the pipes. */
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
if (outpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (errpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (errpipe[1]);
/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
/* " dwProcessID=%d dwThreadId=%d\n", */
/* pi.hProcess, pi.hThread, */
/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
/* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */
/* Fixme: For unknown reasons AllowSetForegroundWindow returns an
invalid argument error if we pass it the correct processID. As a
workaround we use -1 (ASFW_ANY). */
if ((flags & GNUPG_SPAWN_RUN_ASFW))
gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
/* Process has been created suspended; resume it now. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
if (r_infp)
*r_infp = infp;
if (r_outfp)
*r_outfp = outfp;
if (r_errfp)
*r_errfp = errfp;
*pid = handle_to_pid (pi.hProcess);
return 0;
}
/* Simplified version of gnupg_spawn_process. This function forks and
then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
and ERRFD to stderr (any of them may be -1 to connect them to
/dev/null). The arguments for the process are expected in the NULL
terminated array ARGV. The program name itself should not be
included there. Calling gnupg_wait_process is required.
Returns 0 on success or an error code. */
gpg_error_t
gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
int infd, int outfd, int errfd, pid_t *pid)
{
gpg_error_t err;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
STARTUPINFOW si;
char *cmdline;
wchar_t *wcmdline = NULL;
wchar_t *wpgmname = NULL;
int i, rc;
HANDLE stdhd[3];
/* Setup return values. */
*pid = (pid_t)(-1);
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
/* Take care: CreateProcessW may modify wpgmname */
if (!(wpgmname = utf8_to_wchar (pgmname)))
rc = 0;
else if (!(wcmdline = utf8_to_wchar (cmdline)))
rc = 0;
else
rc = CreateProcessW (wpgmname, /* Program to start. */
wcmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
(CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED | DETACHED_PROCESS),
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
);
if (!rc)
{
if (!wpgmname || !wcmdline)
log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
strerror (errno));
else
log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
err = my_error (GPG_ERR_GENERAL);
}
else
err = 0;
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
for (i=0; i < 3; i++)
if (stdhd[i] != INVALID_HANDLE_VALUE)
CloseHandle (stdhd[i]);
if (err)
return err;
/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
/* " dwProcessID=%d dwThreadId=%d\n", */
/* pi.hProcess, pi.hThread, */
/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
/* Process has been created suspended; resume it now. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
*pid = handle_to_pid (pi.hProcess);
return 0;
}
/* See exechelp.h for a description. */
gpg_error_t
gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
{
return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
}
/* See exechelp.h for a description. */
gpg_error_t
gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
int hang, int *r_exitcodes)
{
gpg_err_code_t ec = 0;
size_t i;
HANDLE *procs;
int code;
procs = xtrycalloc (count, sizeof *procs);
if (procs == NULL)
return my_error_from_syserror ();
for (i = 0; i < count; i++)
{
if (r_exitcodes)
r_exitcodes[i] = -1;
if (pids[i] == (pid_t)(-1))
return my_error (GPG_ERR_INV_VALUE);
procs[i] = pid_to_handle (pids[i]);
}
/* FIXME: We should do a pth_waitpid here. However this has not yet
been implemented. A special W32 pth system call would even be
better. */
code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
switch (code)
{
case WAIT_TIMEOUT:
ec = GPG_ERR_TIMEOUT;
goto leave;
case WAIT_FAILED:
log_error (_("waiting for processes to terminate failed: %s\n"),
w32_strerror (-1));
ec = GPG_ERR_GENERAL;
goto leave;
case WAIT_OBJECT_0:
for (i = 0; i < count; i++)
{
DWORD exc;
if (! GetExitCodeProcess (procs[i], &exc))
{
log_error (_("error getting exit code of process %d: %s\n"),
(int) pids[i], w32_strerror (-1) );
ec = GPG_ERR_GENERAL;
}
else if (exc)
{
if (!r_exitcodes)
log_error (_("error running '%s': exit status %d\n"),
pgmnames[i], (int)exc);
else
r_exitcodes[i] = (int)exc;
ec = GPG_ERR_GENERAL;
}
else
{
if (r_exitcodes)
r_exitcodes[i] = 0;
}
}
break;
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);
}
void
gnupg_release_process (pid_t pid)
{
if (pid != (pid_t)INVALID_HANDLE_VALUE)
{
HANDLE process = (HANDLE)pid;
CloseHandle (process);
}
}
/* Spawn a new process and immediately detach from it. The name of
the program to exec is PGMNAME and its arguments are in ARGV (the
programname is automatically passed as first argument).
Environment strings in ENVP are set. An error is returned if
pgmname is not executable; to make this work it is necessary to
provide an absolute file name. All standard file descriptors are
connected to /dev/null. */
gpg_error_t
gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
const char *envp[] )
{
gpg_error_t err;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFOW si;
int cr_flags;
char *cmdline;
wchar_t *wcmdline = NULL;
wchar_t *wpgmname = NULL;
BOOL in_job = FALSE;
gpg_err_code_t ec;
int rc;
int jobdebug;
/* We don't use ENVP. */
(void)envp;
cmdline = getenv ("GNUPG_EXEC_DEBUG_FLAGS");
jobdebug = (cmdline && (atoi (cmdline) & 1));
if ((ec = gnupg_access (pgmname, X_OK)))
return gpg_err_make (default_errsource, ec);
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
/* Start the process. */
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_NEW_PROCESS_GROUP
| DETACHED_PROCESS);
/* Check if we were spawned as part of a Job.
* In a job we need to add CREATE_BREAKAWAY_FROM_JOB
* to the cr_flags, otherwise our child processes
* are killed when we terminate. */
if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job))
{
log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1));
in_job = FALSE;
}
if (in_job)
{
/* Only try to break away from job if it is allowed, otherwise
* CreateProcess() would fail with an "Access is denied" error. */
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation,
&info, sizeof info, NULL))
{
log_error ("QueryInformationJobObject() failed: %s\n",
w32_strerror (-1));
}
else if ((info.BasicLimitInformation.LimitFlags &
JOB_OBJECT_LIMIT_BREAKAWAY_OK))
{
if (jobdebug)
log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n");
cr_flags |= CREATE_BREAKAWAY_FROM_JOB;
}
else if ((info.BasicLimitInformation.LimitFlags &
JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))
{
/* The child process should automatically detach from the job. */
if (jobdebug)
log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; "
"JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n");
}
else
{
/* It seems that the child process must remain in the job.
* This is not necessarily an error, although it can cause premature
* termination of the child process when the job is closed. */
if (jobdebug)
log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n");
}
}
else
{
if (jobdebug)
log_debug ("Process is not in a Job\n");
}
/* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
/* pgmname, cmdline); */
/* Take care: CreateProcessW may modify wpgmname */
if (!(wpgmname = utf8_to_wchar (pgmname)))
rc = 0;
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 (!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);
}
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;
}
/* 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)
{
if (pid != (pid_t) INVALID_HANDLE_VALUE)
{
HANDLE process = (HANDLE) pid;
/* Arbitrary error code. */
TerminateProcess (process, 1);
}
}
struct gnupg_process {
const char *pgmname;

View File

@ -80,134 +80,6 @@ void gnupg_close_pipe (int fd);
#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