2008-06-25  Marcus Brinkmann  <marcus@g10code.de>

	* assuan-pipe-connect.c (struct spawn_fd_item_s): Add new members.
	(HANDLE_TRANSLATION): New macro.
	(pipe_connect_gpgme): Adjust caller of _gpgme_io_spawn.
	[HANDLE_TRANSLATION]: Return translated handles.

gpgme/
2008-06-25  Marcus Brinkmann  <marcus@g10code.de>

	* gpgme-w32spawn.c: New file.
	* Makefile.am (libexec_PROGRAMS) [HAVE_W32_SYSTEM]: New variable
	with gpgme-w32spawn.
	* engine-gpgsm.c (gpgsm_new): Use server translated handles.
	(gpgsm_set_locale): Return early if locale value is NULL.
	* util.h (_gpgme_mkstemp)
	(_gpgme_get_w32spawn_path) [HAVE_W32_SYSTEM]: New function
	prototypes.
	* w32-util.c: Include <stdint.h>, <sys/stat.h> and <unistd.h>.
	(letters, mkstemp, _gpgme_mkstemp, _gpgme_get_w32spawn_path): New
	functions.
	* rungpg.c (gpg_decrypt, gpg_encrypt, gpg_encrypt_sign)
	(gpg_genkey, gpg_import, gpg_verify, gpg_sign): Pass data over
	special filename FD rather than stdin.
	(struct arg_and_data_s): Add member ARG_LOCP.
	(struct fd_data_map_s): Add member ARG_LOC.
	(struct engine_gpg): Add member ARG_LOC to status and colon.
	(_add_arg, add_arg_with_locp): New function.
	(add_arg_ext): Reimplement in terms of _add_arg.
	(gpg_new): Remember argument location for status FD.
	(build_argv): Set argument location if requested.  Also set
	argument location of fd_data_map for data items.
	(start): Adjust caller of _gpgme_io_spawn.
	* priv-io.h (struct spawn_fd_item_s): Add members peer_name and
	arg_loc.
	(_gpgme_io_spawn): Remove parent fd list argument.
	* posix-io.c (get_max_fds): New function.
	(_gpgme_io_dup): Add tracing.
	(_gpgme_io_spawn): Remove parent fd list.  Change meaning of child
	fd list to contain all child fds that should be inherited.  Close
	all other file descriptors after fork.
	* w32-io.c, w32-glib-io.c, w32-qt-io.c(_gpgme_io_spawn): Remove
	parent fd list.  Change meaning of child fd list to contain all
	child fds that should be inherited.  Do not inherit any file
	descriptors, but DuplicateHandle them.  Spawn process through
	wrapper process.  Provide wrapper process with a temporary file
	containing handle translation data.  Return translated handle
	names.
	* w32-io.c (reader): Add more tracing output.
	(_gpgme_io_read): Likewise.
	* engine-gpgconf.c (gpgconf_read): Adjust caller of
	_gpgme_io_spawn.
	* version.c (_gpgme_get_program_version): Likewise.
This commit is contained in:
Marcus Brinkmann 2008-06-25 16:52:31 +00:00
parent d0fe86179c
commit 0560f3089b
17 changed files with 1356 additions and 419 deletions

View File

@ -1,3 +1,10 @@
2008-06-25 Marcus Brinkmann <marcus@g10code.de>
* assuan-pipe-connect.c (struct spawn_fd_item_s): Add new members.
(HANDLE_TRANSLATION): New macro.
(pipe_connect_gpgme): Adjust caller of _gpgme_io_spawn.
[HANDLE_TRANSLATION]: Return translated handles.
2008-02-14 Werner Koch <wk@g10code.com>
* assuan-pipe-connect.c (_gpgme_io_spawn): Adjust prototype.

View File

