diff options
author | Werner Koch <wk@gnupg.org> | 2010-03-16 13:04:42 +0000 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2010-03-16 13:04:42 +0000 |
commit | 4266559927264f0cce31b07a207dbd23d578e147 (patch) | |
tree | e39ae18aa481faf05da4419592972e37d6a407d5 /src | |
parent | 59cc5ffd3b6092adb8d382aa3faf71571525c185 (diff) | |
download | libassuan-4266559927264f0cce31b07a207dbd23d578e147.tar.gz |
Refactored platform system code.
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 6 | ||||
-rw-r--r-- | src/Makefile.am | 11 | ||||
-rw-r--r-- | src/system-posix.c | 339 | ||||
-rw-r--r-- | src/system-w32.c | 466 | ||||
-rw-r--r-- | src/system-w32ce.c | 464 | ||||
-rw-r--r-- | src/system.c | 660 |
6 files changed, 1296 insertions, 650 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index dd71683..1d5ff2e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,9 @@ +2010-03-16 Werner Koch <wk@g10code.com> + + * system.c: For better readability move platform dependend code to .. + * system-posix.c, system-w32.c, system-w32ce.c: .. New. + * Makefile.am (common_sources): Account for this change. + 2010-03-11 Werner Koch <wk@g10code.com> * assuan-defs.h [!HAVE_VASPRINTF]: Add macros vasprintf and asprintf. diff --git a/src/Makefile.am b/src/Makefile.am index 6403e79..c946f11 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,6 +57,17 @@ common_sources = \ assuan-socket.c if HAVE_W32_SYSTEM +if HAVE_W32CE_SYSTEM +common_sources += system-w32ce.c +else +common_sources += system-w32.c +endif +else +common_sources += system-posix.c +endif + + +if HAVE_W32_SYSTEM LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \ `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \ diff --git a/src/system-posix.c b/src/system-posix.c new file mode 100644 index 0000000..e36767a --- /dev/null +++ b/src/system-posix.c @@ -0,0 +1,339 @@ +/* system-posix.c - System support functions. + Copyright (C) 2009, 2010 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <errno.h> +/* Solaris 8 needs sys/types.h before time.h. */ +#include <sys/types.h> +#include <time.h> +#include <fcntl.h> +#include <sys/wait.h> + +#include "assuan-defs.h" +#include "debug.h" + +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + + + + +assuan_fd_t +assuan_fdopen (int fd) +{ + return dup (fd); +} + + + +/* Sleep for the given number of microseconds. Default + implementation. */ +void +__assuan_usleep (assuan_context_t ctx, unsigned int usec) +{ + if (! usec) + return; + +#ifdef HAVE_NANOSLEEP + { + struct timespec req; + struct timespec rem; + + req.tv_sec = 0; + req.tv_nsec = usec * 1000; + + while (nanosleep (&req, &rem) < 0 && errno == EINTR) + req = rem; + } +#else + { + struct timeval tv; + + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + select (0, NULL, NULL, NULL, &tv); + } +#endif +} + + + +/* Create a pipe with one inheritable end. Easy for Posix. */ +int +__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) +{ + return pipe (fd); +} + + + +/* Close the given file descriptor, created with _assuan_pipe or one + of the socket functions. Easy for Posix. */ +int +__assuan_close (assuan_context_t ctx, assuan_fd_t fd) +{ + return close (fd); +} + + + +static ssize_t +__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) +{ + return read (fd, buffer, size); +} + + + +static ssize_t +__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, + size_t size) +{ + return write (fd, buffer, size); +} + + + +static int +__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, + int flags) +{ + int ret; + + do + ret = recvmsg (fd, msg, flags); + while (ret == -1 && errno == EINTR); + + return ret; +} + + + +static int +__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, + int flags) +{ + int ret; + + do + ret = sendmsg (fd, msg, flags); + while (ret == -1 && errno == EINTR); + + return ret; +} + + + +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 */ +} + + +int +__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, + const char **argv, + assuan_fd_t fd_in, assuan_fd_t fd_out, + assuan_fd_t *fd_child_list, + void (*atfork) (void *opaque, int reserved), + void *atforkvalue, unsigned int flags) +{ + int pid; + + pid = fork (); + if (pid < 0) + return -1; + + if (pid == 0) + { + /* Child process (server side). */ + int i; + int n; + char errbuf[512]; + int *fdp; + int fdnul; + + if (atfork) + atfork (atforkvalue, 0); + + fdnul = open ("/dev/null", O_WRONLY); + if (fdnul == -1) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "can't open `/dev/null': %s", strerror (errno)); + _exit (4); + } + + /* Dup handles to stdin/stdout. */ + if (fd_out != STDOUT_FILENO) + { + if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out, + STDOUT_FILENO) == -1) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "dup2 failed in child: %s", strerror (errno)); + _exit (4); + } + } + + if (fd_in != STDIN_FILENO) + { + if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in, + STDIN_FILENO) == -1) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "dup2 failed in child: %s", 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) + { + if (dup2 (fdnul, STDERR_FILENO) == -1) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx, + "dup2(dev/null, 2) failed: %s", strerror (errno)); + _exit (4); + } + } + close (fdnul); + + /* 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); + } + gpg_err_set_errno (0); + + if (! name) + { + /* No name and no args given, thus we don't do an exec + but continue the forked process. */ + *argv = "server"; + + /* FIXME: Cleanup. */ + 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 (ctx, GPG_ERR_ASS_SERVER_START), + name, strerror (errno)); + errbuf[sizeof(errbuf)-1] = 0; + writen (1, errbuf, strlen (errbuf)); + _exit (4); + } + + if (! name) + *argv = "client"; + + *r_pid = pid; + + return 0; +} + + + +/* FIXME: Add some sort of waitpid function that covers GPGME and + gpg-agent's use of assuan. */ +static pid_t +__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, + int *status, int options) +{ + /* We can't just release the PID, a waitpid is mandatory. But + NOWAIT in POSIX systems just means the caller already did the + waitpid for this child. */ + if (! nowait) + return waitpid (pid, NULL, 0); + return 0; +} + + + +int +__assuan_socketpair (assuan_context_t ctx, int namespace, int style, + int protocol, assuan_fd_t filedes[2]) +{ + return socketpair (namespace, style, protocol, filedes); +} + + + +/* The default system hooks for assuan contexts. */ +struct assuan_system_hooks _assuan_system_hooks = + { + ASSUAN_SYSTEM_HOOKS_VERSION, + __assuan_usleep, + __assuan_pipe, + __assuan_close, + __assuan_read, + __assuan_write, + __assuan_recvmsg, + __assuan_sendmsg, + __assuan_spawn, + __assuan_waitpid, + __assuan_socketpair + }; diff --git a/src/system-w32.c b/src/system-w32.c new file mode 100644 index 0000000..31da194 --- /dev/null +++ b/src/system-w32.c @@ -0,0 +1,466 @@ +/* system-w32.c - System support functions for Windows. + Copyright (C) 2009, 2010 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <windows.h> + +#include "assuan-defs.h" +#include "debug.h" + + + +assuan_fd_t +assuan_fdopen (int fd) +{ + assuan_fd_t ifd = (assuan_fd_t) _get_osfhandle (fd); + assuan_fd_t ofd; + + if (! DuplicateHandle(GetCurrentProcess(), ifd, + GetCurrentProcess(), &ofd, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + gpg_err_set_errno (EIO); + return ASSUAN_INVALID_FD; + } + return ofd; +} + + + +/* Sleep for the given number of microseconds. Default + implementation. */ +void +__assuan_usleep (assuan_context_t ctx, unsigned int usec) +{ + if (!usec) + return; + + Sleep (usec / 1000); +} + + + +/* Create a pipe with one inheritable end. Default implementation. */ +int +__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) +{ + HANDLE rh; + HANDLE wh; + HANDLE th; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof (sec_attr)); + sec_attr.nLength = sizeof (sec_attr); + sec_attr.bInheritHandle = FALSE; + + if (!CreatePipe (&rh, &wh, &sec_attr, 0)) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, + "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); + gpg_err_set_errno (EIO); + return -1; + } + + if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh, + GetCurrentProcess(), &th, 0, + TRUE, DUPLICATE_SAME_ACCESS )) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, + "DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1)); + CloseHandle (rh); + CloseHandle (wh); + gpg_err_set_errno (EIO); + return -1; + } + if (inherit_idx == 0) + { + CloseHandle (rh); + rh = th; + } + else + { + CloseHandle (wh); + wh = th; + } + + fd[0] = rh; + fd[1] = wh; + + return 0; +} + + + +/* Close the given file descriptor, created with _assuan_pipe or one + of the socket functions. Default implementation. */ +int +__assuan_close (assuan_context_t ctx, assuan_fd_t fd) +{ + int rc = closesocket (HANDLE2SOCKET(fd)); + if (rc) + gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) ); + if (rc && WSAGetLastError () == WSAENOTSOCK) + { + rc = CloseHandle (fd); + if (rc) + /* FIXME. */ + gpg_err_set_errno (EIO); + } + return rc; +} + + + +static ssize_t +__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) +{ + /* Due to the peculiarities of the W32 API we can't use read for a + network socket and thus we try to use recv first and fallback to + read if recv detects that it is not a network socket. */ + int res; + + res = recv (HANDLE2SOCKET (fd), buffer, size, 0); + if (res == -1) + { + switch (WSAGetLastError ()) + { + case WSAENOTSOCK: + { + DWORD nread = 0; + + res = ReadFile (fd, buffer, size, &nread, NULL); + if (! res) + { + switch (GetLastError ()) + { + case ERROR_BROKEN_PIPE: + gpg_err_set_errno (EPIPE); + break; + + default: + gpg_err_set_errno (EIO); + } + res = -1; + } + else + res = (int) nread; + } + break; + + case WSAEWOULDBLOCK: + gpg_err_set_errno (EAGAIN); + break; + + case ERROR_BROKEN_PIPE: + gpg_err_set_errno (EPIPE); + break; + + default: + gpg_err_set_errno (EIO); + break; + } + } + return res; +} + + + +static ssize_t +__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, + size_t size) +{ + /* Due to the peculiarities of the W32 API we can't use write for a + network socket and thus we try to use send first and fallback to + write if send detects that it is not a network socket. */ + int res; + + res = send (HANDLE2SOCKET (fd), buffer, size, 0); + if (res == -1 && WSAGetLastError () == WSAENOTSOCK) + { + DWORD nwrite; + + res = WriteFile (fd, buffer, size, &nwrite, NULL); + if (! res) + { + switch (GetLastError ()) + { + case ERROR_BROKEN_PIPE: + case ERROR_NO_DATA: + gpg_err_set_errno (EPIPE); + break; + + default: + gpg_err_set_errno (EIO); + break; + } + res = -1; + } + else + res = (int) nwrite; + } + return res; +} + + + +static int +__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, + int flags) +{ + gpg_err_set_errno (ENOSYS); + return -1; +} + + + + +static int +__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, + int flags) +{ + gpg_err_set_errno (ENOSYS); + return -1; +} + + + + +/* 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 (assuan_context_t ctx, 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 = _assuan_malloc (ctx, 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; +} + + +int +__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, + const char **argv, + assuan_fd_t fd_in, assuan_fd_t fd_out, + assuan_fd_t *fd_child_list, + void (*atfork) (void *opaque, int reserved), + void *atforkvalue, unsigned int flags) +{ + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFO si; + assuan_fd_t fd; + assuan_fd_t *fdp; + char *cmdline; + HANDLE nullfd = INVALID_HANDLE_VALUE; + + /* 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. */ + + /* Build the command line. */ + if (build_w32_commandline (ctx, argv, &cmdline)) + return -1; + + /* 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; + /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ + si.hStdInput = fd_in; + si.hStdOutput = fd_out; + + /* Dup stderr to /dev/null unless it is in the list of FDs to be + passed to the child. */ + fd = assuan_fd_from_posix_fd (fileno (stderr)); + fdp = fd_child_list; + if (fdp) + { + for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) + ; + } + if (!fdp || *fdp == ASSUAN_INVALID_FD) + { + nullfd = CreateFileW (L"nul", GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (nullfd == INVALID_HANDLE_VALUE) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); + _assuan_free (ctx, cmdline); + gpg_err_set_errno (EIO); + return -1; + } + si.hStdError = nullfd; + } + else + si.hStdError = 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); */ + 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 + | ((flags & 128)? DETACHED_PROCESS : 0) + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED), /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, + "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); + _assuan_free (ctx, cmdline); + if (nullfd != INVALID_HANDLE_VALUE) + CloseHandle (nullfd); + + gpg_err_set_errno (EIO); + return -1; + } + + _assuan_free (ctx, cmdline); + if (nullfd != INVALID_HANDLE_VALUE) + CloseHandle (nullfd); + + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + *r_pid = (pid_t) pi.hProcess; + + /* No need to modify peer process, as we don't change the handle + names. However this also means we are not safe, as we inherit + too many handles. Should use approach similar to gpgme and glib + using a helper process. */ + + return 0; +} + + + + +/* FIXME: Add some sort of waitpid function that covers GPGME and + gpg-agent's use of assuan. */ +static pid_t +__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, + int *status, int options) +{ + CloseHandle ((HANDLE) pid); + return 0; +} + + + +int +__assuan_socketpair (assuan_context_t ctx, int namespace, int style, + int protocol, assuan_fd_t filedes[2]) +{ + gpg_err_set_errno (ENOSYS); + return -1; +} + + +/* The default system hooks for assuan contexts. */ +struct assuan_system_hooks _assuan_system_hooks = + { + ASSUAN_SYSTEM_HOOKS_VERSION, + __assuan_usleep, + __assuan_pipe, + __assuan_close, + __assuan_read, + __assuan_write, + __assuan_recvmsg, + __assuan_sendmsg, + __assuan_spawn, + __assuan_waitpid, + __assuan_socketpair + }; diff --git a/src/system-w32ce.c b/src/system-w32ce.c new file mode 100644 index 0000000..ce0796c --- /dev/null +++ b/src/system-w32ce.c @@ -0,0 +1,464 @@ +/* system-w32ce.c - System support functions for WindowsCE. + Copyright (C) 2010 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <windows.h> + +#include "assuan-defs.h" +#include "debug.h" + + + +assuan_fd_t +assuan_fdopen (int fd) +{ + assuan_fd_t ifd = (assuan_fd_t)fd; + assuan_fd_t ofd; + + if (! DuplicateHandle(GetCurrentProcess(), ifd, + GetCurrentProcess(), &ofd, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + gpg_err_set_errno (EIO); + return ASSUAN_INVALID_FD; + } + return ofd; +} + + + +/* Sleep for the given number of microseconds. Default + implementation. */ +void +__assuan_usleep (assuan_context_t ctx, unsigned int usec) +{ + if (!usec) + return; + + Sleep (usec / 1000); +} + + + +/* Create a pipe with one inheritable end. Default implementation. */ +int +__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) +{ + HANDLE rh; + HANDLE wh; + HANDLE th; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof (sec_attr)); + sec_attr.nLength = sizeof (sec_attr); + sec_attr.bInheritHandle = FALSE; + + if (!CreatePipe (&rh, &wh, &sec_attr, 0)) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, + "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); + gpg_err_set_errno (EIO); + return -1; + } + + if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh, + GetCurrentProcess(), &th, 0, + TRUE, DUPLICATE_SAME_ACCESS )) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, + "DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1)); + CloseHandle (rh); + CloseHandle (wh); + gpg_err_set_errno (EIO); + return -1; + } + if (inherit_idx == 0) + { + CloseHandle (rh); + rh = th; + } + else + { + CloseHandle (wh); + wh = th; + } + + fd[0] = rh; + fd[1] = wh; + + return 0; +} + + + +/* Close the given file descriptor, created with _assuan_pipe or one + of the socket functions. Default implementation. */ +int +__assuan_close (assuan_context_t ctx, assuan_fd_t fd) +{ + int rc = closesocket (HANDLE2SOCKET(fd)); + if (rc) + gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) ); + if (rc && WSAGetLastError () == WSAENOTSOCK) + { + rc = CloseHandle (fd); + if (rc) + /* FIXME. */ + gpg_err_set_errno (EIO); + } + return rc; +} + + + +static ssize_t +__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) +{ + /* Due to the peculiarities of the W32 API we can't use read for a + network socket and thus we try to use recv first and fallback to + read if recv detects that it is not a network socket. */ + int res; + + res = recv (HANDLE2SOCKET (fd), buffer, size, 0); + if (res == -1) + { + switch (WSAGetLastError ()) + { + case WSAENOTSOCK: + { + DWORD nread = 0; + + res = ReadFile (fd, buffer, size, &nread, NULL); + if (! res) + { + switch (GetLastError ()) + { + case ERROR_BROKEN_PIPE: + gpg_err_set_errno (EPIPE); + break; + + default: + gpg_err_set_errno (EIO); + } + res = -1; + } + else + res = (int) nread; + } + break; + + case WSAEWOULDBLOCK: + gpg_err_set_errno (EAGAIN); + break; + + case ERROR_BROKEN_PIPE: + gpg_err_set_errno (EPIPE); + break; + + default: + gpg_err_set_errno (EIO); + break; + } + } + return res; +} + + + +static ssize_t +__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, + size_t size) +{ + /* Due to the peculiarities of the W32 API we can't use write for a + network socket and thus we try to use send first and fallback to + write if send detects that it is not a network socket. */ + int res; + + res = send (HANDLE2SOCKET (fd), buffer, size, 0); + if (res == -1 && WSAGetLastError () == WSAENOTSOCK) + { + DWORD nwrite; + + res = WriteFile (fd, buffer, size, &nwrite, NULL); + if (! res) + { + switch (GetLastError ()) + { + case ERROR_BROKEN_PIPE: + case ERROR_NO_DATA: + gpg_err_set_errno (EPIPE); + break; + + default: + gpg_err_set_errno (EIO); + break; + } + res = -1; + } + else + res = (int) nwrite; + } + return res; +} + + + +static int +__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, + int flags) +{ + gpg_err_set_errno (ENOSYS); + return -1; +} + + + + +static int +__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, + int flags) +{ + gpg_err_set_errno (ENOSYS); + return -1; +} + + + + +/* 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 (assuan_context_t ctx, 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 = _assuan_malloc (ctx, 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; +} + + +int +__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, + const char **argv, + assuan_fd_t fd_in, assuan_fd_t fd_out, + assuan_fd_t *fd_child_list, + void (*atfork) (void *opaque, int reserved), + void *atforkvalue, unsigned int flags) +{ + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFO si; + assuan_fd_t fd; + assuan_fd_t *fdp; + char *cmdline; + HANDLE nullfd = INVALID_HANDLE_VALUE; + + /* 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. */ + + /* Build the command line. */ + if (build_w32_commandline (ctx, argv, &cmdline)) + return -1; + + /* 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; + /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ + si.hStdInput = fd_in; + si.hStdOutput = fd_out; + + /* Dup stderr to /dev/null unless it is in the list of FDs to be + passed to the child. */ + fd = assuan_fd_from_posix_fd (fileno (stderr)); + fdp = fd_child_list; + if (fdp) + { + for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) + ; + } + if (!fdp || *fdp == ASSUAN_INVALID_FD) + { + nullfd = CreateFileW (L"nul", GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (nullfd == INVALID_HANDLE_VALUE) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); + _assuan_free (ctx, cmdline); + gpg_err_set_errno (EIO); + return -1; + } + si.hStdError = nullfd; + } + else + si.hStdError = 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); */ + 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 + | CREATE_SUSPENDED), /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, + "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); + _assuan_free (ctx, cmdline); + if (nullfd != INVALID_HANDLE_VALUE) + CloseHandle (nullfd); + + gpg_err_set_errno (EIO); + return -1; + } + + _assuan_free (ctx, cmdline); + if (nullfd != INVALID_HANDLE_VALUE) + CloseHandle (nullfd); + + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + *r_pid = (pid_t) pi.hProcess; + + /* No need to modify peer process, as we don't change the handle + names. However this also means we are not safe, as we inherit + too many handles. Should use approach similar to gpgme and glib + using a helper process. */ + + return 0; +} + + + + +/* FIXME: Add some sort of waitpid function that covers GPGME and + gpg-agent's use of assuan. */ +static pid_t +__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, + int *status, int options) +{ + CloseHandle ((HANDLE) pid); + return 0; +} + + + +int +__assuan_socketpair (assuan_context_t ctx, int namespace, int style, + int protocol, assuan_fd_t filedes[2]) +{ + gpg_err_set_errno (ENOSYS); + return -1; +} + + +/* The default system hooks for assuan contexts. */ +struct assuan_system_hooks _assuan_system_hooks = + { + ASSUAN_SYSTEM_HOOKS_VERSION, + __assuan_usleep, + __assuan_pipe, + __assuan_close, + __assuan_read, + __assuan_write, + __assuan_recvmsg, + __assuan_sendmsg, + __assuan_spawn, + __assuan_waitpid, + __assuan_socketpair + }; diff --git a/src/system.c b/src/system.c index fa942f7..22d7a0b 100644 --- a/src/system.c +++ b/src/system.c @@ -28,11 +28,6 @@ #include <sys/types.h> #include <time.h> #include <fcntl.h> -#ifdef HAVE_W32_SYSTEM -# include <windows.h> -#else -# include <sys/wait.h> -#endif #include "assuan-defs.h" #include "debug.h" @@ -45,30 +40,7 @@ #define DEBUG_SYSIO 0 - -assuan_fd_t -assuan_fdopen (int fd) -{ -#ifdef HAVE_W32_SYSTEM -#ifdef HAVE_W32CE_SYSTEM - assuan_fd_t ifd = (assuan_fd_t)fd; -#else - assuan_fd_t ifd = (assuan_fd_t) _get_osfhandle (fd); -#endif - assuan_fd_t ofd; - if (! DuplicateHandle(GetCurrentProcess(), ifd, - GetCurrentProcess(), &ofd, 0, - TRUE, DUPLICATE_SAME_ACCESS)) - { - gpg_err_set_errno (EIO); - return ASSUAN_INVALID_FD; - } - return ofd; -#else - return dup (fd); -#endif -} /* Manage memory specific to a context. */ @@ -154,40 +126,8 @@ _assuan_system_hooks_copy (assuan_system_hooks_t dst, ; } - -/* Sleep for the given number of microseconds. Default - implementation. */ -void -__assuan_usleep (assuan_context_t ctx, unsigned int usec) -{ - if (! usec) - return; - -#ifdef HAVE_NANOSLEEP - { - struct timespec req; - struct timespec rem; - - req.tv_sec = 0; - req.tv_nsec = usec * 1000; - - while (nanosleep (&req, &rem) < 0 && errno == EINTR) - req = rem; - } -#elif defined(HAVE_W32_SYSTEM) - Sleep (usec / 1000); -#else - { - struct timeval tv; - - tv.tv_sec = usec / 1000000; - tv.tv_usec = usec % 1000000; - select (0, NULL, NULL, NULL, &tv); - } -#endif -} - + /* Sleep for the given number of microseconds. */ void _assuan_usleep (assuan_context_t ctx, unsigned int usec) @@ -198,61 +138,8 @@ _assuan_usleep (assuan_context_t ctx, unsigned int usec) (ctx->system.usleep) (ctx, usec); } - -/* Create a pipe with one inheritable end. Default implementation. */ -int -__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) -{ -#ifdef HAVE_W32_SYSTEM - HANDLE rh; - HANDLE wh; - HANDLE th; - SECURITY_ATTRIBUTES sec_attr; - - memset (&sec_attr, 0, sizeof (sec_attr)); - sec_attr.nLength = sizeof (sec_attr); - sec_attr.bInheritHandle = FALSE; - - if (!CreatePipe (&rh, &wh, &sec_attr, 0)) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, - "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); - gpg_err_set_errno (EIO); - return -1; - } - - if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh, - GetCurrentProcess(), &th, 0, - TRUE, DUPLICATE_SAME_ACCESS )) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, - "DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1)); - CloseHandle (rh); - CloseHandle (wh); - gpg_err_set_errno (EIO); - return -1; - } - if (inherit_idx == 0) - { - CloseHandle (rh); - rh = th; - } - else - { - CloseHandle (wh); - wh = th; - } - - fd[0] = rh; - fd[1] = wh; - - return 0; -#else - return pipe (fd); -#endif -} - + /* Create a pipe with one inheritable end. */ int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) @@ -269,30 +156,8 @@ _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) return TRACE_SUC2 ("read=0x%x, write=0x%x", fd[0], fd[1]); } - -/* Close the given file descriptor, created with _assuan_pipe or one - of the socket functions. Default implementation. */ -int -__assuan_close (assuan_context_t ctx, assuan_fd_t fd) -{ -#ifdef HAVE_W32_SYSTEM - int rc = closesocket (HANDLE2SOCKET(fd)); - if (rc) - gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) ); - if (rc && WSAGetLastError () == WSAENOTSOCK) - { - rc = CloseHandle (fd); - if (rc) - /* FIXME. */ - gpg_err_set_errno (EIO); - } - return rc; -#else - return close (fd); -#endif -} - + /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. */ int @@ -304,64 +169,8 @@ _assuan_close (assuan_context_t ctx, assuan_fd_t fd) return (ctx->system.close) (ctx, fd); } - -static ssize_t -__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) -{ -#ifdef HAVE_W32_SYSTEM - /* Due to the peculiarities of the W32 API we can't use read for a - network socket and thus we try to use recv first and fallback to - read if recv detects that it is not a network socket. */ - int res; - - res = recv (HANDLE2SOCKET (fd), buffer, size, 0); - if (res == -1) - { - switch (WSAGetLastError ()) - { - case WSAENOTSOCK: - { - DWORD nread = 0; - - res = ReadFile (fd, buffer, size, &nread, NULL); - if (! res) - { - switch (GetLastError ()) - { - case ERROR_BROKEN_PIPE: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - } - res = -1; - } - else - res = (int) nread; - } - break; - - case WSAEWOULDBLOCK: - gpg_err_set_errno (EAGAIN); - break; - - case ERROR_BROKEN_PIPE: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - break; - } - } - return res; -#else /*!HAVE_W32_SYSTEM*/ - return read (fd, buffer, size); -#endif /*!HAVE_W32_SYSTEM*/ -} - + ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { @@ -376,48 +185,8 @@ _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) #endif } - -static ssize_t -__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, - size_t size) -{ -#ifdef HAVE_W32_SYSTEM - /* Due to the peculiarities of the W32 API we can't use write for a - network socket and thus we try to use send first and fallback to - write if send detects that it is not a network socket. */ - int res; - - res = send (HANDLE2SOCKET (fd), buffer, size, 0); - if (res == -1 && WSAGetLastError () == WSAENOTSOCK) - { - DWORD nwrite; - - res = WriteFile (fd, buffer, size, &nwrite, NULL); - if (! res) - { - switch (GetLastError ()) - { - case ERROR_BROKEN_PIPE: - case ERROR_NO_DATA: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - break; - } - res = -1; - } - else - res = (int) nwrite; - } - return res; -#else /*!HAVE_W32_SYSTEM*/ - return write (fd, buffer, size); -#endif /*!HAVE_W32_SYSTEM*/ -} - + ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) @@ -433,25 +202,8 @@ _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, #endif } - -static int -__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, - int flags) -{ -#ifdef HAVE_W32_SYSTEM - gpg_err_set_errno (ENOSYS); - return -1; -#else - int ret; - do - ret = recvmsg (fd, msg, flags); - while (ret == -1 && errno == EINTR); - - return ret; -#endif -} - + int _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) @@ -485,25 +237,8 @@ _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, #endif } - -static int -__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, - int flags) -{ -#ifdef HAVE_W32_SYSTEM - gpg_err_set_errno (ENOSYS); - return -1; -#else - int ret; - do - ret = sendmsg (fd, msg, flags); - while (ret == -1 && errno == EINTR); - - return ret; -#endif -} - + int _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) @@ -536,340 +271,8 @@ _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, #endif } - -#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 (assuan_context_t ctx, 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 = _assuan_malloc (ctx, 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; -} - - -int -__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, - const char **argv, - assuan_fd_t fd_in, assuan_fd_t fd_out, - assuan_fd_t *fd_child_list, - void (*atfork) (void *opaque, int reserved), - void *atforkvalue, unsigned int flags) -{ - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFO si; - assuan_fd_t fd; - assuan_fd_t *fdp; - char *cmdline; - HANDLE nullfd = INVALID_HANDLE_VALUE; - - /* 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. */ - - /* Build the command line. */ - if (build_w32_commandline (ctx, argv, &cmdline)) - return -1; - - /* 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; - /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ - si.hStdInput = fd_in; - si.hStdOutput = fd_out; - - /* Dup stderr to /dev/null unless it is in the list of FDs to be - passed to the child. */ - fd = assuan_fd_from_posix_fd (fileno (stderr)); - fdp = fd_child_list; - if (fdp) - { - for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) - ; - } - if (!fdp || *fdp == ASSUAN_INVALID_FD) - { - nullfd = CreateFileW (L"nul", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, NULL); - if (nullfd == INVALID_HANDLE_VALUE) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); - _assuan_free (ctx, cmdline); - gpg_err_set_errno (EIO); - return -1; - } - si.hStdError = nullfd; - } - else - si.hStdError = fd; - -#ifdef HAVE_W32CE_SYSTEM -# define DETACHED_PROCESS (0) -#endif - /* 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); */ - 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 - | ((flags & 128)? DETACHED_PROCESS : 0) -#ifndef HAVE_W32CE_SYSTEM - | GetPriorityClass (GetCurrentProcess ()) -#endif - | CREATE_SUSPENDED), /* Creation flags. */ - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - )) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, - "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); - _assuan_free (ctx, cmdline); - if (nullfd != INVALID_HANDLE_VALUE) - CloseHandle (nullfd); - - gpg_err_set_errno (EIO); - return -1; - } - - _assuan_free (ctx, cmdline); - if (nullfd != INVALID_HANDLE_VALUE) - CloseHandle (nullfd); - - ResumeThread (pi.hThread); - CloseHandle (pi.hThread); - - /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ - /* " dwProcessID=%d dwThreadId=%d\n", */ - /* pi.hProcess, pi.hThread, */ - /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ - - *r_pid = (pid_t) pi.hProcess; - - /* No need to modify peer process, as we don't change the handle - names. However this also means we are not safe, as we inherit - too many handles. Should use approach similar to gpgme and glib - using a helper process. */ - - return 0; -} - -#else - -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 */ -} - - -int -__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, - const char **argv, - assuan_fd_t fd_in, assuan_fd_t fd_out, - assuan_fd_t *fd_child_list, - void (*atfork) (void *opaque, int reserved), - void *atforkvalue, unsigned int flags) -{ - int pid; - - pid = fork (); - if (pid < 0) - return -1; - - if (pid == 0) - { - /* Child process (server side). */ - int i; - int n; - char errbuf[512]; - int *fdp; - int fdnul; - - if (atfork) - atfork (atforkvalue, 0); - - fdnul = open ("/dev/null", O_WRONLY); - if (fdnul == -1) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "can't open `/dev/null': %s", strerror (errno)); - _exit (4); - } - - /* Dup handles to stdin/stdout. */ - if (fd_out != STDOUT_FILENO) - { - if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out, - STDOUT_FILENO) == -1) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "dup2 failed in child: %s", strerror (errno)); - _exit (4); - } - } - - if (fd_in != STDIN_FILENO) - { - if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in, - STDIN_FILENO) == -1) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "dup2 failed in child: %s", 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) - { - if (dup2 (fdnul, STDERR_FILENO) == -1) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx, - "dup2(dev/null, 2) failed: %s", strerror (errno)); - _exit (4); - } - } - close (fdnul); - - /* 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); - } - gpg_err_set_errno (0); - - if (! name) - { - /* No name and no args given, thus we don't do an exec - but continue the forked process. */ - *argv = "server"; - - /* FIXME: Cleanup. */ - 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 (ctx, GPG_ERR_ASS_SERVER_START), - name, strerror (errno)); - errbuf[sizeof(errbuf)-1] = 0; - writen (1, errbuf, strlen (errbuf)); - _exit (4); - } - - if (! name) - *argv = "client"; - - *r_pid = pid; - - return 0; -} -#endif /* ! HAVE_W32_SYSTEM */ - + /* Create a new process from NAME and ARGV. Provide FD_IN and FD_OUT as stdin and stdout. Inherit the ASSUAN_INVALID_FD-terminated FD_CHILD_LIST as given (no remapping), which must be inheritable. @@ -924,26 +327,10 @@ _assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, return TRACE_SYSERR (res); } + /* FIXME: Add some sort of waitpid function that covers GPGME and gpg-agent's use of assuan. */ -static pid_t -__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, - int *status, int options) -{ -#ifndef HAVE_W32_SYSTEM - /* We can't just release the PID, a waitpid is mandatory. But - NOWAIT in POSIX systems just means the caller already did the - waitpid for this child. */ - if (! nowait) - return waitpid (pid, NULL, 0); -#else /* ! HAVE_W32_SYSTEM */ - CloseHandle ((HANDLE) pid); -#endif /* HAVE_W32_SYSTEM */ - return 0; -} - - pid_t _assuan_waitpid (assuan_context_t ctx, pid_t pid, int action, int *status, int options) @@ -960,19 +347,8 @@ _assuan_waitpid (assuan_context_t ctx, pid_t pid, int action, #endif } - -int -__assuan_socketpair (assuan_context_t ctx, int namespace, int style, - int protocol, assuan_fd_t filedes[2]) -{ -#if HAVE_W32_SYSTEM - gpg_err_set_errno (ENOSYS); - return -1; -#else - return socketpair (namespace, style, protocol, filedes); -#endif -} + int _assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) @@ -989,19 +365,3 @@ _assuan_socketpair (assuan_context_t ctx, int namespace, int style, return TRACE_SYSERR (res); } - -/* The default system hooks for assuan contexts. */ -struct assuan_system_hooks _assuan_system_hooks = - { - ASSUAN_SYSTEM_HOOKS_VERSION, - __assuan_usleep, - __assuan_pipe, - __assuan_close, - __assuan_read, - __assuan_write, - __assuan_recvmsg, - __assuan_sendmsg, - __assuan_spawn, - __assuan_waitpid, - __assuan_socketpair - }; |