90a7b74477
* assuan-pipe-connect.c (pipe_connect_gpgme): New function, use it if _ASSUAN_IN_GPGME_BUILD_ASSUAN.
1001 lines
28 KiB
C
1001 lines
28 KiB
C
/* assuan-pipe-connect.c - Establish a pipe connection (client)
|
||
* Copyright (C) 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
|
||
*
|
||
* This file is part of Assuan.
|
||
*
|
||
* Assuan 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.
|
||
*
|
||
* Assuan 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, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||
* USA.
|
||
*/
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
#include <config.h>
|
||
#endif
|
||
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <signal.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <sys/types.h>
|
||
#ifndef HAVE_W32_SYSTEM
|
||
#include <sys/wait.h>
|
||
#else
|
||
#include <windows.h>
|
||
#endif
|
||
|
||
#include "assuan-defs.h"
|
||
|
||
/* Hacks for Slowaris. */
|
||
#ifndef PF_LOCAL
|
||
# ifdef PF_UNIX
|
||
# define PF_LOCAL PF_UNIX
|
||
# else
|
||
# define PF_LOCAL AF_UNIX
|
||
# endif
|
||
#endif
|
||
#ifndef AF_LOCAL
|
||
# define AF_LOCAL AF_UNIX
|
||
#endif
|
||
|
||
|
||
#ifdef _POSIX_OPEN_MAX
|
||
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
|
||
#else
|
||
#define MAX_OPEN_FDS 20
|
||
#endif
|
||
|
||
#ifdef HAVE_W32_SYSTEM
|
||
/* 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
|
||
we assume that -1 denotes an invalid handle. */
|
||
#define fd_to_handle(a) ((HANDLE)(a))
|
||
#define handle_to_fd(a) ((int)(a))
|
||
#define pid_to_handle(a) ((HANDLE)(a))
|
||
#define handle_to_pid(a) ((int)(a))
|
||
#endif /*HAVE_W32_SYSTEM*/
|
||
|
||
|
||
/* This should be called to make sure that SIGPIPE gets ignored. */
|
||
static void
|
||
fix_signals (void)
|
||
{
|
||
#ifndef _ASSUAN_NO_FIXED_SIGNALS
|
||
#ifndef HAVE_DOSISH_SYSTEM /* No SIGPIPE for these systems. */
|
||
static int fixed_signals;
|
||
|
||
if (!fixed_signals)
|
||
{
|
||
struct sigaction act;
|
||
|
||
sigaction (SIGPIPE, NULL, &act);
|
||
if (act.sa_handler == SIG_DFL)
|
||
{
|
||
act.sa_handler = SIG_IGN;
|
||
sigemptyset (&act.sa_mask);
|
||
act.sa_flags = 0;
|
||
sigaction (SIGPIPE, &act, NULL);
|
||
}
|
||
fixed_signals = 1;
|
||
/* FIXME: This is not MT safe */
|
||
}
|
||
#endif /*HAVE_DOSISH_SYSTEM*/
|
||
#endif /*!_ASSUAN_NO_FIXED_SIGNALS*/
|
||
}
|
||
|
||
|
||
#ifndef HAVE_W32_SYSTEM
|
||
static int
|
||
writen (int fd, const char *buffer, size_t length)
|
||
{
|
||
while (length)
|
||
{
|
||
int nwritten = write (fd, buffer, length);
|
||
|
||
if (nwritten < 0)
|
||
{
|
||
if (errno == EINTR)
|
||
continue;
|
||
return -1; /* write error */
|
||
}
|
||
length -= nwritten;
|
||
buffer += nwritten;
|
||
}
|
||
return 0; /* okay */
|
||
}
|
||
#endif
|
||
|
||
static int
|
||
do_finish (assuan_context_t ctx)
|
||
{
|
||
if (ctx->inbound.fd != -1)
|
||
{
|
||
_assuan_close (ctx->inbound.fd);
|
||
if (ctx->inbound.fd == ctx->outbound.fd)
|
||
ctx->outbound.fd = -1;
|
||
ctx->inbound.fd = -1;
|
||
}
|
||
if (ctx->outbound.fd != -1)
|
||
{
|
||
_assuan_close (ctx->outbound.fd);
|
||
ctx->outbound.fd = -1;
|
||
}
|
||
if (ctx->pid != -1 && ctx->pid)
|
||
{
|
||
#ifndef HAVE_W32_SYSTEM
|
||
#ifndef _ASSUAN_USE_DOUBLE_FORK
|
||
if (!ctx->flags.no_waitpid)
|
||
_assuan_waitpid (ctx->pid, NULL, 0);
|
||
ctx->pid = -1;
|
||
#endif
|
||
#endif /*!HAVE_W32_SYSTEM*/
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
do_deinit (assuan_context_t ctx)
|
||
{
|
||
do_finish (ctx);
|
||
}
|
||
|
||
|
||
/* Helper for pipe_connect. */
|
||
static assuan_error_t
|
||
initial_handshake (assuan_context_t *ctx)
|
||
{
|
||
int okay, off;
|
||
assuan_error_t err;
|
||
|
||
err = _assuan_read_from_server (*ctx, &okay, &off);
|
||
if (err)
|
||
_assuan_log_printf ("can't connect server: %s\n",
|
||
assuan_strerror (err));
|
||
else if (okay != 1)
|
||
{
|
||
_assuan_log_printf ("can't connect server: `%s'\n",
|
||
(*ctx)->inbound.line);
|
||
err = _assuan_error (ASSUAN_Connect_Failed);
|
||
}
|
||
|
||
if (err)
|
||
{
|
||
assuan_disconnect (*ctx);
|
||
*ctx = NULL;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
#ifndef HAVE_W32_SYSTEM
|
||
#define pipe_connect pipe_connect_unix
|
||
/* Unix version of the pipe connection code. We use an extra macro to
|
||
make ChangeLog entries easier. */
|
||
static assuan_error_t
|
||
pipe_connect_unix (assuan_context_t *ctx,
|
||
const char *name, const char *const argv[],
|
||
int *fd_child_list,
|
||
void (*atfork) (void *opaque, int reserved),
|
||
void *atforkvalue)
|
||
{
|
||
assuan_error_t err;
|
||
int rp[2];
|
||
int wp[2];
|
||
char mypidstr[50];
|
||
|
||
if (!ctx || !name || !argv || !argv[0])
|
||
return _assuan_error (ASSUAN_Invalid_Value);
|
||
|
||
fix_signals ();
|
||
|
||
sprintf (mypidstr, "%lu", (unsigned long)getpid ());
|
||
|
||
if (pipe (rp) < 0)
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
|
||
if (pipe (wp) < 0)
|
||
{
|
||
close (rp[0]);
|
||
close (rp[1]);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
err = _assuan_new_context (ctx);
|
||
if (err)
|
||
{
|
||
close (rp[0]);
|
||
close (rp[1]);
|
||
close (wp[0]);
|
||
close (wp[1]);
|
||
return err;
|
||
}
|
||
(*ctx)->pipe_mode = 1;
|
||
(*ctx)->inbound.fd = rp[0]; /* Our inbound is read end of read pipe. */
|
||
(*ctx)->outbound.fd = wp[1]; /* Our outbound is write end of write pipe. */
|
||
(*ctx)->deinit_handler = do_deinit;
|
||
(*ctx)->finish_handler = do_finish;
|
||
|
||
/* FIXME: For GPGME we should better use _gpgme_io_spawn. The PID
|
||
stored here is actually soon useless. */
|
||
(*ctx)->pid = fork ();
|
||
if ((*ctx)->pid < 0)
|
||
{
|
||
close (rp[0]);
|
||
close (rp[1]);
|
||
close (wp[0]);
|
||
close (wp[1]);
|
||
_assuan_release_context (*ctx);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
if ((*ctx)->pid == 0)
|
||
{
|
||
#ifdef _ASSUAN_USE_DOUBLE_FORK
|
||
pid_t pid;
|
||
|
||
if ((pid = fork ()) == 0)
|
||
#endif
|
||
{
|
||
int i, n;
|
||
char errbuf[512];
|
||
int *fdp;
|
||
|
||
if (atfork)
|
||
atfork (atforkvalue, 0);
|
||
|
||
/* Dup handles to stdin/stdout. */
|
||
if (rp[1] != STDOUT_FILENO)
|
||
{
|
||
if (dup2 (rp[1], STDOUT_FILENO) == -1)
|
||
{
|
||
_assuan_log_printf ("dup2 failed in child: %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
}
|
||
if (wp[0] != STDIN_FILENO)
|
||
{
|
||
if (dup2 (wp[0], STDIN_FILENO) == -1)
|
||
{
|
||
_assuan_log_printf ("dup2 failed in child: %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
}
|
||
|
||
/* Dup stderr to /dev/null unless it is in the list of FDs to be
|
||
passed to the child. */
|
||
fdp = fd_child_list;
|
||
if (fdp)
|
||
{
|
||
for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
|
||
;
|
||
}
|
||
if (!fdp || *fdp == -1)
|
||
{
|
||
int fd = open ("/dev/null", O_WRONLY);
|
||
if (fd == -1)
|
||
{
|
||
_assuan_log_printf ("can't open `/dev/null': %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
if (dup2 (fd, STDERR_FILENO) == -1)
|
||
{
|
||
_assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
}
|
||
|
||
|
||
/* Close all files which will not be duped and are not in the
|
||
fd_child_list. */
|
||
n = sysconf (_SC_OPEN_MAX);
|
||
if (n < 0)
|
||
n = MAX_OPEN_FDS;
|
||
for (i=0; i < n; i++)
|
||
{
|
||
if ( i == STDIN_FILENO || i == STDOUT_FILENO
|
||
|| i == STDERR_FILENO)
|
||
continue;
|
||
fdp = fd_child_list;
|
||
if (fdp)
|
||
{
|
||
while (*fdp != -1 && *fdp != i)
|
||
fdp++;
|
||
}
|
||
|
||
if (!(fdp && *fdp != -1))
|
||
close(i);
|
||
}
|
||
errno = 0;
|
||
|
||
/* We store our parents pid in the environment so that the
|
||
execed assuan server is able to read the actual pid of the
|
||
client. The server can't use getppid because it might have
|
||
been double forked before the assuan server has been
|
||
initialized. */
|
||
setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
|
||
|
||
/* Make sure that we never pass a connection fd variable
|
||
when using a simple pipe. */
|
||
unsetenv ("_assuan_connection_fd");
|
||
|
||
execv (name, (char *const *) argv);
|
||
/* oops - use the pipe to tell the parent about it */
|
||
snprintf (errbuf, sizeof(errbuf)-1,
|
||
"ERR %d can't exec `%s': %.50s\n",
|
||
_assuan_error (ASSUAN_Problem_Starting_Server),
|
||
name, strerror (errno));
|
||
errbuf[sizeof(errbuf)-1] = 0;
|
||
writen (1, errbuf, strlen (errbuf));
|
||
_exit (4);
|
||
}
|
||
#ifdef _ASSUAN_USE_DOUBLE_FORK
|
||
if (pid == -1)
|
||
_exit (1);
|
||
else
|
||
_exit (0);
|
||
#endif
|
||
}
|
||
|
||
#ifdef _ASSUAN_USE_DOUBLE_FORK
|
||
_assuan_waitpid ((*ctx)->pid, NULL, 0);
|
||
(*ctx)->pid = -1;
|
||
#endif
|
||
|
||
close (rp[1]);
|
||
close (wp[0]);
|
||
|
||
return initial_handshake (ctx);
|
||
}
|
||
#endif /*!HAVE_W32_SYSTEM*/
|
||
|
||
|
||
#ifndef HAVE_W32_SYSTEM
|
||
/* This function is similar to pipe_connect but uses a socketpair and
|
||
sets the I/O up to use sendmsg/recvmsg. */
|
||
static assuan_error_t
|
||
socketpair_connect (assuan_context_t *ctx,
|
||
const char *name, const char *const argv[],
|
||
int *fd_child_list,
|
||
void (*atfork) (void *opaque, int reserved),
|
||
void *atforkvalue)
|
||
{
|
||
assuan_error_t err;
|
||
int fds[2];
|
||
char mypidstr[50];
|
||
|
||
if (!ctx
|
||
|| (name && (!argv || !argv[0]))
|
||
|| (!name && argv))
|
||
return _assuan_error (ASSUAN_Invalid_Value);
|
||
|
||
fix_signals ();
|
||
|
||
sprintf (mypidstr, "%lu", (unsigned long)getpid ());
|
||
|
||
if ( socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) )
|
||
{
|
||
_assuan_log_printf ("socketpair failed: %s\n", strerror (errno));
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
err = _assuan_new_context (ctx);
|
||
if (err)
|
||
{
|
||
close (fds[0]);
|
||
close (fds[1]);
|
||
return err;
|
||
}
|
||
(*ctx)->pipe_mode = 1;
|
||
(*ctx)->inbound.fd = fds[0];
|
||
(*ctx)->outbound.fd = fds[0];
|
||
_assuan_init_uds_io (*ctx);
|
||
(*ctx)->deinit_handler = _assuan_uds_deinit;
|
||
(*ctx)->finish_handler = do_finish;
|
||
|
||
(*ctx)->pid = fork ();
|
||
if ((*ctx)->pid < 0)
|
||
{
|
||
close (fds[0]);
|
||
close (fds[1]);
|
||
_assuan_release_context (*ctx);
|
||
*ctx = NULL;
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
if ((*ctx)->pid == 0)
|
||
{
|
||
#ifdef _ASSUAN_USE_DOUBLE_FORK
|
||
pid_t pid;
|
||
|
||
if ((pid = fork ()) == 0)
|
||
#endif
|
||
{
|
||
int fd, i, n;
|
||
char errbuf[512];
|
||
int *fdp;
|
||
|
||
if (atfork)
|
||
atfork (atforkvalue, 0);
|
||
|
||
/* Connect stdin and stdout to /dev/null. */
|
||
fd = open ("/dev/null", O_RDONLY);
|
||
if (fd == -1 || dup2 (fd, STDIN_FILENO) == -1)
|
||
{
|
||
_assuan_log_printf ("dup2(dev/null) failed: %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
fd = open ("/dev/null", O_WRONLY);
|
||
if (fd == -1 || dup2 (fd, STDOUT_FILENO) == -1)
|
||
{
|
||
_assuan_log_printf ("dup2(dev/null) failed: %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
|
||
/* Dup stderr to /dev/null unless it is in the list of FDs to be
|
||
passed to the child. */
|
||
fdp = fd_child_list;
|
||
if (fdp)
|
||
{
|
||
for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
|
||
;
|
||
}
|
||
if (!fdp || *fdp == -1)
|
||
{
|
||
fd = open ("/dev/null", O_WRONLY);
|
||
if (fd == -1 || dup2 (fd, STDERR_FILENO) == -1)
|
||
{
|
||
_assuan_log_printf ("dup2(dev/null) failed: %s\n",
|
||
strerror (errno));
|
||
_exit (4);
|
||
}
|
||
}
|
||
|
||
|
||
/* Close all files which will not be duped, are not in the
|
||
fd_child_list and are not the connection fd. */
|
||
n = sysconf (_SC_OPEN_MAX);
|
||
if (n < 0)
|
||
n = MAX_OPEN_FDS;
|
||
for (i=0; i < n; i++)
|
||
{
|
||
if ( i == STDIN_FILENO || i == STDOUT_FILENO
|
||
|| i == STDERR_FILENO || i == fds[1])
|
||
continue;
|
||
fdp = fd_child_list;
|
||
if (fdp)
|
||
{
|
||
while (*fdp != -1 && *fdp != i)
|
||
fdp++;
|
||
}
|
||
|
||
if (!(fdp && *fdp != -1))
|
||
close(i);
|
||
}
|
||
errno = 0;
|
||
|
||
/* We store our parents pid in the environment so that the
|
||
execed assuan server is able to read the actual pid of the
|
||
client. The server can't use getppid becuase it might have
|
||
been double forked before the assuan server has been
|
||
initialized. */
|
||
setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
|
||
|
||
/* Now set the environment variable used to convey the
|
||
connection's file descriptor. */
|
||
sprintf (mypidstr, "%d", fds[1]);
|
||
if (setenv ("_assuan_connection_fd", mypidstr, 1))
|
||
{
|
||
_assuan_log_printf ("setenv failed: %s\n", strerror (errno));
|
||
_exit (4);
|
||
}
|
||
|
||
if (!name && !argv)
|
||
{
|
||
/* No name and no args given, thus we don't do an exec
|
||
but continue the forked process. */
|
||
_assuan_release_context (*ctx);
|
||
*ctx = NULL;
|
||
return 0;
|
||
}
|
||
|
||
execv (name, (char *const *) argv);
|
||
/* oops - use the pipe to tell the parent about it */
|
||
snprintf (errbuf, sizeof(errbuf)-1,
|
||
"ERR %d can't exec `%s': %.50s\n",
|
||
_assuan_error (ASSUAN_Problem_Starting_Server),
|
||
name, strerror (errno));
|
||
errbuf[sizeof(errbuf)-1] = 0;
|
||
writen (fds[1], errbuf, strlen (errbuf));
|
||
_exit (4);
|
||
}
|
||
#ifdef _ASSUAN_USE_DOUBLE_FORK
|
||
if (pid == -1)
|
||
_exit (1);
|
||
else
|
||
_exit (0);
|
||
#endif
|
||
}
|
||
|
||
|
||
#ifdef _ASSUAN_USE_DOUBLE_FORK
|
||
_assuan_waitpid ((*ctx)->pid, NULL, 0);
|
||
(*ctx)->pid = -1;
|
||
#endif
|
||
|
||
close (fds[1]);
|
||
|
||
return initial_handshake (ctx);
|
||
}
|
||
#endif /*!HAVE_W32_SYSTEM*/
|
||
|
||
|
||
|
||
#ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
|
||
|
||
#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,
|
||
const char *name, const char *const argv[],
|
||
int *fd_child_list,
|
||
void (*atfork) (void *opaque, int reserved),
|
||
void *atforkvalue)
|
||
{
|
||
assuan_error_t err;
|
||
int pid;
|
||
int rp[2];
|
||
int wp[2];
|
||
char mypidstr[50];
|
||
struct spawn_fd_item_s child_fds[3]; /* stdin, stdout, terminating -1 */
|
||
|
||
if (!ctx || !name || !argv || !argv[0])
|
||
return _assuan_error (ASSUAN_Invalid_Value);
|
||
|
||
/* Actually, GPGME does this for us. But we plan to reuse this code
|
||
in the generic assuan. */
|
||
fix_signals ();
|
||
|
||
sprintf (mypidstr, "%lu", (unsigned long)getpid ());
|
||
|
||
/* Create the two pipes. */
|
||
if (_gpgme_io_pipe (rp, 0))
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
|
||
if (_gpgme_io_pipe (wp, 1))
|
||
{
|
||
_gpgme_io_close (rp[0]);
|
||
_gpgme_io_close (rp[1]);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
err = _assuan_new_context (ctx);
|
||
if (err)
|
||
{
|
||
_gpgme_io_close (rp[0]);
|
||
_gpgme_io_close (rp[1]);
|
||
_gpgme_io_close (wp[0]);
|
||
_gpgme_io_close (wp[1]);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
(*ctx)->pipe_mode = 1;
|
||
(*ctx)->inbound.fd = rp[0]; /* Our inbound is read end of read pipe. */
|
||
(*ctx)->outbound.fd = wp[1]; /* Our outbound is write end of write pipe. */
|
||
(*ctx)->deinit_handler = do_deinit;
|
||
(*ctx)->finish_handler = do_finish;
|
||
|
||
/* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
|
||
variable to mypidstr. However this requires us to write a full
|
||
environment handler, because the strings are expected in sorted
|
||
order. The suggestion given in the MS Reference Library, to save
|
||
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. */
|
||
/* Server stdout is its write end of our read pipe. */
|
||
child_fds[0].fd = rp[1];
|
||
child_fds[0].dup_to = 1;
|
||
/* 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;
|
||
|
||
/* Start the process. */
|
||
pid = _gpgme_io_spawn (name, argv, child_fds, child_fds);
|
||
if (pid == -1)
|
||
{
|
||
_assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno));
|
||
_gpgme_io_close (rp[0]);
|
||
_gpgme_io_close (rp[1]);
|
||
_gpgme_io_close (wp[0]);
|
||
_gpgme_io_close (wp[1]);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
/* ERR contains the PID. */
|
||
(*ctx)->pid = 0; /* We don't use the PID. */
|
||
|
||
/* FIXME: Should be done by GPGME. */
|
||
CloseHandle ((HANDLE) pid); /* We don't need to wait for the process. */
|
||
|
||
return initial_handshake (ctx);
|
||
}
|
||
|
||
#else
|
||
#ifdef HAVE_W32_SYSTEM
|
||
/* Build a command line for use with W32's CreateProcess. On success
|
||
CMDLINE gets the address of a newly allocated string. */
|
||
static int
|
||
build_w32_commandline (const char * const *argv, char **cmdline)
|
||
{
|
||
int i, n;
|
||
const char *s;
|
||
char *buf, *p;
|
||
|
||
*cmdline = NULL;
|
||
n = 0;
|
||
for (i=0; (s=argv[i]); i++)
|
||
{
|
||
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
|
||
for (; *s; s++)
|
||
if (*s == '\"')
|
||
n++; /* Need to double inner quotes. */
|
||
}
|
||
n++;
|
||
|
||
buf = p = xtrymalloc (n);
|
||
if (!buf)
|
||
return -1;
|
||
|
||
for (i=0; argv[i]; i++)
|
||
{
|
||
if (i)
|
||
p = stpcpy (p, " ");
|
||
if (!*argv[i]) /* Empty string. */
|
||
p = stpcpy (p, "\"\"");
|
||
else if (strpbrk (argv[i], " \t\n\v\f\""))
|
||
{
|
||
p = stpcpy (p, "\"");
|
||
for (s=argv[i]; *s; s++)
|
||
{
|
||
*p++ = *s;
|
||
if (*s == '\"')
|
||
*p++ = *s;
|
||
}
|
||
*p++ = '\"';
|
||
*p = 0;
|
||
}
|
||
else
|
||
p = stpcpy (p, argv[i]);
|
||
}
|
||
|
||
*cmdline= buf;
|
||
return 0;
|
||
}
|
||
#endif /*HAVE_W32_SYSTEM*/
|
||
|
||
|
||
#ifdef HAVE_W32_SYSTEM
|
||
/* Create pipe where one end end is inheritable. */
|
||
static int
|
||
create_inheritable_pipe (int filedes[2], int for_write)
|
||
{
|
||
HANDLE r, w, h;
|
||
SECURITY_ATTRIBUTES sec_attr;
|
||
|
||
memset (&sec_attr, 0, sizeof sec_attr );
|
||
sec_attr.nLength = sizeof sec_attr;
|
||
sec_attr.bInheritHandle = FALSE;
|
||
|
||
if (!CreatePipe (&r, &w, &sec_attr, 0))
|
||
{
|
||
_assuan_log_printf ("CreatePipe failed: %s\n", w32_strerror (-1));
|
||
return -1;
|
||
}
|
||
|
||
if (!DuplicateHandle (GetCurrentProcess(), for_write? r : w,
|
||
GetCurrentProcess(), &h, 0,
|
||
TRUE, DUPLICATE_SAME_ACCESS ))
|
||
{
|
||
_assuan_log_printf ("DuplicateHandle failed: %s\n", w32_strerror (-1));
|
||
CloseHandle (r);
|
||
CloseHandle (w);
|
||
return -1;
|
||
}
|
||
if (for_write)
|
||
{
|
||
CloseHandle (r);
|
||
r = h;
|
||
}
|
||
else
|
||
{
|
||
CloseHandle (w);
|
||
w = h;
|
||
}
|
||
|
||
_assuan_log_printf ("created pipe: read=%p%s, write=%p%s\n",
|
||
r, for_write? " (inherit)":"",
|
||
w, for_write? "":" (inherit)");
|
||
filedes[0] = handle_to_fd (r);
|
||
filedes[1] = handle_to_fd (w);
|
||
return 0;
|
||
}
|
||
#endif /* HAVE_W32_SYSTEM */
|
||
|
||
#ifdef HAVE_W32_SYSTEM
|
||
#define pipe_connect pipe_connect_w32
|
||
/* W32 version of the pipe connection code. */
|
||
static assuan_error_t
|
||
pipe_connect_w32 (assuan_context_t *ctx,
|
||
const char *name, const char *const argv[],
|
||
int *fd_child_list,
|
||
void (*atfork) (void *opaque, int reserved),
|
||
void *atforkvalue)
|
||
{
|
||
assuan_error_t err;
|
||
int rp[2];
|
||
int wp[2];
|
||
char mypidstr[50];
|
||
char *cmdline;
|
||
SECURITY_ATTRIBUTES sec_attr;
|
||
PROCESS_INFORMATION pi =
|
||
{
|
||
NULL, /* Returns process handle. */
|
||
0, /* Returns primary thread handle. */
|
||
0, /* Returns pid. */
|
||
0 /* Returns tid. */
|
||
};
|
||
STARTUPINFO si;
|
||
int fd, *fdp;
|
||
HANDLE nullfd = INVALID_HANDLE_VALUE;
|
||
|
||
if (!ctx || !name || !argv || !argv[0])
|
||
return _assuan_error (ASSUAN_Invalid_Value);
|
||
|
||
fix_signals ();
|
||
|
||
sprintf (mypidstr, "%lu", (unsigned long)getpid ());
|
||
|
||
/* Build the command line. */
|
||
if (build_w32_commandline (argv, &cmdline))
|
||
return _assuan_error (ASSUAN_Out_Of_Core);
|
||
|
||
/* Create thew two pipes. */
|
||
if (create_inheritable_pipe (rp, 0))
|
||
{
|
||
xfree (cmdline);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
if (create_inheritable_pipe (wp, 1))
|
||
{
|
||
CloseHandle (fd_to_handle (rp[0]));
|
||
CloseHandle (fd_to_handle (rp[1]));
|
||
xfree (cmdline);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
|
||
err = _assuan_new_context (ctx);
|
||
if (err)
|
||
{
|
||
CloseHandle (fd_to_handle (rp[0]));
|
||
CloseHandle (fd_to_handle (rp[1]));
|
||
CloseHandle (fd_to_handle (wp[0]));
|
||
CloseHandle (fd_to_handle (wp[1]));
|
||
xfree (cmdline);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
|
||
(*ctx)->pipe_mode = 1;
|
||
(*ctx)->inbound.fd = rp[0]; /* Our inbound is read end of read pipe. */
|
||
(*ctx)->outbound.fd = wp[1]; /* Our outbound is write end of write pipe. */
|
||
(*ctx)->deinit_handler = do_deinit;
|
||
(*ctx)->finish_handler = do_finish;
|
||
|
||
|
||
/* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
|
||
variable. However this requires us to write a full environment
|
||
handler, because the strings are expected in sorted order. The
|
||
suggestion given in the MS Reference Library, to save the old
|
||
value, changeit, create proces and restore it, is not thread
|
||
safe. */
|
||
|
||
/* Start the process. */
|
||
memset (&sec_attr, 0, sizeof sec_attr );
|
||
sec_attr.nLength = sizeof sec_attr;
|
||
sec_attr.bInheritHandle = FALSE;
|
||
|
||
memset (&si, 0, sizeof si);
|
||
si.cb = sizeof (si);
|
||
si.dwFlags = STARTF_USESTDHANDLES;
|
||
si.hStdInput = fd_to_handle (wp[0]);
|
||
si.hStdOutput = fd_to_handle (rp[1]);
|
||
|
||
/* Dup stderr to /dev/null unless it is in the list of FDs to be
|
||
passed to the child. */
|
||
fd = fileno (stderr);
|
||
fdp = fd_child_list;
|
||
if (fdp)
|
||
{
|
||
for (; *fdp != -1 && *fdp != fd; fdp++)
|
||
;
|
||
}
|
||
if (!fdp || *fdp == -1)
|
||
{
|
||
nullfd = CreateFile ("nul", GENERIC_WRITE,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
NULL, OPEN_EXISTING, 0, NULL);
|
||
_assuan_log_printf ("created nul device, hd=%p\n", nullfd);
|
||
if (nullfd == INVALID_HANDLE_VALUE)
|
||
{
|
||
_assuan_log_printf ("can't open `nul': %s\n", w32_strerror (-1));
|
||
CloseHandle (fd_to_handle (rp[0]));
|
||
CloseHandle (fd_to_handle (rp[1]));
|
||
CloseHandle (fd_to_handle (wp[0]));
|
||
CloseHandle (fd_to_handle (wp[1]));
|
||
xfree (cmdline);
|
||
_assuan_release_context (*ctx);
|
||
return -1;
|
||
}
|
||
si.hStdError = nullfd;
|
||
}
|
||
else
|
||
si.hStdError = fd_to_handle (_get_osfhandle (fd));
|
||
|
||
|
||
/* Note: We inherit all handles flagged as inheritable. This seems
|
||
to be a security flaw but there seems to be no way of selecting
|
||
handles to inherit. */
|
||
_assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n",
|
||
name, cmdline);
|
||
_assuan_log_printf (" stdin=%p stdout=%p stderr=%p\n",
|
||
si.hStdInput, si.hStdOutput, si.hStdError);
|
||
if (!CreateProcess (name, /* Program to start. */
|
||
cmdline, /* Command line arguments. */
|
||
&sec_attr, /* Process security attributes. */
|
||
&sec_attr, /* Thread security attributes. */
|
||
TRUE, /* Inherit handles. */
|
||
(CREATE_DEFAULT_ERROR_MODE
|
||
| DETACHED_PROCESS
|
||
| GetPriorityClass (GetCurrentProcess ())
|
||
| CREATE_SUSPENDED), /* Creation flags. */
|
||
NULL, /* Environment. */
|
||
NULL, /* Use current drive/directory. */
|
||
&si, /* Startup information. */
|
||
&pi /* Returns process information. */
|
||
))
|
||
{
|
||
_assuan_log_printf ("CreateProcess failed: %s\n", w32_strerror (-1));
|
||
CloseHandle (fd_to_handle (rp[0]));
|
||
CloseHandle (fd_to_handle (rp[1]));
|
||
CloseHandle (fd_to_handle (wp[0]));
|
||
CloseHandle (fd_to_handle (wp[1]));
|
||
if (nullfd != INVALID_HANDLE_VALUE)
|
||
CloseHandle (nullfd);
|
||
xfree (cmdline);
|
||
_assuan_release_context (*ctx);
|
||
return _assuan_error (ASSUAN_General_Error);
|
||
}
|
||
xfree (cmdline);
|
||
cmdline = NULL;
|
||
if (nullfd != INVALID_HANDLE_VALUE)
|
||
{
|
||
CloseHandle (nullfd);
|
||
nullfd = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
_assuan_log_printf ("closing handles %p and %p\n",
|
||
fd_to_handle (rp[1]), fd_to_handle (wp[0]) );
|
||
CloseHandle (fd_to_handle (rp[1]));
|
||
CloseHandle (fd_to_handle (wp[0]));
|
||
|
||
_assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p"
|
||
" dwProcessID=%d dwThreadId=%d\n",
|
||
pi.hProcess, pi.hThread,
|
||
(int) pi.dwProcessId, (int) pi.dwThreadId);
|
||
|
||
ResumeThread (pi.hThread);
|
||
CloseHandle (pi.hThread);
|
||
(*ctx)->pid = 0; /* We don't use the PID. */
|
||
CloseHandle (pi.hProcess); /* We don't need to wait for the process. */
|
||
|
||
return initial_handshake (ctx);
|
||
}
|
||
#endif /*HAVE_W32_SYSTEM*/
|
||
#endif /* !_ASSUAN_IN_GPGME_BUILD_ASSUAN */
|
||
|
||
|
||
/* Connect to a server over a pipe, creating the assuan context and
|
||
returning it in CTX. The server filename is NAME, the argument
|
||
vector in ARGV. FD_CHILD_LIST is a -1 terminated list of file
|
||
descriptors not to close in the child. */
|
||
assuan_error_t
|
||
assuan_pipe_connect (assuan_context_t *ctx, const char *name,
|
||
const char *const argv[], int *fd_child_list)
|
||
{
|
||
return pipe_connect (ctx, name, argv, fd_child_list, NULL, NULL);
|
||
}
|
||
|
||
|
||
|
||
assuan_error_t
|
||
assuan_pipe_connect2 (assuan_context_t *ctx,
|
||
const char *name, const char *const argv[],
|
||
int *fd_child_list,
|
||
void (*atfork) (void *opaque, int reserved),
|
||
void *atforkvalue)
|
||
{
|
||
return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue);
|
||
}
|
||
|
||
|
||
/* Connect to a server over a full-duplex socket (i.e. created by
|
||
socketpair), creating the assuan context and returning it in CTX.
|
||
The server filename is NAME, the argument vector in ARGV.
|
||
FD_CHILD_LIST is a -1 terminated list of file descriptors not to
|
||
close in the child. ATFORK is called in the child right after the
|
||
fork; ATFORKVALUE is passed as the first argument and 0 is passed
|
||
as the second argument. The ATFORK function should only act if the
|
||
second value is 0.
|
||
|
||
For now FLAGS may either take the value 0 to behave like
|
||
assuan_pipe_connect2 or 1 to enable the described full-duplex
|
||
socket behaviour.
|
||
|
||
If NAME as well as ARGV are NULL, no exec is done but the same
|
||
process is continued. However all file descriptors are closed and
|
||
some special environment variables are set. To let the caller
|
||
detect whether the child or the parent continues, the child returns
|
||
a CTX of NULL. */
|
||
assuan_error_t
|
||
assuan_pipe_connect_ext (assuan_context_t *ctx,
|
||
const char *name, const char *const argv[],
|
||
int *fd_child_list,
|
||
void (*atfork) (void *opaque, int reserved),
|
||
void *atforkvalue, unsigned int flags)
|
||
{
|
||
if ((flags & 1))
|
||
{
|
||
#ifdef HAVE_W32_SYSTEM
|
||
return _assuan_error (ASSUAN_Not_Implemented);
|
||
#else
|
||
return socketpair_connect (ctx, name, argv, fd_child_list,
|
||
atfork, atforkvalue);
|
||
#endif
|
||
}
|
||
else
|
||
return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue);
|
||
}
|
||
|