@ -41,10 +41,19 @@
#ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
/* From GPGME priv-io.h */
struct spawn_fd_item_s
{
int fd;
int dup_to;
int peer_name;
int arg_loc;
};
int _gpgme_io_pipe (int filedes[2], int inherit_idx);
int _gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid);
struct spawn_fd_item_s *fd_list, pid_t *r_pid);
#endif
/* Hacks for Slowaris. */
@ -566,13 +575,6 @@ socketpair_connect (assuan_context_t *ctx,
#define pipe_connect pipe_connect_gpgme
/* From GPGME priv-io.h */
struct spawn_fd_item_s
{
int fd;
int dup_to;
};
/* W32 version of the pipe connection code. */
static assuan_error_t
pipe_connect_gpgme (assuan_context_t *ctx,
@ -583,14 +585,25 @@ pipe_connect_gpgme (assuan_context_t *ctx,
{
assuan_error_t err;
int res;
int idx;
int nr;
int rp[2];
int wp[2];
char mypidstr[50];
struct spawn_fd_item_s child_fds[3]; /* stdin, stdout, terminating -1 */
struct spawn_fd_item_s *child_fds;
if (!ctx || !name || !argv || !argv[0])
return _assuan_error (ASSUAN_Invalid_Value);
/* stdin, stdout, terminating -1 */
nr = 3;
for (idx = 0; fd_child_list[idx] != -1; idx++)
nr++;
child_fds = calloc (nr, sizeof *child_fds);
if (! child_fds)
return _assuan_error (ASSUAN_Out_Of_Core);
/* Actually, GPGME does this for us. But we plan to reuse this code
in the generic assuan. */
fix_signals ();
@ -631,19 +644,28 @@ pipe_connect_gpgme (assuan_context_t *ctx,
the old value, changeit, create proces and restore it, is not
thread safe. */
/* Parent list is same as client list. Note that GPGME will dup nul
to stderr even if the caller wants to inherit the handle for
it. */
nr = 0;
/* Server stdout is its write end of our read pipe. */
child_fds[0].fd = rp[1];
child_fds[0].dup_to = 1;
child_fds[nr].fd = rp[1];
child_fds[nr].dup_to = 1;
nr++;
/* Server stdin is its read end of our write pipe. */
child_fds[1].fd = wp[0];
child_fds[1].dup_to = 0;
child_fds[2].fd = -1;
child_fds[nr].fd = wp[0];
child_fds[nr].dup_to = 0;
nr++;
for (idx = 0; fd_child_list[idx] != -1; idx++)
{
child_fds[nr].fd = fd_child_list[idx];
child_fds[nr].dup_to = -1;
nr++;
}
child_fds[nr].fd = -1;
child_fds[nr].dup_to = -1;
/* Start the process. */
res = _gpgme_io_spawn (name, argv, child_fds, child_fds, NULL);
res = _gpgme_io_spawn (name, argv, child_fds, NULL);
if (res == -1)
{
_assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno));
@ -653,6 +675,14 @@ pipe_connect_gpgme (assuan_context_t *ctx,
_gpgme_io_close (wp[1]);
return _assuan_error (ASSUAN_General_Error);
}
else
{
/* For W32, the user needs to know the server-local names of the
inherited handles. Return them here. */
for (idx = 0; fd_child_list[idx] != -1; idx++)
/* We add 2 to skip over the stdin/stdout pair. */
fd_child_list[idx] = child_fds[idx + 2].peer_name;
}
(*ctx)->pid = 0; /* We don't use the PID. */

View File

@ -1,3 +1,49 @@
2008-06-25 Marcus Brinkmann <marcus@g10code.de>
* gpgme-w32spawn.c: New file.
* Makefile.am (libexec_PROGRAMS) [HAVE_W32_SYSTEM]: New variable
with gpgme-w32spawn.
* engine-gpgsm.c (gpgsm_new): Use server translated handles.
(gpgsm_set_locale): Return early if locale value is NULL.
* util.h (_gpgme_mkstemp)
(_gpgme_get_w32spawn_path) [HAVE_W32_SYSTEM]: New function
prototypes.
* w32-util.c: Include <stdint.h>, <sys/stat.h> and <unistd.h>.
(letters, mkstemp, _gpgme_mkstemp, _gpgme_get_w32spawn_path): New
functions.
* rungpg.c (gpg_decrypt, gpg_encrypt, gpg_encrypt_sign)
(gpg_genkey, gpg_import, gpg_verify, gpg_sign): Pass data over
special filename FD rather than stdin.
(struct arg_and_data_s): Add member ARG_LOCP.
(struct fd_data_map_s): Add member ARG_LOC.
(struct engine_gpg): Add member ARG_LOC to status and colon.
(_add_arg, add_arg_with_locp): New function.
(add_arg_ext): Reimplement in terms of _add_arg.
(gpg_new): Remember argument location for status FD.
(build_argv): Set argument location if requested. Also set
argument location of fd_data_map for data items.
(start): Adjust caller of _gpgme_io_spawn.
* priv-io.h (struct spawn_fd_item_s): Add members peer_name and
arg_loc.
(_gpgme_io_spawn): Remove parent fd list argument.
* posix-io.c (get_max_fds): New function.
(_gpgme_io_dup): Add tracing.
(_gpgme_io_spawn): Remove parent fd list. Change meaning of child
fd list to contain all child fds that should be inherited. Close
all other file descriptors after fork.
* w32-io.c, w32-glib-io.c, w32-qt-io.c(_gpgme_io_spawn): Remove
parent fd list. Change meaning of child fd list to contain all
child fds that should be inherited. Do not inherit any file
descriptors, but DuplicateHandle them. Spawn process through
wrapper process. Provide wrapper process with a temporary file
containing handle translation data. Return translated handle
names.
* w32-io.c (reader): Add more tracing output.
(_gpgme_io_read): Likewise.
* engine-gpgconf.c (gpgconf_read): Adjust caller of
_gpgme_io_spawn.
* version.c (_gpgme_get_program_version): Likewise.
2008-06-20 Werner Koch <wk@g10code.com>
* engine-gpgconf.c (gpgconf_read): Change ARGV initialization for

View File

@ -142,6 +142,12 @@ AM_CFLAGS = @PTH_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@
if HAVE_W32_SYSTEM
# Windows provides us with an endless stream of Tough Love. To spawn
# processes with a controlled set of inherited handles, we need a
# wrapper process.
libexec_PROGRAMS = gpgme-w32spawn
LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \
`echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
sed -e 's/-I/--include-dir /g;s/-D/--define /g'`

View File

@ -201,8 +201,8 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
int linelen = 0;
char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
int rp[2];
struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
{-1, -1} };
int status;
int nread;
char *mark = NULL;
@ -219,10 +219,9 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
if (_gpgme_io_pipe (rp, 1) < 0)
return gpg_error_from_syserror ();
pfd[0].fd = rp[1];
cfd[0].fd = rp[1];
status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd, NULL);
status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, NULL);
if (status < 0)
{
_gpgme_io_close (rp[0]);
@ -645,7 +644,6 @@ gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
int buflen = 0;
char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
int rp[2];
struct spawn_fd_item_s pfd[] = { {1, -1}, {-1, -1} };
struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
int status;
int nwrite;
@ -659,10 +657,9 @@ gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
if (_gpgme_io_pipe (rp, 0) < 0)
return gpg_error_from_syserror ();
pfd[0].fd = rp[0];
cfd[0].fd = rp[0];
status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd, NULL);
status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, NULL);
if (status < 0)
{
_gpgme_io_close (rp[0]);

View File

@ -405,9 +405,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
}
gpgsm->input_cb.fd = fds[1];
gpgsm->input_cb.server_fd = fds[0];
_gpgme_io_fd2str (gpgsm->input_cb.server_fd_str,
sizeof gpgsm->input_cb.server_fd_str,
gpgsm->input_cb.server_fd);
if (_gpgme_io_pipe (fds, 1) < 0)
{
@ -416,9 +413,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
}
gpgsm->output_cb.fd = fds[0];
gpgsm->output_cb.server_fd = fds[1];
_gpgme_io_fd2str (gpgsm->output_cb.server_fd_str,
sizeof gpgsm->output_cb.server_fd_str,
gpgsm->output_cb.server_fd);
if (_gpgme_io_pipe (fds, 0) < 0)
{
@ -427,9 +421,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
}
gpgsm->message_cb.fd = fds[1];
gpgsm->message_cb.server_fd = fds[0];
_gpgme_io_fd2str (gpgsm->message_cb.server_fd_str,
sizeof gpgsm->message_cb.server_fd_str,
gpgsm->message_cb.server_fd);
child_fds[0] = gpgsm->input_cb.server_fd;
child_fds[1] = gpgsm->output_cb.server_fd;
@ -455,10 +446,35 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
err = assuan_pipe_connect
(&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
argv, child_fds);
/* On Windows, handles are inserted in the spawned process with
DuplicateHandle, and child_fds contains the server-local names
for the inserted handles when assuan_pipe_connect returns. */
if (!err)
{
/* Note: We don't use _gpgme_io_fd2str here. On W32 the
returned handles are real W32 system handles, not whatever
GPGME uses internally (which may be a system handle, a C
library handle or a GLib/Qt channel. Confusing, yes, but
remember these are server-local names, so they are not part
of GPGME at all. */
snprintf (gpgsm->input_cb.server_fd_str,
sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
snprintf (gpgsm->output_cb.server_fd_str,
sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
snprintf (gpgsm->message_cb.server_fd_str,
sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
}
#endif
if (err)
goto leave;
/* assuan_pipe_connect in this case uses _gpgme_io_spawn which
closes the child fds for us. */
gpgsm->input_cb.server_fd = -1;
gpgsm->output_cb.server_fd = -1;
gpgsm->message_cb.server_fd = -1;
err = _gpgme_getenv ("DISPLAY", &dft_display);
if (err)
goto leave;
@ -623,6 +639,10 @@ gpgsm_set_locale (void *engine, int category, const char *value)
else
return gpg_error (GPG_ERR_INV_VALUE);
/* FIXME: Reset value to default. */
if (!value)
return 0;
if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
err = gpg_error_from_errno (errno);
else
@ -633,6 +653,7 @@ gpgsm_set_locale (void *engine, int category, const char *value)
if (err)
err = map_assuan_error (err);
}
return err;
}

434
gpgme/gpgme-w32spawn.c Normal file
View File

@ -0,0 +1,434 @@
/* gpgme-w32spawn.c - Wrapper to spawn a process under Windows.
Copyright (C) 2008 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
GPGME is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <process.h>
#include <windows.h>
struct spawn_fd_item_s
{
int handle;
int dup_to;
int peer_name;
int arg_loc;
};
static char *
build_commandline (char **argv)
{
int i;
int n = 0;
char *buf;
char *p;
/* We have to quote some things because under Windows the program
parses the commandline and does some unquoting. We enclose the
whole argument in double-quotes, and escape literal double-quotes
as well as backslashes with a backslash. We end up with a
trailing space at the end of the line, but that is harmless. */
for (i = 0; argv[i]; i++)
{
p = argv[i];
/* The leading double-quote. */
n++;
while (*p)
{
/* An extra one for each literal that must be escaped. */
if (*p == '\\' || *p == '"')
n++;
n++;
p++;
}
/* The trailing double-quote and the delimiter. */
n += 2;
}
/* And a trailing zero. */
n++;
buf = p = malloc (n);
if (!buf)
return NULL;
for (i = 0; argv[i]; i++)
{
char *argvp = argv[i];
*(p++) = '"';
while (*argvp)
{
if (*argvp == '\\' || *argvp == '"')
*(p++) = '\\';
*(p++) = *(argvp++);
}
*(p++) = '"';
*(p++) = ' ';
}
*(p++) = 0;
return buf;
}
int
my_spawn (char **argv, struct spawn_fd_item_s *fd_list)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* returns process handle */
0, /* returns primary thread handle */
0, /* returns pid */
0 /* returns tid */
};
STARTUPINFO si;
char *envblock = NULL;
int cr_flags = CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ());
int i;
char *arg_string;
int duped_stdin = 0;
int duped_stdout = 0;
int duped_stderr = 0;
HANDLE hnul = INVALID_HANDLE_VALUE;
/* FIXME. */
int debug_me = 0;
i = 0;
while (argv[i])
{
fprintf (stderr, "argv[%2i] = %s\n", i, argv[i]);
i++;
}
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
arg_string = build_commandline (argv);
if (!arg_string)
return -1;
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
fprintf (stderr, "spawning: %s\n", arg_string);
for (i = 0; fd_list[i].handle != -1; i++)
{
/* The handle already is inheritable. */
if (fd_list[i].dup_to == 0)
{
si.hStdInput = (HANDLE) fd_list[i].peer_name;
duped_stdin = 1;
fprintf (stderr, "dup 0x%x to stdin\n", fd_list[i].peer_name);
}
else if (fd_list[i].dup_to == 1)
{
si.hStdOutput = (HANDLE) fd_list[i].peer_name;
duped_stdout = 1;
fprintf (stderr, "dup 0x%x to stdout\n", fd_list[i].peer_name);
}
else if (fd_list[i].dup_to == 2)
{
si.hStdError = (HANDLE) fd_list[i].peer_name;
duped_stderr = 1;
fprintf (stderr, "dup 0x%x to stderr\n", fd_list[i].peer_name);
}
}
if (!duped_stdin || !duped_stdout || !duped_stderr)
{
SECURITY_ATTRIBUTES sa;
memset (&sa, 0, sizeof sa);
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
hnul = CreateFile ("nul",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hnul == INVALID_HANDLE_VALUE)
{
free (arg_string);
/* FIXME: Should translate the error code. */
errno = EIO;
return -1;
}
/* Make sure that the process has a connected stdin. */
if (!duped_stdin)
si.hStdInput = hnul;
/* Make sure that the process has a connected stdout. */
if (!duped_stdout)
si.hStdOutput = hnul;
/* We normally don't want all the normal output. */
if (!duped_stderr)
si.hStdError = hnul;
}
cr_flags |= CREATE_SUSPENDED;
cr_flags |= DETACHED_PROCESS;
if (!CreateProcessA (argv[0],
arg_string,
&sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */
TRUE, /* inherit handles */
cr_flags, /* creation flags */
envblock, /* environment */
NULL, /* use current drive/directory */
&si, /* startup information */
&pi)) /* returns process information */
{
free (arg_string);
/* FIXME: Should translate the error code. */
errno = EIO;
return -1;
}
free (arg_string);
/* Close the /dev/nul handle if used. */
if (hnul != INVALID_HANDLE_VALUE)
CloseHandle (hnul);
for (i = 0; fd_list[i].handle != -1; i++)
CloseHandle ((HANDLE) fd_list[i].handle);
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return 0;
}
#define MAX_TRANS 10
int
translate_get_from_file (const char *trans_file,
struct spawn_fd_item_s *fd_list)
{
/* Hold roughly MAX_TRANS triplets of 64 bit numbers in hex
notation: "0xFEDCBA9876543210". 10*19*4 - 1 = 759. This plans
ahead for a time when a HANDLE is 64 bit. */
#define BUFFER_MAX 800
char line[BUFFER_MAX + 1];
char *linep;
int idx;
int res;
int fd;
fd = open (trans_file, O_RDONLY);
if (fd < 0)
return -1;
/* We always read one line from stdin. */
res = read (fd, line, BUFFER_MAX);
close (fd);
if (res < 0)
return -1;
line[BUFFER_MAX] = '\0';
linep = strchr (line, '\n');
if (linep > line && linep[-1] == '\r')
linep--;
*linep = '\0';
linep = line;
/* Now start to read mapping pairs. */
for (idx = 0; idx < MAX_TRANS; idx++)
{
unsigned long from;
long dup_to;
unsigned long to;
unsigned long loc;
char *tail;
/* FIXME: Maybe could use scanf. */
while (isspace (*((unsigned char *)linep)))
linep++;
if (*linep == '\0')
break;
from = strtoul (linep, &tail, 0);
if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
break;
linep = tail;
while (isspace (*linep))
linep++;
if (*linep == '\0')
break;
dup_to = strtol (linep, &tail, 0);
if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
break;
linep = tail;
while (isspace (*linep))
linep++;
if (*linep == '\0')
break;
to = strtoul (linep, &tail, 0);
if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
break;
linep = tail;
while (isspace (*linep))
linep++;
if (*linep == '\0')
break;
loc = strtoul (linep, &tail, 0);
if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
break;
linep = tail;
fd_list[idx].handle = from;
fd_list[idx].dup_to = dup_to;
fd_list[idx].peer_name = to;
fd_list[idx].arg_loc = loc;
}
fd_list[idx].handle = -1;
fd_list[idx].dup_to = -1;
fd_list[idx].peer_name = -1;
fd_list[idx].arg_loc = 0;
return 0;
}
/* Read the translated handles from TRANS_FILE and do a substitution
in ARGV. Returns the new argv and the list of substitutions in
FD_LIST (which must be MAX_TRANS+1 large). */
char **
translate_handles (const char *trans_file, const char * const *argv,
struct spawn_fd_item_s *fd_list)
{
int res;
int idx;
char **args;
res = translate_get_from_file (trans_file, fd_list);
if (res < 0)
return NULL;
for (idx = 0; argv[idx]; idx++)
;
args = malloc (sizeof (*args) * (idx + 1));
for (idx = 0; argv[idx]; idx++)
{
args[idx] = strdup (argv[idx]);
if (!args[idx])
return NULL;
}
args[idx] = NULL;
for (idx = 0; fd_list[idx].handle != -1; idx++)
{
char buf[25];
int aidx;
aidx = fd_list[idx].arg_loc;
if (aidx == 0)
continue;
args[aidx] = malloc (sizeof (buf));
/* We currently disable translation for stdin/stdout/stderr. We
assume that the spawned program handles 0/1/2 specially
already. FIXME: Check if this is true. */
if (!args[idx] || fd_list[idx].dup_to != -1)
return NULL;
/* NOTE: Here is the part where application specific knowledge
comes in. GPGME/GnuPG uses two forms of descriptor
specification, a plain number and a "-&" form. */
if (argv[aidx][0] == '-' && argv[aidx][1] == '&')
snprintf (args[aidx], sizeof (buf), "-&%d", fd_list[idx].peer_name);
else
snprintf (args[aidx], sizeof (buf), "%d", fd_list[idx].peer_name);
}
return args;
}
int
main (int argc, const char * const *argv)
{
int rc = 0;
char **argv_spawn;
struct spawn_fd_item_s fd_list[MAX_TRANS + 1];
if (argc < 3)
{
rc = 2;
goto leave;
}
argv_spawn = translate_handles (argv[1], &argv[2], fd_list);
if (!argv_spawn)
{
rc = 2;
goto leave;
}
/* Using execv does not replace the existing program image, but
spawns a new one and daemonizes it, confusing the command line
interpreter. So we have to use spawnv. */
rc = my_spawn (argv_spawn, fd_list);
if (rc < 0)
{
fprintf (stderr, "gpgwrap: executing `%s' failed: %s\n",
argv[0], strerror (errno));
rc = 2;
goto leave;
}
leave:
if (rc)
fprintf (stderr, "gpg-w32spawn: internal error\n");
/* Always try to delete the temporary file. */
if (argc >= 2)
{
if (DeleteFile (argv[1]) == 0)
fprintf (stderr, "Failed to delete %s: ec=%ld\n",
argv[1], GetLastError ());
}
return rc;
}

