diff options
author | Werner Koch <[email protected]> | 2006-09-12 11:07:18 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2006-09-12 11:07:18 +0000 |
commit | d1210ab25c96e60fc439cfa9b50ec431642ada36 (patch) | |
tree | c123ce388013b3d83914648caf00b44e1d8d356d | |
parent | Added stuff to the manual. Minor prototype cleanups. (diff) | |
download | libassuan-d1210ab25c96e60fc439cfa9b50ec431642ada36.tar.gz libassuan-d1210ab25c96e60fc439cfa9b50ec431642ada36.zip |
Integrated descriptor passing.
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | src/ChangeLog | 32 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/assuan-defs.h | 50 | ||||
-rw-r--r-- | src/assuan-domain-connect.c | 534 | ||||
-rw-r--r-- | src/assuan-domain-server.c | 50 | ||||
-rw-r--r-- | src/assuan-handler.c | 2 | ||||
-rw-r--r-- | src/assuan-io.c | 134 | ||||
-rw-r--r-- | src/assuan-pipe-connect.c | 669 | ||||
-rw-r--r-- | src/assuan-pipe-server.c | 24 | ||||
-rw-r--r-- | src/assuan-socket-server.c | 7 | ||||
-rw-r--r-- | src/assuan-uds.c | 273 | ||||
-rw-r--r-- | src/assuan.h | 33 | ||||
-rw-r--r-- | tests/fdpassing.c | 161 |
15 files changed, 1071 insertions, 907 deletions
@@ -17,6 +17,11 @@ Noteworthy changes in version 0.9.0 printing of the full data, a new environment variable ASSUAN_FULL_LOGGING may be set to any value. + * Removed the assuan_domain fucntions. Added new function + assuan_pipe_connect_ext to allow connections on a socketpair and to + pass descriptors. + + Noteworthy changes in version 0.6.10 (2005-06-20) ------------------------------------------------- @@ -13,3 +13,4 @@ * Check the system error to assuan error translation * Do a configure test for SO_PEERCRED. We already use HAVE_SO_PEERCRED buty it never gets defined. +* Replace assuan_pipe_connect2 by assuan_pipe_connect.
\ No newline at end of file diff --git a/src/ChangeLog b/src/ChangeLog index 77649e6..fd4e3d3 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,35 @@ +2006-09-12 Werner Koch <[email protected]> + + * assuan-defs.h (DIM, DIMof): New. + + * assuan-domain-server.c: Removed. + * assuan-domain-connect.c: Renamed to .. + * assuan-uds.c: this. + (domain_reader, domain_writer, domain_sendfd, domain_receivefd) + (assuan_domain_connect, _assuan_domain_init): Removed. + (uds_reader, uds_writer, uds_sendfd, uds_receivefd) + (_assuan_init_uds_io): New. + (_assuan_uds_deinit): New. + + * assuan-io.c (_assuan_simple_sendmsg, _assuan_simple_recvmsg): New. + (my_pth_fdmode, my_pth_select): New. + +2006-09-11 Werner Koch <[email protected]> + + * assuan-pipe-server.c (assuan_init_pipe_server): Allow for + FILEDES to be NULL and try to start as a socketpair server in this + case. + + * assuan-pipe-connect.c (assuan_pipe_connect2): Split up into two + functions (unix and w32) for clarity. + (pipe_connect_unix): This is the new fucntion. Add USE_CMSG flag. + (pipe_connect_w32): Ditto. + (initial_handshake): Factored out code. + (socketpair_connect): New. + (assuan_pipe_connect_ext): New. + (do_finish): Handle case if outbound and inbound fd are the same. + This is to support socketpairs. + 2006-09-10 Werner Koch <[email protected]> * assuan-util.c (_assuan_log_print_buffer) diff --git a/src/Makefile.am b/src/Makefile.am index 1774875..e90b7af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,9 +46,8 @@ libassuan_a_SOURCES = \ assuan-socket-server.c \ assuan-pipe-connect.c \ assuan-socket-connect.c \ + assuan-uds.c \ assuan-io.c \ - assuan-domain-connect.c \ - assuan-domain-server.c \ assuan-logging.c \ assuan-socket.c diff --git a/src/assuan-defs.h b/src/assuan-defs.h index e6bacf5..1f908fb 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -106,7 +106,7 @@ struct assuan_context_s char *hello_line; char *okay_line; /* See assuan_set_okay_line() */ - void *user_pointer; /* For assuan_get_pointer and assuan-set_pointer (). */ + void *user_pointer; /* For assuan_get_pointer and assuan_set_pointer (). */ FILE *log_fp; @@ -116,7 +116,7 @@ struct assuan_context_s char line[LINELENGTH]; int linelen; /* w/o CR, LF - might not be the same as strlen(line) due to embedded nuls. However a nul - is always written at this pos */ + is always written at this pos. */ struct { char line[LINELENGTH]; int linelen ; @@ -135,7 +135,7 @@ struct assuan_context_s } outbound; int pipe_mode; /* We are in pipe mode, i.e. we can handle just one - connection and must terminate then */ + connection and must terminate then. */ pid_t pid; /* The pid of the peer. */ int listen_fd; /* The fd we are listening on (used by socket servers) */ int connected_fd; /* helper */ @@ -144,19 +144,19 @@ struct assuan_context_s /* Used for Unix domain sockets. */ struct sockaddr_un myaddr; struct sockaddr_un serveraddr; - /* When reading from datagram sockets, we must read an entire - message at a time. This means that we have to do our own - buffering to be able to get the semantics of read. */ - void *domainbuffer; - /* Offset of start of buffer. */ - int domainbufferoffset; - /* Bytes buffered. */ - int domainbuffersize; - /* Memory allocated. */ - int domainbufferallocated; - - int *pendingfds; - int pendingfdscount; + + /* Structure used for unix domain socket buffering. FIXME: We don't + use datagrams anymore thus we could get away with a simpler + buffering approach. */ + struct { + void *buffer; /* Malloced buffer. */ + int bufferallocated; /* Memory allocated. */ + int bufferoffset; /* Offset of start of buffer. */ + int buffersize; /* Bytes buffered. */ + + int pendingfds[5]; /* Array to save received descriptors. */ + int pendingfdscount; /* Number of received descriptors. */ + } uds; void (*deinit_handler)(ASSUAN_CONTEXT); int (*accept_handler)(ASSUAN_CONTEXT); @@ -184,13 +184,10 @@ struct assuan_context_s int _assuan_new_context (ASSUAN_CONTEXT *r_ctx); void _assuan_release_context (ASSUAN_CONTEXT ctx); -/*-- assuan-domain-connect.c --*/ -/* Make a connection to the Unix domain socket NAME and return a new - Assuan context in CTX. SERVER_PID is currently not used but may - become handy in the future. */ -assuan_error_t _assuan_domain_init (ASSUAN_CONTEXT *r_ctx, - int rendezvousfd, - pid_t peer); +/*-- assuan-uds.c --*/ +void _assuan_uds_deinit (assuan_context_t ctx); +void _assuan_init_uds_io (assuan_context_t ctx); + /*-- assuan-handler.c --*/ int _assuan_register_std_commands (ASSUAN_CONTEXT ctx); @@ -259,6 +256,8 @@ void _assuan_log_sanitized_string (const char *string); ssize_t _assuan_simple_read (ASSUAN_CONTEXT ctx, void *buffer, size_t size); ssize_t _assuan_simple_write (ASSUAN_CONTEXT ctx, const void *buffer, size_t size); +ssize_t _assuan_simple_sendmsg (assuan_context_t ctx, struct msghdr *msg); +ssize_t _assuan_simple_recvmsg (assuan_context_t ctx, struct msghdr *msg); /*-- assuan-socket.c --*/ int _assuan_close (int fd); @@ -290,4 +289,9 @@ char *stpcpy (char *dest, const char *src); int setenv (const char *name, const char *value, int replace); #endif + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) + + #endif /*ASSUAN_DEFS_H*/ diff --git a/src/assuan-domain-connect.c b/src/assuan-domain-connect.c deleted file mode 100644 index bc98e39..0000000 --- a/src/assuan-domain-connect.c +++ /dev/null @@ -1,534 +0,0 @@ -/* assuan-domain-connect.c - Assuan unix domain socket based client - * Copyright (C) 2002, 2003, 2005 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 <stddef.h> -#include <stdio.h> -#include <errno.h> -#include <sys/types.h> -#ifndef HAVE_W32_SYSTEM -#include <sys/socket.h> -#include <sys/un.h> -#else -#include <windows.h> -#endif -#if HAVE_SYS_UIO_H -#include <sys/uio.h> -#endif -#include <unistd.h> -#include <fcntl.h> -#include <string.h> -#include <assert.h> - -#include "assuan-defs.h" - -#ifndef PF_LOCAL -# ifdef PF_UNIX -# define PF_LOCAL PF_UNIX -# else -# define PF_LOCAL AF_UNIX -# endif -# ifndef AF_LOCAL -# define AF_LOCAL AF_UNIX -# endif -#endif - - - -/* Read an integer from byte address ADDR. Works even if ADDR is - misaligned. */ -static int -read_int (const void *addr) -{ - int val; - - memcpy (&val, addr, sizeof (int)); - - return val; -} - - -/* Write the integer VAL to byte address ADDR. Works even if ADDR is - misaligned. */ -static void -write_int (void *addr, int val) -{ - memcpy (addr, &val, sizeof (int)); -} - - -static void -do_deinit (assuan_context_t ctx) -{ - if (ctx->inbound.fd != -1) - _assuan_close (ctx->inbound.fd); - ctx->inbound.fd = -1; - ctx->outbound.fd = -1; - - if (ctx->domainbuffer) - { - assert (ctx->domainbufferallocated); - xfree (ctx->domainbuffer); - } - - if (ctx->pendingfds) - { - int i; - - assert (ctx->pendingfdscount > 0); - for (i = 0; i < ctx->pendingfdscount; i ++) - _assuan_close (ctx->pendingfds[i]); - - xfree (ctx->pendingfds); - } - - unlink (ctx->myaddr.sun_path); -} - - -/* Read from the socket server. */ -static ssize_t -domain_reader (assuan_context_t ctx, void *buf, size_t buflen) -{ - int len = ctx->domainbuffersize; - -#ifndef HAVE_W32_SYSTEM - start: - if (len == 0) - /* No data is buffered. */ - { - struct msghdr msg; - struct iovec iovec; - struct sockaddr_un sender; - struct - { - struct cmsghdr hdr; - int fd; - } cmsg; - - memset (&msg, 0, sizeof (msg)); - - for (;;) - { - msg.msg_name = &sender; - msg.msg_namelen = sizeof (struct sockaddr_un); - msg.msg_iov = &iovec; - msg.msg_iovlen = 1; - iovec.iov_base = ctx->domainbuffer; - iovec.iov_len = ctx->domainbufferallocated; - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof cmsg; - - /* Peek first: if the buffer we have is too small then it - will be truncated. */ - len = recvmsg (ctx->inbound.fd, &msg, MSG_PEEK); - if (len < 0) - { - _assuan_log_printf ("domain_reader: %s\n", strerror (errno)); - return -1; - } - - if (strcmp (ctx->serveraddr.sun_path, - ((struct sockaddr_un *) msg.msg_name)->sun_path) != 0) - { - /* XXX: Arg. Not from whom we expected! What do we - want to do? Should we just ignore it? Either way, - we still need to consume the message. */ - break; - } - - if (msg.msg_flags & MSG_TRUNC) - /* Enlarge the buffer and try again. */ - { - int size = ctx->domainbufferallocated; - void *tmp; - - if (size == 0) - size = 4 * 1024; - else - size *= 2; - - tmp = xtrymalloc (size); - if (! tmp) - return -1; - - xfree (ctx->domainbuffer); - ctx->domainbuffer = tmp; - ctx->domainbufferallocated = size; - } - else - /* We have enough space! */ - break; - } - - /* Now we have to actually consume it (remember, we only - peeked). */ - msg.msg_name = &sender; - msg.msg_namelen = sizeof (struct sockaddr_un); - msg.msg_iov = &iovec; - msg.msg_iovlen = 1; - iovec.iov_base = ctx->domainbuffer; - iovec.iov_len = ctx->domainbufferallocated; - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof cmsg; - - if (strcmp (ctx->serveraddr.sun_path, - ((struct sockaddr_un *) msg.msg_name)->sun_path) != 0) - { - /* XXX: Arg. Not from whom we expected! What do we want to - do? Should we just ignore it? We shall do the latter - for the moment. */ - _assuan_log_printf ("not setup to receive messages from `%s'\n", - ((struct sockaddr_un *) msg.msg_name)->sun_path); - goto start; - } - - len = recvmsg (ctx->inbound.fd, &msg, 0); - if (len < 0) - { - _assuan_log_printf ("domain_reader: %s\n", strerror (errno)); - return -1; - } - - ctx->domainbuffersize = len; - ctx->domainbufferoffset = 0; - - if (sizeof (cmsg) == msg.msg_controllen) - /* We received a file descriptor. */ - { - void *tmp; - - tmp = xtryrealloc (ctx->pendingfds, - sizeof (int) * (ctx->pendingfdscount + 1)); - if (! tmp) - { - _assuan_log_printf ("domain_reader: %s\n", strerror (errno)); - return -1; - } - - ctx->pendingfds = tmp; - ctx->pendingfds[ctx->pendingfdscount++] - = read_int (CMSG_DATA (&cmsg.hdr)); - - _assuan_log_printf ("received file descriptor %d from peer\n", - ctx->pendingfds[ctx->pendingfdscount - 1]); - } - - if (len == 0) - goto start; - } -#else - len = recvfrom (ctx->inbound.fd, buf, buflen, 0, NULL, NULL); -#endif - - /* Return some data to the user. */ - - if (len > buflen) - /* We have more than the user requested. */ - len = buflen; - - memcpy (buf, ctx->domainbuffer + ctx->domainbufferoffset, len); - ctx->domainbuffersize -= len; - assert (ctx->domainbuffersize >= 0); - ctx->domainbufferoffset += len; - assert (ctx->domainbufferoffset <= ctx->domainbufferallocated); - - return len; -} - -/* Write to the domain server. */ -static ssize_t -domain_writer (assuan_context_t ctx, const void *buf, size_t buflen) -{ -#ifndef HAVE_W32_SYSTEM - struct msghdr msg; - struct iovec iovec; - ssize_t len; - - memset (&msg, 0, sizeof (msg)); - - msg.msg_name = &ctx->serveraddr; - msg.msg_namelen = offsetof (struct sockaddr_un, sun_path) - + strlen (ctx->serveraddr.sun_path) + 1; - - msg.msg_iovlen = 1; - msg.msg_iov = &iovec; - iovec.iov_base = (void *) buf; - iovec.iov_len = buflen; - msg.msg_control = 0; - msg.msg_controllen = 0; - - len = sendmsg (ctx->outbound.fd, &msg, 0); - if (len < 0) - _assuan_log_printf ("domain_writer: %s\n", strerror (errno)); -#else - int len; - - len = sendto (ctx->outbound.fd, buf, buflen, 0, - (struct sockaddr *)&ctx->serveraddr, - sizeof (struct sockaddr_in)); -#endif - return len; -} - -static assuan_error_t -domain_sendfd (assuan_context_t ctx, int fd) -{ -#ifndef HAVE_W32_SYSTEM - struct msghdr msg; - struct - { - struct cmsghdr hdr; - int fd; - } cmsg; - int len; - - memset (&msg, 0, sizeof (msg)); - - msg.msg_name = &ctx->serveraddr; - msg.msg_namelen = offsetof (struct sockaddr_un, sun_path) - + strlen (ctx->serveraddr.sun_path) + 1; - - msg.msg_iovlen = 0; - msg.msg_iov = 0; - - cmsg.hdr.cmsg_level = SOL_SOCKET; - cmsg.hdr.cmsg_type = SCM_RIGHTS; - cmsg.hdr.cmsg_len = sizeof (cmsg); - - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof (cmsg); - - write_int (CMSG_DATA (&cmsg.hdr), fd); - - len = sendmsg (ctx->outbound.fd, &msg, 0); - if (len < 0) - { - _assuan_log_printf ("domain_sendfd: %s\n", strerror (errno)); - return _assuan_error (ASSUAN_General_Error); - } - else - return 0; -#else - return 0; -#endif -} - -static assuan_error_t -domain_receivefd (assuan_context_t ctx, int *fd) -{ -#ifndef HAVE_W32_SYSTEM - if (ctx->pendingfds == 0) - { - _assuan_log_printf ("no pending file descriptors!\n"); - return _assuan_error (ASSUAN_General_Error); - } - - *fd = ctx->pendingfds[0]; - if (-- ctx->pendingfdscount == 0) - { - xfree (ctx->pendingfds); - ctx->pendingfds = 0; - } - else /* Fix the array. */ - { - void *tmp; - - memmove (ctx->pendingfds, ctx->pendingfds + 1, - ctx->pendingfdscount * sizeof (int)); - tmp = xtryrealloc (ctx->pendingfds, - ctx->pendingfdscount * sizeof (int)); - if (tmp) - ctx->pendingfds = tmp; - /* Note: we ignore an shrinking error here thus the next realloc - to increase the size will succeed as the block is already of - the then requested size. */ - } -#endif - return 0; -} - - - -assuan_error_t -_assuan_domain_init (assuan_context_t *r_ctx, int rendezvousfd, pid_t peer) -{ - static struct assuan_io io = { domain_reader, domain_writer, - domain_sendfd, domain_receivefd }; - - assuan_error_t err; - assuan_context_t ctx; - int fd; - size_t len; - int tries; - - if (!r_ctx) - return _assuan_error (ASSUAN_Invalid_Value); - *r_ctx = NULL; - - err = _assuan_new_context (&ctx); - if (err) - return err; - - /* Save it in case we need it later. */ - ctx->pid = peer; - - /* Override the default (NOP) handlers. */ - ctx->deinit_handler = do_deinit; - - /* Setup the socket. */ - - fd = _assuan_sock_new (PF_LOCAL, SOCK_DGRAM, 0); - if (fd == -1) - { - _assuan_log_printf ("can't create socket: %s\n", strerror (errno)); - _assuan_release_context (ctx); - return _assuan_error (ASSUAN_General_Error); - } - - ctx->inbound.fd = fd; - ctx->outbound.fd = fd; - - /* And the io buffers. */ - - ctx->io = &io; - ctx->domainbuffer = 0; - ctx->domainbufferoffset = 0; - ctx->domainbuffersize = 0; - ctx->domainbufferallocated = 0; - ctx->pendingfds = 0; - ctx->pendingfdscount = 0; - - /* Get usable name and bind to it. */ - - for (tries = 0; tries < TMP_MAX; tries ++) - { - char *p; - char buf[L_tmpnam]; - - /* XXX: L_tmpnam must be shorter than sizeof (sun_path)! */ - assert (L_tmpnam < sizeof (ctx->myaddr.sun_path)); - - /* XXX: W32 tmpnam is broken */ - p = tmpnam (buf); - if (! p) - { - _assuan_log_printf ("cannot determine an appropriate temporary file " - "name. DoS in progress?\n"); - _assuan_release_context (ctx); - _assuan_close (fd); - return _assuan_error (ASSUAN_General_Error); - } - - memset (&ctx->myaddr, 0, sizeof ctx->myaddr); - ctx->myaddr.sun_family = AF_LOCAL; - len = strlen (buf) + 1; - memcpy (ctx->myaddr.sun_path, buf, len); - len += offsetof (struct sockaddr_un, sun_path); - - err = _assuan_sock_bind (fd, (struct sockaddr *) &ctx->myaddr, len); - if (! err) - break; - } - - if (err) - { - _assuan_log_printf ("can't bind to `%s': %s\n", ctx->myaddr.sun_path, - strerror (errno)); - _assuan_release_context (ctx); - _assuan_close (fd); - return _assuan_error (ASSUAN_Connect_Failed); - } - - /* Rendezvous with our peer. */ - { - FILE *fp; - char *p; - - fp = fdopen (rendezvousfd, "w+"); - if (! fp) - { - _assuan_log_printf ("can't open rendezvous port: %s\n", - strerror (errno)); - return _assuan_error (ASSUAN_Connect_Failed); - } - - /* Send our address. */ - fprintf (fp, "%s\n", ctx->myaddr.sun_path); - fflush (fp); - - /* And receive our peer's. */ - memset (&ctx->serveraddr, 0, sizeof ctx->serveraddr); - for (p = ctx->serveraddr.sun_path; - p < (ctx->serveraddr.sun_path - + sizeof ctx->serveraddr.sun_path - 1); - p ++) - { - *p = fgetc (fp); - if (*p == '\n') - break; - } - *p = '\0'; - fclose (fp); - - ctx->serveraddr.sun_family = AF_LOCAL; - } - - *r_ctx = ctx; - return 0; -} - -/* Connect to a Unix domain socket server. RENDEZVOUSFD is - bidirectional file descriptor (normally returned via socketpair) - which the client can use to rendezvous with the server. SERVER is - the server's pid. */ -assuan_error_t -assuan_domain_connect (assuan_context_t *r_ctx, int rendezvousfd, pid_t server) -{ - assuan_error_t aerr; - int okay, off; - - aerr = _assuan_domain_init (r_ctx, rendezvousfd, server); - if (aerr) - return aerr; - - /* Initial handshake. */ - aerr = _assuan_read_from_server (*r_ctx, &okay, &off); - if (aerr) - _assuan_log_printf ("can't connect to server: %s\n", - assuan_strerror (aerr)); - else if (okay != 1) - { - _assuan_log_printf ("can't connect to server: `"); - _assuan_log_sanitized_string ((*r_ctx)->inbound.line); - fprintf (assuan_get_assuan_log_stream (), "'\n"); - aerr = _assuan_error (ASSUAN_Connect_Failed); - } - - if (aerr) - assuan_disconnect (*r_ctx); - - return aerr; -} diff --git a/src/assuan-domain-server.c b/src/assuan-domain-server.c deleted file mode 100644 index 2194c39..0000000 --- a/src/assuan-domain-server.c +++ /dev/null @@ -1,50 +0,0 @@ -/* assuan-socket-server.c - Assuan socket based server - * Copyright (C) 2002 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 <unistd.h> - -#include "assuan-defs.h" - -/* Initialize a server. RENDEZVOUSFD is a bidirectional file - descriptor (normally returned via socketpair) that the domain - server can use to rendezvous with the client. CLIENT is the - client's pid. */ -assuan_error_t -assuan_init_domain_server (ASSUAN_CONTEXT *r_ctx, - int rendezvousfd, - pid_t client) -{ - assuan_error_t err; - - err = _assuan_domain_init (r_ctx, rendezvousfd, client); - if (err) - return err; - - (*r_ctx)->is_server = 1; - /* A domain server can only be used once. */ - (*r_ctx)->pipe_mode = 1; - - return 0; -} diff --git a/src/assuan-handler.c b/src/assuan-handler.c index d2e90a2..0624305 100644 --- a/src/assuan-handler.c +++ b/src/assuan-handler.c @@ -479,7 +479,7 @@ process_request (ASSUAN_CONTEXT ctx) rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK"); } else if (err_is_eof (rc)) - { /* No error checking because the peer may have already disconnect */ + { /* No error checking because the peer may have already disconnect. */ assuan_write_line (ctx, "OK closing connection"); ctx->finish_handler (ctx); } diff --git a/src/assuan-io.c b/src/assuan-io.c index 30302d6..3a3a017 100644 --- a/src/assuan-io.c +++ b/src/assuan-io.c @@ -1,5 +1,5 @@ /* assuan-io.c - Wraps the read and write functions. - * Copyright (C) 2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc. * * This file is part of Assuan. * @@ -23,23 +23,69 @@ #include <config.h> #endif -#include "assuan-defs.h" #include <sys/types.h> +#include <sys/socket.h> +#if HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif #include <unistd.h> +#include <errno.h> #ifdef HAVE_W32_SYSTEM #include <windows.h> #endif +#include "assuan-defs.h" + +/* We can't include pth.h and we are not sure whether other headers + already included it. This we define macros with the same + values. */ +#define MY_PTH_FDMODE_ERROR (-1) +#define MY_PTH_FDMODE_POLL 0 +#define MY_PTH_FDMODE_BLOCK 1 +#define MY_PTH_FDMODE_NONBLOCK 2 + + #ifndef _ASSUAN_NO_PTH extern ssize_t pth_read (int fd, void *buffer, size_t size); extern ssize_t pth_write (int fd, const void *buffer, size_t size); +extern int pth_fdmode (int, int); +extern int pth_select(int, fd_set*, fd_set*, fd_set*, struct timeval*); #ifndef HAVE_W32_SYSTEM #pragma weak pth_read #pragma weak pth_write +#pragma weak pth_fdmode +#pragma weak pth_select #endif #endif /*!_ASSUAN_NO_PTH*/ +#ifndef _ASSUAN_NO_PTH +/* Wrapper around pth_fdmode. */ +static int +my_pth_fdmode (int fd, int mode) +{ + if (pth_fdmode) + return pth_fdmode (fd, mode); + else + return MY_PTH_FDMODE_NONBLOCK; /* This is okay, given the way we use it. */ +} +#endif /*_ASSUAN_NO_PTH*/ + +#ifndef _ASSUAN_NO_PTH +/* Wrapper around pth_select. */ +static int +my_pth_select (int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds, + struct timeval *timeout) +{ + if (pth_select) + return pth_select (nfd, rfds, wfds, efds, timeout); + else + return 1; /* Fake one fd ready; this is okay, given the way we use it. */ +} +#endif /*_ASSUAN_NO_PTH*/ + + + ssize_t _assuan_simple_read (assuan_context_t ctx, void *buffer, size_t size) { @@ -69,3 +115,87 @@ _assuan_simple_write (assuan_context_t ctx, const void *buffer, size_t size) # endif #endif } + + +ssize_t +_assuan_simple_sendmsg (assuan_context_t ctx, struct msghdr *msg) +{ +#if defined(HAVE_W32_SYSTEM) + return _assuan_error (ASSUAN_Not_Implemented); +#elif defined(_ASSUAN_NO_PTH) + int ret; + while ( (ret = sendmsg (ctx->outbound.fd, msg, 0)) == -1 && errno == EINTR) + ; + return ret; +#else + /* Pth does not provide a sendmsg function. Thus we implement it here. */ + int ret; + int fd = ctx->outbound.fd; + int fdmode; + + fdmode = my_pth_fdmode (fd, MY_PTH_FDMODE_POLL); + if (fdmode == MY_PTH_FDMODE_ERROR) + { + errno = EBADF; + return -1; + } + if (fdmode == MY_PTH_FDMODE_BLOCK) + { + fd_set fds; + + FD_ZERO (&fds); + FD_SET (fd, &fds); + while ( (ret = my_pth_select (fd+1, NULL, &fds, NULL, NULL)) < 0 + && errno == EINTR) + ; + if (ret < 0) + return -1; + } + + while ((ret = sendmsg (fd, msg, 0)) == -1 && errno == EINTR) + ; + return ret; +#endif +} + + +ssize_t +_assuan_simple_recvmsg (assuan_context_t ctx, struct msghdr *msg) +{ +#if defined(HAVE_W32_SYSTEM) + return _assuan_error (ASSUAN_Not_Implemented); +#elif defined(_ASSUAN_NO_PTH) + int ret; + while ( (ret = recvmsg (ctx->inbound.fd, msg, 0)) == -1 && errno == EINTR) + ; + return ret; +#else + /* Pth does not provide a recvmsg function. Thus we implement it here. */ + int ret; + int fd = ctx->inbound.fd; + int fdmode; + + fdmode = my_pth_fdmode (fd, MY_PTH_FDMODE_POLL); + if (fdmode == MY_PTH_FDMODE_ERROR) + { + errno = EBADF; + return -1; + } + if (fdmode == MY_PTH_FDMODE_BLOCK) + { + fd_set fds; + + FD_ZERO (&fds); + FD_SET (fd, &fds); + while ( (ret = my_pth_select (fd+1, &fds, NULL, NULL, NULL)) < 0 + && errno == EINTR) + ; + if (ret < 0) + return -1; + } + + while ((ret = recvmsg (fd, msg, 0)) == -1 && errno == EINTR) + ; + return ret; +#endif +} diff --git a/src/assuan-pipe-connect.c b/src/assuan-pipe-connect.c index 4a6b436..c3ceb53 100644 --- a/src/assuan-pipe-connect.c +++ b/src/assuan-pipe-connect.c @@ -1,5 +1,5 @@ /* assuan-pipe-connect.c - Establish a pipe connection (client) - * Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. * * This file is part of Assuan. * @@ -112,6 +112,8 @@ 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) @@ -139,6 +141,402 @@ do_deinit (assuan_context_t 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 + 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 (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 + waitpid ((*ctx)->pid, NULL, 0); + (*ctx)->pid = -1; +#endif + + close (fds[1]); + + return initial_handshake (ctx); +} +#endif /*!HAVE_W32_SYSTEM*/ + + + #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. */ @@ -237,21 +635,16 @@ create_inheritable_pipe (int filedes[2], int for_write) #endif /*HAVE_W32_SYSTEM*/ -/* 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. 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. */ -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) -{ #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]; @@ -414,201 +807,11 @@ assuan_pipe_connect2 (assuan_context_t *ctx, (*ctx)->pid = 0; /* We don't use the PID. */ CloseHandle (pi.hProcess); /* We don't need to wait for the process. */ -#else /*!HAVE_W32_SYSTEM*/ - 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 becuase it might have - been double forked before the assuan server has been - initialized. */ - setenv ("_assuan_pipe_connect_pid", mypidstr, 1); - - 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 - waitpid ((*ctx)->pid, NULL, 0); - (*ctx)->pid = -1; -#endif - - close (rp[1]); - close (wp[0]); - -#endif /*!HAVE_W32_SYSTEM*/ - - /* initial handshake */ - { - int okay, off; - - 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; + return initial_handshake (ctx); } +#endif /*HAVE_W32_SYSTEM*/ - + /* 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 @@ -617,5 +820,53 @@ assuan_error_t assuan_pipe_connect (assuan_context_t *ctx, const char *name, const char *const argv[], int *fd_child_list) { - return assuan_pipe_connect2 (ctx, name, argv, fd_child_list, NULL, NULL); + return pipe_connect (ctx, name, argv, fd_child_list, NULL, NULL); } + + +/* 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. 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. */ +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 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. + + 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 specila environment variables are set. To let the caller + detect whether the cild 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) +{ +#ifdef HAVE_W32_SYSTEM + return _assuan_error (ASSUAN_Not_Implemented); +#else + return socketpair_connect (ctx, name, argv, fd_child_list, + atfork, atforkvalue); +#endif +} + diff --git a/src/assuan-pipe-server.c b/src/assuan-pipe-server.c index 1ad85dc..509fc12 100644 --- a/src/assuan-pipe-server.c +++ b/src/assuan-pipe-server.c @@ -111,8 +111,28 @@ assuan_init_pipe_server (ASSUAN_CONTEXT *r_ctx, int filedes[2]) ctx->inbound.fd = _get_osfhandle (filedes[0]); ctx->outbound.fd = _get_osfhandle (filedes[1]); #else - ctx->inbound.fd = filedes[0]; - ctx->outbound.fd = filedes[1]; + s = getenv ("_assuan_connection_fd"); + if (!filedes && s && *s && atoi (s) >= 0 ) + { + /* Well, we are called with an bi-directional file + descriptor. Prepare for using sendmsg/recvmsg. In this + case we ignore the passed file descriptors. */ + ctx->inbound.fd = ctx->outbound.fd = atoi (s); + _assuan_init_uds_io (ctx); + ctx->deinit_handler = _assuan_uds_deinit; + } + else if (filedes) + { + /* Standard pipe server. */ + ctx->inbound.fd = filedes[0]; + ctx->outbound.fd = filedes[1]; + } + else + { + _assuan_release_context (*r_ctx); + *r_ctx = NULL; + return ASSUAN_Problem_Starting_Server; + } #endif ctx->pipe_mode = 1; diff --git a/src/assuan-socket-server.c b/src/assuan-socket-server.c index aab1b5a..91adb71 100644 --- a/src/assuan-socket-server.c +++ b/src/assuan-socket-server.c @@ -34,6 +34,10 @@ #include "assuan-defs.h" +static struct assuan_io io = { _assuan_simple_read, + _assuan_simple_write }; + + static int accept_connection_bottom (assuan_context_t ctx) { @@ -105,9 +109,6 @@ deinit_socket_server (assuan_context_t ctx) finish_connection (ctx); } -static struct assuan_io io = { _assuan_simple_read, - _assuan_simple_write }; - /* Initialize a server for the socket LISTEN_FD which has already be put into listen mode */ int diff --git a/src/assuan-uds.c b/src/assuan-uds.c new file mode 100644 index 0000000..52988d4 --- /dev/null +++ b/src/assuan-uds.c @@ -0,0 +1,273 @@ +/* assuan-uds.c - Assuan unix domain socket utilities + * Copyright (C) 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 <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#ifndef HAVE_W32_SYSTEM +#include <sys/socket.h> +#include <sys/un.h> +#else +#include <windows.h> +#endif +#if HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> + +#include "assuan-defs.h" + + +/* Read from a unix domain socket using sendmsg. */ +static ssize_t +uds_reader (assuan_context_t ctx, void *buf, size_t buflen) +{ + int len = ctx->uds.buffersize; + +#ifndef HAVE_W32_SYSTEM + + if (!ctx->uds.bufferallocated) + { + ctx->uds.buffer = xtrymalloc (2048); + if (!ctx->uds.buffer) + return _assuan_error (ASSUAN_Out_Of_Core); + ctx->uds.bufferallocated = 2048; + } + + while (!len) /* No data is buffered. */ + { + struct msghdr msg; + struct iovec iovec; + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof (int))]; + } control_u; + struct cmsghdr *cmptr; + + memset (&msg, 0, sizeof (msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iovec; + msg.msg_iovlen = 1; + iovec.iov_base = ctx->uds.buffer; + iovec.iov_len = ctx->uds.bufferallocated; + msg.msg_control = control_u.control; + msg.msg_controllen = sizeof (control_u.control); + + len = _assuan_simple_recvmsg (ctx, &msg); + if (len < 0) + return -1; + + ctx->uds.buffersize = len; + ctx->uds.bufferoffset = 0; + + cmptr = CMSG_FIRSTHDR (&msg); + if (cmptr && cmptr->cmsg_len == CMSG_LEN (sizeof(int))) + { + if (cmptr->cmsg_level != SOL_SOCKET + || cmptr->cmsg_type != SCM_RIGHTS) + _assuan_log_printf ("unexpected ancillary data received\n"); + else + { + int fd = *((int*)CMSG_DATA (cmptr)); + + if (ctx->uds.pendingfdscount >= DIM (ctx->uds.pendingfds)) + { + _assuan_log_printf ("too many descriptors pending - " + "closing received descriptor %d\n", fd); + _assuan_close (fd); + } + else + ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd; + } + } + } +#else /*HAVE_W32_SYSTEM*/ + len = recvfrom (ctx->inbound.fd, buf, buflen, 0, NULL, NULL); +#endif /*HAVE_W32_SYSTEM*/ + + /* Return some data to the user. */ + + if (len > buflen) /* We have more than the user requested. */ + len = buflen; + + memcpy (buf, ctx->uds.buffer + ctx->uds.bufferoffset, len); + ctx->uds.buffersize -= len; + assert (ctx->uds.buffersize >= 0); + ctx->uds.bufferoffset += len; + assert (ctx->uds.bufferoffset <= ctx->uds.bufferallocated); + + return len; +} + + +/* Write to the domain server. */ +static ssize_t +uds_writer (assuan_context_t ctx, const void *buf, size_t buflen) +{ +#ifndef HAVE_W32_SYSTEM + struct msghdr msg; + struct iovec iovec; + ssize_t len; + + memset (&msg, 0, sizeof (msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iovlen = 1; + msg.msg_iov = &iovec; + iovec.iov_base = (void*)buf; + iovec.iov_len = buflen; + msg.msg_control = 0; + msg.msg_controllen = 0; + + len = _assuan_simple_sendmsg (ctx, &msg); +#else /*HAVE_W32_SYSTEM*/ + int len; + + len = sendto (ctx->outbound.fd, buf, buflen, 0, + (struct sockaddr *)&ctx->serveraddr, + sizeof (struct sockaddr_in)); +#endif /*HAVE_W32_SYSTEM*/ + return len; +} + + +static assuan_error_t +uds_sendfd (assuan_context_t ctx, int fd) +{ +#ifndef HAVE_W32_SYSTEM + struct msghdr msg; + struct iovec iovec; + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof (int))]; + } control_u; + struct cmsghdr *cmptr; + int len; + char buffer[80]; + + /* We need to send some real data so that a read won't return 0 + which will be taken as an EOF. It also helps with debugging. */ + snprintf (buffer, sizeof(buffer)-1, "# descriptor %d is in flight\n", fd); + buffer[sizeof(buffer)-1] = 0; + + memset (&msg, 0, sizeof (msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iovlen = 1; + msg.msg_iov = &iovec; + iovec.iov_base = buffer; + iovec.iov_len = strlen (buffer); + + msg.msg_control = control_u.control; + msg.msg_controllen = sizeof (control_u.control); + cmptr = CMSG_FIRSTHDR (&msg); + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + *((int*)CMSG_DATA (cmptr)) = fd; + + len = _assuan_simple_sendmsg (ctx, &msg); + if (len < 0) + { + _assuan_log_printf ("uds_sendfd: %s\n", strerror (errno)); + return _assuan_error (ASSUAN_Write_Error); + } + else + return 0; +#else + return _assuan_error (ASSUAN_Not_Implemented); +#endif +} + + +static assuan_error_t +uds_receivefd (assuan_context_t ctx, int *fd) +{ +#ifndef HAVE_W32_SYSTEM + if (!ctx->uds.pendingfds) + { + _assuan_log_printf ("no pending file descriptors!\n"); + return _assuan_error (ASSUAN_General_Error); + } + + *fd = ctx->uds.pendingfds[0]; + if (--ctx->uds.pendingfdscount) + memmove (ctx->uds.pendingfds, ctx->uds.pendingfds + 1, + ctx->uds.pendingfdscount * sizeof (int)); + + return 0; +#else + return _assuan_error (ASSUAN_Not_Implemented); +#endif +} + + +/* Deinitialize the unix domain socket I/O functions. */ +void +_assuan_uds_deinit (assuan_context_t ctx) +{ + int i; + + /* First call the finish_handler which should close descriptors etc. */ + ctx->finish_handler (ctx); + + if (ctx->uds.buffer) + { + assert (ctx->uds.bufferallocated); + ctx->uds.bufferallocated = 0; + xfree (ctx->uds.buffer); + } + + for (i = 0; i < ctx->uds.pendingfdscount; i++) + _assuan_close (ctx->uds.pendingfds[i]); + ctx->uds.pendingfdscount = 0; +} + + + +/* Helper function to initialize a context for domain I/O. */ +void +_assuan_init_uds_io (assuan_context_t ctx) +{ + static struct assuan_io io = { uds_reader, uds_writer, + uds_sendfd, uds_receivefd }; + + ctx->io = &io; + ctx->uds.buffer = 0; + ctx->uds.bufferoffset = 0; + ctx->uds.buffersize = 0; + ctx->uds.bufferallocated = 0; + ctx->uds.pendingfdscount = 0; +} + diff --git a/src/assuan.h b/src/assuan.h index b85e642..e16d821 100644 --- a/src/assuan.h +++ b/src/assuan.h @@ -347,7 +347,7 @@ assuan_error_t assuan_write_status (assuan_context_t ctx, file descriptor via CTX and stores it in *RDF (the CTX must be capable of passing file descriptors). */ assuan_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line, - int *rfd); + int *rfd); /*-- assuan-listen.c --*/ assuan_error_t assuan_set_hello_line (assuan_context_t ctx, const char *line); @@ -368,36 +368,27 @@ int assuan_init_connected_socket_server (assuan_context_t *r_ctx, int fd); /*-- assuan-pipe-connect.c --*/ -assuan_error_t assuan_pipe_connect (assuan_context_t *ctx, const char *name, +assuan_error_t assuan_pipe_connect (assuan_context_t *ctx, + const char *name, const char *const argv[], int *fd_child_list); -assuan_error_t assuan_pipe_connect2 (assuan_context_t *ctx, const char *name, +assuan_error_t assuan_pipe_connect2 (assuan_context_t *ctx, + const char *name, const char *const argv[], int *fd_child_list, void (*atfork) (void*, int), void *atforkvalue); +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 *, int), + void *atforkvalue); + /*-- assuan-socket-connect.c --*/ assuan_error_t assuan_socket_connect (assuan_context_t *ctx, const char *name, pid_t server_pid); -/*-- assuan-domain-connect.c --*/ - -/* Connect to a Unix domain socket server. RENDEZVOUSFD is - bidirectional file descriptor (normally returned via socketpair) - which the client can use to rendezvous with the server. SERVER s - the server's pid. */ -assuan_error_t assuan_domain_connect (assuan_context_t *r_ctx, - int rendezvousfd, - pid_t server); - -/*-- assuan-domain-server.c --*/ - -/* RENDEZVOUSFD is a bidirectional file descriptor (normally returned - via socketpair) that the domain server can use to rendezvous with - the client. CLIENT is the client's pid. */ -assuan_error_t assuan_init_domain_server (assuan_context_t *r_ctx, - int rendezvousfd, - pid_t client); /*-- assuan-connect.c --*/ diff --git a/tests/fdpassing.c b/tests/fdpassing.c index c2f4bf6..f62be62 100644 --- a/tests/fdpassing.c +++ b/tests/fdpassing.c @@ -45,23 +45,28 @@ cmd_echo (assuan_context_t ctx, char *line) int fd; int c; FILE *fp; + int nbytes; log_info ("got ECHO command (%s)\n", line); fd = assuan_get_input_fd (ctx); if (fd == -1) return ASSUAN_No_Input; - fp = fdopen (dup (fd), "r"); + fp = fdopen (fd, "r"); if (!fp) { log_error ("fdopen failed on input fd: %s\n", strerror (errno)); return ASSUAN_General_Error; } log_info ("printing input to stdout:\n"); + nbytes = 0; while ( (c=getc (fp)) != -1) - putc (c, stdout); + { + putc (c, stdout); + nbytes++; + } fflush (stdout); - log_info ("done printing input to stdout\n"); + log_info ("done printing %d bytes to stdout\n", nbytes); fclose (fp); return 0; @@ -93,22 +98,21 @@ register_commands (assuan_context_t ctx) static void -server (int fd) +server (void) { int rc; assuan_context_t ctx; - log_info ("server started on fd %d\n", fd); + log_info ("server started\n"); - rc = assuan_init_domain_server (&ctx, fd, (pid_t)(-1)); + rc = assuan_init_pipe_server (&ctx, NULL); if (rc) - log_fatal ("assuan_init_domain_server failed: %s\n", assuan_strerror (rc)); + log_fatal ("assuan_init_pipe_server failed: %s\n", assuan_strerror (rc)); rc = register_commands (ctx); if (rc) log_fatal ("register_commands failed: %s\n", assuan_strerror(rc)); - assuan_set_assuan_log_prefix (log_prefix); assuan_set_log_stream (ctx, stderr); for (;;) @@ -116,7 +120,8 @@ server (int fd) rc = assuan_accept (ctx); if (rc) { - log_error ("assuan_accept failed: %s\n", assuan_strerror (rc)); + if (rc != -1) + log_error ("assuan_accept failed: %s\n", assuan_strerror (rc)); break; } @@ -140,52 +145,50 @@ server (int fd) /* Client main. If true is returned, a disconnect has not been done. */ static int -client (int fd) +client (assuan_context_t ctx) { int rc; - assuan_context_t ctx; FILE *fp; int i; - log_info ("client started on fd %d\n", fd); - - rc = assuan_domain_connect (&ctx, fd, (pid_t)(-1)); - if (rc) - { - log_error ("assuan_domain_connect failed: %s\n", assuan_strerror (rc)); - return -1; - } + log_info ("client started\n"); - fp = fopen ("/etc/motd", "r"); - if (!fp) + for (i=0; i < 8; i++) { - log_error ("failed to open `%s': %s\n", "/etc/motd", strerror (errno)); - return -1; - } - - rc = assuan_sendfd (ctx, fileno (fp)); - if (rc) - { - log_error ("assuan_sendfd failed: %s\n", assuan_strerror (rc)); - return -1; - } - - rc = assuan_transact (ctx, "INPUT FD", NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - { - log_error ("sending INPUT FD failed: %s\n", assuan_strerror (rc)); - return -1; - } + fp = fopen ("/etc/motd", "r"); + if (!fp) + { + log_error ("failed to open `%s': %s\n", "/etc/motd", + strerror (errno)); + return -1; + } + + rc = assuan_sendfd (ctx, fileno (fp)); + if (rc) + { + log_error ("assuan_sendfd failed: %s\n", assuan_strerror (rc)); + return -1; + } + fclose (fp); + rc = assuan_transact (ctx, "INPUT FD", NULL, NULL, NULL, NULL, + NULL, NULL); + if (rc) + { + log_error ("sending INPUT FD failed: %s\n", assuan_strerror (rc)); + return -1; + } - rc = assuan_transact (ctx, "ECHO", NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - { - log_error ("sending ECHO failed: %s\n", assuan_strerror (rc)); - return -1; + rc = assuan_transact (ctx, "ECHO", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + { + log_error ("sending ECHO failed: %s\n", assuan_strerror (rc)); + return -1; + } } - sleep (100); + /* Give us some time to check with lsof that all descriptors are closed. */ +/* sleep (10); */ assuan_disconnect (ctx); return 0; @@ -204,8 +207,12 @@ main (int argc, char **argv) { int last_argc = -1; const char *srcdir = getenv ("srcdir"); - int fds[2]; - pid_t pid; + assuan_context_t ctx; + int err; + int no_close_fds[2]; + const char *arglist[10]; + int is_server = 0; + int with_exec = 0; if (!srcdir) srcdir = "."; @@ -223,7 +230,10 @@ main (int argc, char **argv) puts ( "usage: ./fdpassing [options]\n" "\n" -" Options are --verbose and --debug"); +"Options:\n" +" --verbose Show what is going on\n" +" --with-exec Exec the child. Default is just a fork\n" +); exit (0); } if (!strcmp (*argv, "--verbose")) @@ -236,29 +246,60 @@ main (int argc, char **argv) verbose = debug = 1; argc--; argv++; } + else if (!strcmp (*argv, "--server")) + { + is_server = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--with-exec")) + { + with_exec = 1; + argc--; argv++; + } } - /* Create a socketpair. */ - if ( socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) ) - log_fatal ("socketpair failed: %s\n", strerror (errno)); + assuan_set_assuan_log_prefix (log_prefix); + assuan_set_assuan_log_stream (stderr); - /* Fork and run server and client. */ - pid = fork (); - if (pid == (pid_t)(-1)) - log_fatal ("fork failed: %s\n", strerror (errno)); - if (!pid) + if (is_server) { - server (fds[0]); /* The child is our server. */ + server (); log_info ("server finished\n"); } else { - if (client (fds[1])) /* The parent is the client. */ + no_close_fds[0] = 2; + no_close_fds[1] = -1; + if (with_exec) + { + arglist[0] = "fdpassing"; + arglist[1] = "--server"; + arglist[2] = verbose? "--verbose":NULL; + arglist[3] = NULL; + } + err = assuan_pipe_connect_ext (&ctx, with_exec? "./fdpassing":NULL, + with_exec? arglist :NULL, + no_close_fds, NULL, NULL); + if (err) + { + log_error ("assuan_pipe_connect failed: %s\n",assuan_strerror (err)); + return 1; + } + + if (!ctx) + { + server (); + log_info ("server finished\n"); + } + else { - log_info ("waiting for server to terminate...\n"); - waitpid (pid, NULL, 0); + if (client (ctx)) + { + log_info ("waiting for server to terminate...\n"); + assuan_disconnect (ctx); + } + log_info ("client finished\n"); } - log_info ("client finished\n"); } return errorcount? 1:0; |