View File

@ -72,7 +72,7 @@ extern "C" {
AM_PATH_GPGME macro) check that this header matches the installed
library. Warning: Do not edit the next line. configure will do
that for you! */
#define GPGME_VERSION "1.1.7-svn1313"
#define GPGME_VERSION "1.1.7-svn1315"

View File

@ -34,6 +34,8 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/resource.h>
#include <unistd.h>
#include "util.h"
#include "priv-io.h"
@ -207,6 +209,72 @@ _gpgme_io_set_nonblocking (int fd)
}
static long int
get_max_fds (void)
{
char *source = NULL;
long int fds = -1;
int rc;
#ifdef RLIMIT_NOFILE
{
struct rlimit rl;
rc = getrlimit (RLIMIT_NOFILE, &rl);
if (rc == 0)
{
source = "RLIMIT_NOFILE";
fds = rl.rlim_max;
}
}
#endif
#ifdef RLIMIT_OFILE
if (fds == -1)
{
struct rlimit rl;
rc = getrlimit (RLIMIT_OFILE, &rl);
if (rc == 0)
{
source = "RLIMIT_OFILE";
fds = rl.rlim_max;
}
}
#endif
#ifdef _SC_OPEN_MAX
if (fds == -1)
{
long int scres;
scres = sysconf (_SC_OPEN_MAX);
if (scres >= 0)
{
source = "_SC_OPEN_MAX";
return scres;
}
}
#endif
#ifdef OPEN_MAX
if (fds == -1)
{
source = "OPEN_MAX";
fds = OPEN_MAX;
}
#endif
#if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
&& !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
#warning "No known way to get the maximum number of file descriptors."
#endif
if (fds == -1)
{
source = "arbitrary";
/* Arbitrary limit. */
fds = 1024;
}
TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%i (%s)", fds, source);
return fds;
}
static int
_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
{
@ -234,8 +302,7 @@ _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
/* Returns 0 on success, -1 on error. */
int
_gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid)
struct spawn_fd_item_s *fd_list, pid_t *r_pid)
{
pid_t pid;
int i;
@ -249,6 +316,11 @@ _gpgme_io_spawn (const char *path, char **argv,
TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
i++;
}
for (i = 0; fd_list[i].fd != -1; i++)
if (fd_list[i].dup_to == -1)
TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
else
TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
pid = fork ();
if (pid == -1)
@ -259,22 +331,44 @@ _gpgme_io_spawn (const char *path, char **argv,
/* Intermediate child to prevent zombie processes. */
if ((pid = fork ()) == 0)
{
int max_fds = get_max_fds ();
int fd;
/* Child. */
int duped_stdin = 0;
int duped_stderr = 0;
int seen_stdin = 0;
int seen_stderr = 0;
/* First close all fds which will not be duped. */
for (i=0; fd_child_list[i].fd != -1; i++)
if (fd_child_list[i].dup_to == -1)
close (fd_child_list[i].fd);
/* First close all fds which will not be inherited. */
for (fd = 0; fd < max_fds; fd++)
{
for (i = 0; fd_list[i].fd != -1; i++)
if (fd_list[i].fd == fd)
break;
if (fd_list[i].fd == -1)
close (fd);
}
/* And now dup and close the rest. */
for (i=0; fd_child_list[i].fd != -1; i++)
/* And now dup and close those to be duplicated. */
for (i = 0; fd_list[i].fd != -1; i++)
{
if (fd_child_list[i].dup_to != -1)
{
if (dup2 (fd_child_list[i].fd,
fd_child_list[i].dup_to) == -1)
int child_fd;
int res;
if (fd_list[i].dup_to != -1)
child_fd = fd_list[i].dup_to;
else
child_fd = fd_list[i].fd;
if (child_fd == 0)
seen_stdin = 1;
else if (child_fd == 2)
seen_stderr = 1;
if (fd_list[i].dup_to == -1)
continue;
res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
if (res < 0)
{
#if 0
/* FIXME: The debug file descriptor is not
@ -284,17 +378,13 @@ _gpgme_io_spawn (const char *path, char **argv,
#endif
_exit (8);
}
if (fd_child_list[i].dup_to == 0)
duped_stdin=1;
if (fd_child_list[i].dup_to == 2)
duped_stderr=1;
close (fd_child_list[i].fd);
}
close (fd_list[i].fd);
}
if (!duped_stdin || !duped_stderr)
if (! seen_stdin || ! seen_stderr)
{
int fd = open ("/dev/null", O_RDWR);
fd = open ("/dev/null", O_RDWR);
if (fd == -1)
{
#if 0
@ -306,7 +396,7 @@ _gpgme_io_spawn (const char *path, char **argv,
_exit (8);
}
/* Make sure that the process has a connected stdin. */
if (!duped_stdin)
if (! seen_stdin && fd != 0)
{
if (dup2 (fd, 0) == -1)
{
@ -319,7 +409,7 @@ _gpgme_io_spawn (const char *path, char **argv,
_exit (8);
}
}
if (!duped_stderr)
if (! seen_stderr && fd != 2)
if (dup2 (fd, 2) == -1)
{
#if 0
@ -330,10 +420,11 @@ _gpgme_io_spawn (const char *path, char **argv,
#endif
_exit (8);
}
if (fd != 0 && fd != 2)
close (fd);
}
execv ( path, argv );
execv (path, argv);
/* Hmm: in that case we could write a special status code to the
status-pipe. */
#if 0
@ -342,7 +433,8 @@ _gpgme_io_spawn (const char *path, char **argv,
TRACE_LOG1 ("exec of `%s' failed\n", path);
#endif
_exit (8);
} /* End child. */
/* End child. */
}
if (pid == -1)
_exit (1);
else
@ -354,9 +446,12 @@ _gpgme_io_spawn (const char *path, char **argv,
if (status)
return TRACE_SYSRES (-1);
/* .dup_to is not used in the parent list. */
for (i = 0; fd_parent_list[i].fd != -1; i++)
_gpgme_io_close (fd_parent_list[i].fd);
for (i = 0; fd_list[i].fd != -1; i++)
{
_gpgme_io_close (fd_list[i].fd);
/* No handle translation. */
fd_list[i].peer_name = fd_list[i].fd;
}
if (r_pid)
*r_pid = pid;
@ -549,5 +644,9 @@ _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
int
_gpgme_io_dup (int fd)
{
return dup (fd);
int new_fd = dup (fd);
TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
return new_fd;
}

View File

@ -24,11 +24,19 @@
/* A single file descriptor passed to spawn. For child fds, dup_to
specifies the fd it should become in the child. */
specifies the fd it should become in the child, but only 0, 1 and 2
are valid values (due to a limitation in the W32 code). As return
value, the PEER_NAME fields specify the name of the file
descriptor in the spawned process, or -1 if no change. If ARG_LOC
is not 0, it specifies the index in the argument vector of the
program which contains a numerical representation of the file
descriptor for translation purposes. */
struct spawn_fd_item_s
{
int fd;
int dup_to;
int peer_name;
int arg_loc;
};
struct io_select_fd_s
@ -51,12 +59,13 @@ int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
void *value);
int _gpgme_io_set_nonblocking (int fd);
/* Spawn the executable PATH with ARGV as arguments, after forking
close all fds in FD_PARENT_LIST in the parent and close or dup all
fds in FD_CHILD_LIST in the child. */
/* Spawn the executable PATH with ARGV as arguments. After forking
close all fds except for those in FD_LIST in the child, then
optionally dup() the child fds. Finally, all fds in the list are
closed in the parent. */
int _gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid);
struct spawn_fd_item_s *fd_list, pid_t *r_pid);
int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock);
/* Write the printable version of FD to the buffer BUF of length

View File

@ -52,6 +52,8 @@ struct arg_and_data_s
int inbound; /* True if this is used for reading from gpg. */
int dup_to;
int print_fd; /* Print the fd number and not the special form of it. */
int *arg_locp; /* Write back the argv idx of this argument when
building command line to this location. */
char arg[1]; /* Used if data above is not used. */
};
@ -62,7 +64,8 @@ struct fd_data_map_s
int inbound; /* true if this is used for reading from gpg */
int dup_to;
int fd; /* the fd to use */
int peer_fd; /* the outher side of the pipe */
int peer_fd; /* the other side of the pipe */
int arg_loc; /* The index into the argv for translation purposes. */
void *tag;
};
@ -82,6 +85,7 @@ struct engine_gpg
struct
{
int fd[2];
int arg_loc;
size_t bufsize;
char *buffer;
size_t readpos;
@ -95,6 +99,7 @@ struct engine_gpg
struct
{
int fd[2];
int arg_loc;
size_t bufsize;
char *buffer;
size_t readpos;
@ -191,7 +196,7 @@ close_notify_handler (int fd, void *opaque)
/* If FRONT is true, push at the front of the list. Use this for
options added late in the process. */
static gpgme_error_t
add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
_add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
{
struct arg_and_data_s *a;
@ -204,6 +209,8 @@ add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
a->data = NULL;
a->dup_to = -1;
a->arg_locp = arg_locp;
strcpy (a->arg, arg);
if (front)
{
@ -226,12 +233,27 @@ add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
return 0;
}
static gpgme_error_t
add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
{
return _add_arg (gpg, arg, front, NULL);
}
static gpgme_error_t
add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp)
{
return _add_arg (gpg, arg, 0, locp);
}
static gpgme_error_t
add_arg (engine_gpg_t gpg, const char *arg)
{
return add_arg_ext (gpg, arg, 0);
}
static gpgme_error_t
add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
{
@ -246,6 +268,8 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
a->next = NULL;
a->data = data;
a->inbound = inbound;
a->arg_locp = NULL;
if (dup_to == -2)
{
a->print_fd = 1;
@ -450,7 +474,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
{
char buf[25];
_gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]);
rc = add_arg (gpg, buf);
rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc);
if (rc)
goto leave;
}
@ -798,6 +822,9 @@ build_argv (engine_gpg_t gpg)
argc++;
for (a = gpg->arglist; a; a = a->next)
{
if (a->arg_locp)
*(a->arg_locp) = argc;
if (a->data)
{
/* Create a pipe to pass it down to gpg. */
@ -853,6 +880,7 @@ build_argv (engine_gpg_t gpg)
fd_data_map[datac].data = a->data;
fd_data_map[datac].dup_to = a->dup_to;
if (a->dup_to == -1)
{
char *ptr;
@ -876,6 +904,7 @@ build_argv (engine_gpg_t gpg)
}
_gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd);
fd_data_map[datac].arg_loc = argc;
argc++;
}
datac++;
@ -1225,7 +1254,7 @@ start (engine_gpg_t gpg)
int saved_errno;
int i, n;
int status;
struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
struct spawn_fd_item_s *fd_list;
pid_t pid;
if (!gpg)
@ -1256,63 +1285,41 @@ start (engine_gpg_t gpg)
if (rc)
return rc;
n = 3; /* status_fd, colon_fd and end of list */
/* status_fd, colon_fd and end of list. */
n = 3;
for (i = 0; gpg->fd_data_map[i].data; i++)
n++;
fd_child_list = calloc (n + n, sizeof *fd_child_list);
if (!fd_child_list)
fd_list = calloc (n, sizeof *fd_list);
if (! fd_list)
return gpg_error_from_errno (errno);
fd_parent_list = fd_child_list + n;
/* build the fd list for the child */
/* Build the fd list for the child. */
n = 0;
/* The status fd is never dup'ed, so do not include it in the list. */
fd_list[n].fd = gpg->status.fd[1];
fd_list[n].dup_to = -1;
fd_list[n].arg_loc = gpg->status.arg_loc;
n++;
if (gpg->colon.fnc)
{
fd_child_list[n].fd = gpg->colon.fd[1];
fd_child_list[n].dup_to = 1; /* dup to stdout */
fd_list[n].fd = gpg->colon.fd[1];
fd_list[n].dup_to = 1;
n++;
}
for (i = 0; gpg->fd_data_map[i].data; i++)
{
if (gpg->fd_data_map[i].dup_to != -1)
{
fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd;
fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to;
fd_list[n].fd = gpg->fd_data_map[i].peer_fd;
fd_list[n].dup_to = gpg->fd_data_map[i].dup_to;
fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc;
n++;
}
}
fd_child_list[n].fd = -1;
fd_child_list[n].dup_to = -1;
/* Build the fd list for the parent. */
n = 0;
if (gpg->status.fd[1] != -1)
{
fd_parent_list[n].fd = gpg->status.fd[1];
fd_parent_list[n].dup_to = -1;
n++;
}
if (gpg->colon.fd[1] != -1)
{
fd_parent_list[n].fd = gpg->colon.fd[1];
fd_parent_list[n].dup_to = -1;
n++;
}
for (i = 0; gpg->fd_data_map[i].data; i++)
{
fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd;
fd_parent_list[n].dup_to = -1;
n++;
}
fd_parent_list[n].fd = -1;
fd_parent_list[n].dup_to = -1;
fd_list[n].fd = -1;
fd_list[n].dup_to = -1;
status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name :
_gpgme_get_gpg_path (),
gpg->argv, fd_child_list, fd_parent_list, &pid);
_gpgme_get_gpg_path (), gpg->argv, fd_list, &pid);
saved_errno = errno;
free (fd_child_list);
free (fd_list);
if (status == -1)
return gpg_error_from_errno (saved_errno);
@ -1382,7 +1389,9 @@ gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
if (!err)
err = add_data (gpg, plain, 1, 1);
if (!err)
err = add_data (gpg, ciph, 0, 0);
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, ciph, -1, 0);
if (!err)
start (gpg);
@ -1616,7 +1625,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, plain, 0, 0);
err = add_data (gpg, plain, -1, 0);
if (!err)
err = start (gpg);
@ -1670,7 +1679,7 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, plain, 0, 0);
err = add_data (gpg, plain, -1, 0);
if (!err)
err = start (gpg);
@ -1759,7 +1768,9 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
if (!err && use_armor)
err = add_arg (gpg, "--armor");
if (!err)
err = add_data (gpg, help_data, 0, 0);
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, help_data, -1, 0);
if (!err)
err = start (gpg);
@ -1776,7 +1787,9 @@ gpg_import (void *engine, gpgme_data_t keydata)
err = add_arg (gpg, "--import");
if (!err)
err = add_data (gpg, keydata, 0, 0);
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, keydata, -1, 0);
if (!err)
err = start (gpg);
@ -2011,7 +2024,9 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
/* Tell the gpg object about the data. */
if (!err)
err = add_data (gpg, in, 0, 0);
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, in, -1, 0);
if (!err)
err = add_data (gpg, out, 1, 1);
@ -2061,7 +2076,7 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, sig, 0, 0);
err = add_data (gpg, sig, -1, 0);
if (!err)
err = add_data (gpg, plaintext, 1, 1);
}
@ -2072,13 +2087,8 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, sig, -1, 0);
if (signed_text)
{
if (!err)
err = add_arg (gpg, "-");
if (!err)
err = add_data (gpg, signed_text, 0, 0);
}
if (!err && signed_text)
err = add_data (gpg, signed_text, -1, 0);
}
if (!err)

View File

@ -105,4 +105,10 @@ gpgme_error_t _gpgme_map_gnupg_error (char *err);
set, return NULL in *VALUE. */
gpgme_error_t _gpgme_getenv (const char *name, char **value);
#ifdef HAVE_W32_SYSTEM
int _gpgme_mkstemp (int *fd, char **name);
const char *_gpgme_get_w32spawn_path (void);
#endif
#endif /* UTIL_H */

View File

@ -247,8 +247,8 @@ _gpgme_get_program_version (const char *const file_name)
int rp[2];
int nread;
char *argv[] = {NULL /* file_name */, "--version", 0};
struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
{-1, -1} };
int status;
if (!file_name)
@ -258,10 +258,9 @@ _gpgme_get_program_version (const char *const file_name)
if (_gpgme_io_pipe (rp, 1) < 0)
return NULL;
pfd[0].fd = rp[1];
cfd[0].fd = rp[1];
status = _gpgme_io_spawn (file_name, argv, cfd, pfd, NULL);
status = _gpgme_io_spawn (file_name, argv, cfd, NULL);
if (status < 0)
{
_gpgme_io_close (rp[0]);

View File

@ -449,8 +449,7 @@ build_commandline (char **argv)
int
_gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid)
struct spawn_fd_item_s *fd_list, pid_t *r_pid)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
@ -461,16 +460,16 @@ _gpgme_io_spawn (const char *path, char **argv,
0 /* returns tid */
};
STARTUPINFO si;
char *envblock = NULL;
int cr_flags = CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ());
int i;
char **args;
char *arg_string;
int duped_stdin = 0;
int duped_stderr = 0;
HANDLE hnul = INVALID_HANDLE_VALUE;
/* FIXME. */
int debug_me = 0;
int tmp_fd;
char *tmp_name;
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
"path=%s", path);
i = 0;
@ -480,119 +479,145 @@ _gpgme_io_spawn (const char *path, char **argv,
i++;
}
/* We do not inherit any handles by default, and just insert those
handles we want the child to have afterwards. But some handle
values occur on the command line, and we need to move
stdin/out/err to the right location. So we use a wrapper program
which gets the information from a temporary file. */
if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
{
TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
return TRACE_SYSRES (-1);
}
TRACE_LOG1 ("tmp_name = %s", tmp_name);
args = calloc (2 + i + 1, sizeof (*args));
args[0] = (char *) _gpgme_get_w32spawn_path ();
args[1] = tmp_name;
args[2] = path;
memcpy (&args[3], &argv[1], i * sizeof (*args));
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
arg_string = build_commandline (argv);
arg_string = build_commandline (args);
free (args);
if (!arg_string)
{
close (tmp_fd);
DeleteFile (tmp_name);
return TRACE_SYSRES (-1);
}
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
for (i = 0; fd_child_list[i].fd != -1; i++)
{
if (fd_child_list[i].dup_to == 0)
{
si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd,
_get_osfhandle (fd_child_list[i].fd));
duped_stdin = 1;
}
else if (fd_child_list[i].dup_to == 1)
{
si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd,
_get_osfhandle (fd_child_list[i].fd));
}
else if (fd_child_list[i].dup_to == 2)
{
si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd,
_get_osfhandle (fd_child_list[i].fd));
duped_stderr = 1;
}
}
if (!duped_stdin || !duped_stderr)
{
SECURITY_ATTRIBUTES sa;
memset (&sa, 0, sizeof sa);
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
hnul = CreateFile ("nul",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hnul == INVALID_HANDLE_VALUE)
{
TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
(int) GetLastError ());
free (arg_string);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Make sure that the process has a connected stdin. */
if (!duped_stdin)
{
si.hStdInput = hnul;
TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
}
/* We normally don't want all the normal output. */
if (!duped_stderr)
{
si.hStdError = hnul;
TRACE_LOG1 ("using %d for dummy stderr", (int)hnul);
}
}
si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
si.hStdInput = INVALID_HANDLE_VALUE;
si.hStdOutput = INVALID_HANDLE_VALUE;
si.hStdError = INVALID_HANDLE_VALUE;
cr_flags |= CREATE_SUSPENDED;
cr_flags |= DETACHED_PROCESS;
if (!CreateProcessA (path,
if (!CreateProcessA (_gpgme_get_w32spawn_path (),
arg_string,
&sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */
TRUE, /* inherit handles */
FALSE, /* inherit handles */
cr_flags, /* creation flags */
envblock, /* environment */
NULL, /* environment */
NULL, /* use current drive/directory */
&si, /* startup information */
&pi)) /* returns process information */
{
TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
free (arg_string);
close (tmp_fd);
DeleteFile (tmp_name);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Close the /dev/nul handle if used. */
if (hnul != INVALID_HANDLE_VALUE)
free (arg_string);
/* Insert the inherited handles. */
for (i = 0; fd_list[i].fd != -1; i++)
{
if (!CloseHandle (hnul))
TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
(int) GetLastError ());
HANDLE hd;
/* Make it inheritable for the wrapper process. */
if (!DuplicateHandle (GetCurrentProcess(), _get_osfhandle (fd_list[i].fd),
pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
TerminateProcess (pi.hProcess, 0);
/* Just in case TerminateProcess didn't work, let the
process fail on its own. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
close (tmp_fd);
DeleteFile (tmp_name);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Return the child name of this handle. */
fd_list[i].peer_name = (int) hd;
}
/* Close the other ends of the pipes. */
for (i = 0; fd_parent_list[i].fd != -1; i++)
_gpgme_io_close (fd_parent_list[i].fd);
/* Write the handle translation information to the temporary
file. */
{
/* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
notation: "0xFEDCBA9876543210" with an extra white space after
every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
for a time when a HANDLE is 64 bit. */
#define BUFFER_MAX 800
char line[BUFFER_MAX + 1];
int res;
int written;
size_t len;
line[0] = '\n';
line[1] = '\0';
for (i = 0; fd_list[i].fd != -1; i++)
{
/* Strip the newline. */
len = strlen (line) - 1;
/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
fd_list[i].fd, fd_list[i].dup_to,
fd_list[i].peer_name, fd_list[i].arg_loc);
/* Rather safe than sorry. */
line[BUFFER_MAX - 1] = '\n';
line[BUFFER_MAX] = '\0';
}
len = strlen (line);
written = 0;
do
{
res = write (tmp_fd, &line[written], len - written);
if (res > 0)
written += res;
}
while (res > 0 || (res < 0 && errno == EAGAIN));
}
close (tmp_fd);
/* The temporary file is deleted by the gpgme-w32spawn process
(hopefully). */
TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
"dwProcessID=%d, dwThreadId=%d",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
if (r_pid)
*r_pid = (pid_t)pi.dwProcessId;
@ -603,10 +628,24 @@ _gpgme_io_spawn (const char *path, char **argv,
TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
(int) GetLastError ());
TRACE_SUC1 ("process=%p", pi.hProcess);
TRACE_LOG1 ("process=%p", pi.hProcess);
/* We don't need to wait for the process. */
CloseHandle (pi.hProcess);
if (!CloseHandle (pi.hProcess))
TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
(int) GetLastError ());
for (i = 0; fd_list[i].fd != -1; i++)
_gpgme_io_close (fd_list[i].fd);
for (i = 0; fd_list[i].fd != -1; i++)
if (fd_list[i].dup_to == -1)
TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
fd_list[i].peer_name);
else
TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
((fd_list[i].dup_to == 1) ? "out" : "err"));
return TRACE_SYSRES (0);
}

View File

@ -39,6 +39,7 @@
#include "priv-io.h"
#include "debug.h"
/* We assume that a HANDLE can be represented by an int which should
be true for all i386 systems (HANDLE is defined as void *) and
these are the only systems for which Windows is available. Further
@ -242,12 +243,14 @@ reader (void *arg)
}
ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
if (!SetEvent (ctx->have_data_ev))
TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
(int) GetLastError ());
UNLOCK (ctx->mutex);
}
/* Indicate that we have an error or EOF. */
if (!SetEvent (ctx->have_data_ev))
TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
(int) GetLastError ());
SetEvent (ctx->stopped);
return TRACE_SUC ();
@ -479,7 +482,8 @@ _gpgme_io_read (int fd, void *buffer, size_t count)
}
if (!SetEvent (ctx->have_space_ev))
{
TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
ctx->have_space_ev, (int) GetLastError ());
UNLOCK (ctx->mutex);
/* FIXME: Should translate the error code. */
errno = EIO;
@ -1006,8 +1010,7 @@ build_commandline (char **argv)
int
_gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid)
struct spawn_fd_item_s *fd_list, pid_t *r_pid)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
@ -1018,16 +1021,16 @@ _gpgme_io_spawn (const char *path, char **argv,
0 /* returns tid */
};
STARTUPINFO si;
char *envblock = NULL;
int cr_flags = CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ());
int i;
char **args;
char *arg_string;
int duped_stdin = 0;
int duped_stderr = 0;
HANDLE hnul = INVALID_HANDLE_VALUE;
/* FIXME. */
int debug_me = 0;
int tmp_fd;
char *tmp_name;
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
"path=%s", path);
i = 0;
@ -1037,116 +1040,145 @@ _gpgme_io_spawn (const char *path, char **argv,
i++;
}
/* We do not inherit any handles by default, and just insert those
handles we want the child to have afterwards. But some handle
values occur on the command line, and we need to move
stdin/out/err to the right location. So we use a wrapper program
which gets the information from a temporary file. */
if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
{
TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
return TRACE_SYSRES (-1);
}
TRACE_LOG1 ("tmp_name = %s", tmp_name);
args = calloc (2 + i + 1, sizeof (*args));
args[0] = (char *) _gpgme_get_w32spawn_path ();
args[1] = tmp_name;
args[2] = path;
memcpy (&args[3], &argv[1], i * sizeof (*args));
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
arg_string = build_commandline (argv);
arg_string = build_commandline (args);
free (args);
if (!arg_string)
{
close (tmp_fd);
DeleteFile (tmp_name);
return TRACE_SYSRES (-1);
}
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
for (i = 0; fd_child_list[i].fd != -1; i++)
{
if (fd_child_list[i].dup_to == 0)
{
si.hStdInput = fd_to_handle (fd_child_list[i].fd);
TRACE_LOG1 ("using 0x%x for stdin", fd_child_list[i].fd);
duped_stdin = 1;
}
else if (fd_child_list[i].dup_to == 1)
{
si.hStdOutput = fd_to_handle (fd_child_list[i].fd);
TRACE_LOG1 ("using 0x%x for stdout", fd_child_list[i].fd);
}
else if (fd_child_list[i].dup_to == 2)
{
si.hStdError = fd_to_handle (fd_child_list[i].fd);
TRACE_LOG1 ("using 0x%x for stderr", fd_child_list[i].fd);
duped_stderr = 1;
}
}
if (!duped_stdin || !duped_stderr)
{
SECURITY_ATTRIBUTES sa;
memset (&sa, 0, sizeof sa);
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
hnul = CreateFile ("nul",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hnul == INVALID_HANDLE_VALUE)
{
TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
(int) GetLastError ());
free (arg_string);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Make sure that the process has a connected stdin. */
if (!duped_stdin)
{
si.hStdInput = hnul;
TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
}
/* We normally don't want all the normal output. */
if (!duped_stderr)
{
si.hStdError = hnul;
TRACE_LOG1 ("using 0x%x for dummy stderr", (int) hnul);
}
}
si.hStdInput = INVALID_HANDLE_VALUE;
si.hStdOutput = INVALID_HANDLE_VALUE;
si.hStdError = INVALID_HANDLE_VALUE;
cr_flags |= CREATE_SUSPENDED;
cr_flags |= DETACHED_PROCESS;
if (!CreateProcessA (path,
if (!CreateProcessA (_gpgme_get_w32spawn_path (),
arg_string,
&sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */
TRUE, /* inherit handles */
FALSE, /* inherit handles */
cr_flags, /* creation flags */
envblock, /* environment */
NULL, /* environment */
NULL, /* use current drive/directory */
&si, /* startup information */
&pi)) /* returns process information */
{
TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
free (arg_string);
close (tmp_fd);
DeleteFile (tmp_name);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Close the /dev/nul handle if used. */
if (hnul != INVALID_HANDLE_VALUE)
free (arg_string);
/* Insert the inherited handles. */
for (i = 0; fd_list[i].fd != -1; i++)
{
if (!CloseHandle (hnul))
TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
(int) GetLastError ());
HANDLE hd;
/* Make it inheritable for the wrapper process. */
if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
TerminateProcess (pi.hProcess, 0);
/* Just in case TerminateProcess didn't work, let the
process fail on its own. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
close (tmp_fd);
DeleteFile (tmp_name);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Return the child name of this handle. */
fd_list[i].peer_name = handle_to_fd (hd);
}
/* Close the other ends of the pipes. */
for (i = 0; fd_parent_list[i].fd != -1; i++)
_gpgme_io_close (fd_parent_list[i].fd);
/* Write the handle translation information to the temporary
file. */
{
/* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
notation: "0xFEDCBA9876543210" with an extra white space after
every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
for a time when a HANDLE is 64 bit. */
#define BUFFER_MAX 800
char line[BUFFER_MAX + 1];
int res;
int written;
size_t len;
line[0] = '\n';
line[1] = '\0';
for (i = 0; fd_list[i].fd != -1; i++)
{
/* Strip the newline. */
len = strlen (line) - 1;
/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
fd_list[i].fd, fd_list[i].dup_to,
fd_list[i].peer_name, fd_list[i].arg_loc);
/* Rather safe than sorry. */
line[BUFFER_MAX - 1] = '\n';
line[BUFFER_MAX] = '\0';
}
len = strlen (line);
written = 0;
do
{
res = write (tmp_fd, &line[written], len - written);
if (res > 0)
written += res;
}
while (res > 0 || (res < 0 && errno == EAGAIN));
}
close (tmp_fd);
/* The temporary file is deleted by the gpgme-w32spawn process
(hopefully). */
TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
"dwProcessID=%d, dwThreadId=%d",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
if (r_pid)
*r_pid = (pid_t)pi.dwProcessId;
@ -1157,10 +1189,24 @@ _gpgme_io_spawn (const char *path, char **argv,
TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
(int) GetLastError ());
TRACE_SUC1 ("process=%p", pi.hProcess);
TRACE_LOG1 ("process=%p", pi.hProcess);
/* We don't need to wait for the process. */
CloseHandle (pi.hProcess);
if (!CloseHandle (pi.hProcess))
TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
(int) GetLastError ());
for (i = 0; fd_list[i].fd != -1; i++)
_gpgme_io_close (fd_list[i].fd);
for (i = 0; fd_list[i].fd != -1; i++)
if (fd_list[i].dup_to == -1)
TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
fd_list[i].peer_name);
else
TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
((fd_list[i].dup_to == 1) ? "out" : "err"));
return TRACE_SYSRES (0);
}

View File

@ -398,8 +398,7 @@ build_commandline (char **argv)
int
_gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid)
struct spawn_fd_item_s *fd_list, pid_t *r_pid)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
@ -410,16 +409,16 @@ _gpgme_io_spawn (const char *path, char **argv,
0 /* returns tid */
};
STARTUPINFO si;
char *envblock = NULL;
int cr_flags = CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ());
int i;
char **args;
char *arg_string;
int duped_stdin = 0;
int duped_stderr = 0;
HANDLE hnul = INVALID_HANDLE_VALUE;
/* FIXME. */
int debug_me = 0;
int tmp_fd;
char *tmp_name;
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
"path=%s", path);
i = 0;
@ -429,120 +428,145 @@ _gpgme_io_spawn (const char *path, char **argv,
i++;
}
/* We do not inherit any handles by default, and just insert those
handles we want the child to have afterwards. But some handle
values occur on the command line, and we need to move
stdin/out/err to the right location. So we use a wrapper program
which gets the information from a temporary file. */
if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
{
TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
return TRACE_SYSRES (-1);
}
TRACE_LOG1 ("tmp_name = %s", tmp_name);
args = (char **) calloc (2 + i + 1, sizeof (*args));
args[0] = (char *) _gpgme_get_w32spawn_path ();
args[1] = tmp_name;
args[2] = const_cast<char *>(path);
memcpy (&args[3], &argv[1], i * sizeof (*args));
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
arg_string = build_commandline (argv);
arg_string = build_commandline (args);
free (args);
if (!arg_string)
{
close (tmp_fd);
DeleteFile (tmp_name);
return TRACE_SYSRES (-1);
}
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
for (i = 0; fd_child_list[i].fd != -1; i++)
{
if (fd_child_list[i].dup_to == 0)
{
si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd,
_get_osfhandle (fd_child_list[i].fd));
duped_stdin = 1;
}
else if (fd_child_list[i].dup_to == 1)
{
si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd,
_get_osfhandle (fd_child_list[i].fd));
}
else if (fd_child_list[i].dup_to == 2)
{
si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd,
_get_osfhandle (fd_child_list[i].fd));
duped_stderr = 1;
}
}
if (!duped_stdin || !duped_stderr)
{
SECURITY_ATTRIBUTES sa;
memset (&sa, 0, sizeof sa);
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
hnul = CreateFile ("nul",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hnul == INVALID_HANDLE_VALUE)
{
TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
(int) GetLastError ());
free (arg_string);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Make sure that the process has a connected stdin. */
if (!duped_stdin)
{
si.hStdInput = hnul;
TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
}
/* We normally don't want all the normal output. */
if (!duped_stderr)
{
si.hStdError = hnul;
TRACE_LOG1 ("using %d for dummy stderr", (int)hnul);
}
}
si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
si.hStdInput = INVALID_HANDLE_VALUE;
si.hStdOutput = INVALID_HANDLE_VALUE;
si.hStdError = INVALID_HANDLE_VALUE;
cr_flags |= CREATE_SUSPENDED;
cr_flags |= DETACHED_PROCESS;
if (!CreateProcessA (path,
if (!CreateProcessA (_gpgme_get_w32spawn_path (),
arg_string,
&sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */
TRUE, /* inherit handles */
FALSE, /* inherit handles */
cr_flags, /* creation flags */
envblock, /* environment */
NULL, /* environment */
NULL, /* use current drive/directory */
&si, /* startup information */
&pi)) /* returns process information */
{
TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
free (arg_string);
close (tmp_fd);
DeleteFile (tmp_name);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Close the /dev/nul handle if used. */
if (hnul != INVALID_HANDLE_VALUE)
free (arg_string);
/* Insert the inherited handles. */
for (i = 0; fd_list[i].fd != -1; i++)
{
if (!CloseHandle (hnul))
TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
(int) GetLastError ());
HANDLE hd;
if (!DuplicateHandle (GetCurrentProcess(),
(HANDLE) _get_osfhandle (fd_list[i].fd),
pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
TerminateProcess (pi.hProcess, 0);
/* Just in case TerminateProcess didn't work, let the
process fail on its own. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
close (tmp_fd);
DeleteFile (tmp_name);
/* FIXME: Should translate the error code. */
errno = EIO;
return TRACE_SYSRES (-1);
}
/* Return the child name of this handle. */
fd_list[i].peer_name = (int) hd;
}
/* Close the other ends of the pipes. */
for (i = 0; fd_parent_list[i].fd != -1; i++)
_gpgme_io_close (fd_parent_list[i].fd);
/* Write the handle translation information to the temporary
file. */
{
/* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
notation: "0xFEDCBA9876543210" with an extra white space after
every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
for a time when a HANDLE is 64 bit. */
#define BUFFER_MAX 800
char line[BUFFER_MAX + 1];
int res;
int written;
size_t len;
line[0] = '\n';
line[1] = '\0';
for (i = 0; fd_list[i].fd != -1; i++)
{
/* Strip the newline. */
len = strlen (line) - 1;
/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
fd_list[i].fd, fd_list[i].dup_to,
fd_list[i].peer_name, fd_list[i].arg_loc);
/* Rather safe than sorry. */
line[BUFFER_MAX - 1] = '\n';
line[BUFFER_MAX] = '\0';
}
len = strlen (line);
written = 0;
do
{
res = write (tmp_fd, &line[written], len - written);
if (res > 0)
written += res;
}
while (res > 0 || (res < 0 && errno == EAGAIN));
}
close (tmp_fd);
/* The temporary file is deleted by the gpgme-w32spawn process
(hopefully). */
TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
"dwProcessID=%d, dwThreadId=%d",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
if (r_pid)
*r_pid = (pid_t)pi.dwProcessId;
@ -553,10 +577,24 @@ _gpgme_io_spawn (const char *path, char **argv,
TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
(int) GetLastError ());
TRACE_SUC1 ("process=%p", pi.hProcess);
TRACE_LOG1 ("process=%p", pi.hProcess);
/* We don't need to wait for the process. */
CloseHandle (pi.hProcess);
if (!CloseHandle (pi.hProcess))
TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
(int) GetLastError ());
for (i = 0; fd_list[i].fd != -1; i++)
_gpgme_io_close (fd_list[i].fd);
for (i = 0; fd_list[i].fd != -1; i++)
if (fd_list[i].dup_to == -1)
TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
fd_list[i].peer_name);
else
TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
((fd_list[i].dup_to == 1) ? "out" : "err"));
return TRACE_SYSRES (0);
}

View File

@ -28,8 +28,11 @@
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <windows.h>
@ -350,6 +353,22 @@ _gpgme_get_gpgconf_path (void)
}
const char *
_gpgme_get_w32spawn_path (void)
{
static char *w32spawn_program;
LOCK (get_path_lock);
if (!w32spawn_program)
w32spawn_program = find_program_in_inst_dir ("gpgme-w32spawn.exe");
if (!w32spawn_program)
w32spawn_program
= find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe");
UNLOCK (get_path_lock);
return w32spawn_program;
}
/* Return an integer value from gpgme specific configuration
entries. VALUE receives that value; function returns true if a value
has been configured and false if not. */
@ -395,3 +414,134 @@ _gpgme_allow_set_foregound_window (pid_t pid)
}
/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright
(C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. */
static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
/* Generate a temporary file name based on TMPL. TMPL must match the
rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
does not exist at the time of the call to mkstemp. TMPL is
overwritten with the result. */
static int
mkstemp (char *tmpl)
{
int len;
char *XXXXXX;
static uint64_t value;
uint64_t random_time_bits;
unsigned int count;
int fd = -1;
int save_errno = errno;
/* A lower bound on the number of temporary files to attempt to
generate. The maximum total number of temporary file names that
can exist for a given template is 62**6. It should never be
necessary to try all these combinations. Instead if a reasonable
number of names is tried (we define reasonable as 62**3) fail to
give the system administrator the chance to remove the problems. */
#define ATTEMPTS_MIN (62 * 62 * 62)
/* The number of times to attempt to generate a temporary file. To
conform to POSIX, this must be no smaller than TMP_MAX. */
#if ATTEMPTS_MIN < TMP_MAX
unsigned int attempts = TMP_MAX;
#else
unsigned int attempts = ATTEMPTS_MIN;
#endif
len = strlen (tmpl);
if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
{
errno = EINVAL;
return -1;
}
/* This is where the Xs start. */
XXXXXX = &tmpl[len - 6];
/* Get some more or less random data. */
{
struct timeval tv;
gettimeofday (&tv, NULL);
random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
}
value += random_time_bits ^ getpid ();
for (count = 0; count < attempts; value += 7777, ++count)
{
uint64_t v = value;
/* Fill in the random bits. */
XXXXXX[0] = letters[v % 62];
v /= 62;
XXXXXX[1] = letters[v % 62];
v /= 62;
XXXXXX[2] = letters[v % 62];
v /= 62;
XXXXXX[3] = letters[v % 62];
v /= 62;
XXXXXX[4] = letters[v % 62];
v /= 62;
XXXXXX[5] = letters[v % 62];
fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd >= 0)
{
errno = save_errno;
return fd;
}
else if (errno != EEXIST)
return -1;
}
/* We got out of the loop because we ran out of combinations to try. */
errno = EEXIST;
return -1;
}
int
_gpgme_mkstemp (int *fd, char **name)
{
char tmp[MAX_PATH + 2];
char *tmpname;
int err;
*fd = -1;
*name = NULL;
err = GetTempPath (MAX_PATH + 1, tmp);
if (err == 0 || err > MAX_PATH + 1)
strcpy (tmp,"c:\\windows\\temp");
else
{
int len = strlen(tmp);
/* GetTempPath may return with \ on the end */
while(len > 0 && tmp[len - 1] == '\\')
{
tmp[len-1] = '\0';
len--;
}
}
tmpname = malloc (strlen (tmp) + 13 + 1);
if (!tmpname)
return -1;
sprintf (tmpname, "%s\\gpgme-XXXXXX", tmp);
*fd = mkstemp (tmpname);
if (fd < 0)
return -1;
*name = tmpname;
return 0;
}