Mercurial > dovecot > core-2.2
changeset 18481:21a2ce6f8f37
lib: Added istream-unix for reading fd sockets via istream.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 25 Apr 2015 11:23:00 +0300 |
parents | 1c275f718758 |
children | 25c848f10517 |
files | src/lib/Makefile.am src/lib/istream-file-private.h src/lib/istream-file.c src/lib/istream-unix.c src/lib/istream-unix.h src/lib/test-istream-unix.c src/lib/test-lib.c src/lib/test-lib.h |
diffstat | 8 files changed, 355 insertions(+), 20 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/Makefile.am Sat Apr 25 11:22:39 2015 +0300 +++ b/src/lib/Makefile.am Sat Apr 25 11:23:00 2015 +0300 @@ -74,6 +74,7 @@ istream-sized.c \ istream-tee.c \ istream-timeout.c \ + istream-unix.c \ ioloop.c \ ioloop-iolist.c \ ioloop-notify-none.c \ @@ -200,6 +201,7 @@ istream-chain.h \ istream-concat.h \ istream-crlf.h \ + istream-file-private.h \ istream-hash.h \ istream-jsonstr.h \ istream-private.h \ @@ -208,6 +210,7 @@ istream-sized.h \ istream-tee.h \ istream-timeout.h \ + istream-unix.h \ ioloop.h \ ioloop-iolist.h \ ioloop-private.h \ @@ -302,6 +305,7 @@ test-istream-crlf.c \ test-istream-seekable.c \ test-istream-tee.c \ + test-istream-unix.c \ test-json-parser.c \ test-json-tree.c \ test-llist.c \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/istream-file-private.h Sat Apr 25 11:23:00 2015 +0300 @@ -0,0 +1,23 @@ +#ifndef ISTREAM_FILE_PRIVATE_H +#define ISTREAM_FILE_PRIVATE_H + +#include "istream-private.h" + +struct file_istream { + struct istream_private istream; + + uoff_t skip_left; + + unsigned int file:1; + unsigned int autoclose_fd:1; + unsigned int seen_eof:1; +}; + +struct istream * +i_stream_create_file_common(struct file_istream *fstream, + int fd, const char *path, + size_t max_buffer_size, bool autoclose_fd); +ssize_t i_stream_file_read(struct istream_private *stream); +void i_stream_file_close(struct iostream_private *stream, bool close_parent); + +#endif
--- a/src/lib/istream-file.c Sat Apr 25 11:22:39 2015 +0300 +++ b/src/lib/istream-file.c Sat Apr 25 11:23:00 2015 +0300 @@ -4,7 +4,7 @@ #include "lib.h" #include "ioloop.h" -#include "istream-private.h" +#include "istream-file-private.h" #include "net.h" #include <time.h> @@ -12,18 +12,8 @@ #include <fcntl.h> #include <sys/stat.h> -struct file_istream { - struct istream_private istream; - - uoff_t skip_left; - - unsigned int file:1; - unsigned int autoclose_fd:1; - unsigned int seen_eof:1; -}; - -static void i_stream_file_close(struct iostream_private *stream, - bool close_parent ATTR_UNUSED) +void i_stream_file_close(struct iostream_private *stream, + bool close_parent ATTR_UNUSED) { struct file_istream *fstream = (struct file_istream *)stream; struct istream_private *_stream = (struct istream_private *)stream; @@ -51,7 +41,7 @@ return 0; } -static ssize_t i_stream_file_read(struct istream_private *stream) +ssize_t i_stream_file_read(struct istream_private *stream) { struct file_istream *fstream = (struct file_istream *) stream; uoff_t offset; @@ -183,16 +173,15 @@ return 0; } -static struct istream * -i_stream_create_file_common(int fd, const char *path, +struct istream * +i_stream_create_file_common(struct file_istream *fstream, + int fd, const char *path, size_t max_buffer_size, bool autoclose_fd) { - struct file_istream *fstream; struct istream *input; struct stat st; bool is_file; - fstream = i_new(struct file_istream, 1); fstream->autoclose_fd = autoclose_fd; fstream->istream.iostream.close = i_stream_file_close; @@ -235,9 +224,13 @@ struct istream *i_stream_create_fd(int fd, size_t max_buffer_size, bool autoclose_fd) { + struct file_istream *fstream; + i_assert(fd != -1); - return i_stream_create_file_common(fd, NULL, max_buffer_size, autoclose_fd); + fstream = i_new(struct file_istream, 1); + return i_stream_create_file_common(fstream, fd, NULL, + max_buffer_size, autoclose_fd); } struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) @@ -251,9 +244,12 @@ struct istream *i_stream_create_file(const char *path, size_t max_buffer_size) { + struct file_istream *fstream; struct istream *input; - input = i_stream_create_file_common(-1, path, max_buffer_size, TRUE); + fstream = i_new(struct file_istream, 1); + input = i_stream_create_file_common(fstream, -1, path, + max_buffer_size, TRUE); i_stream_set_name(input, path); return input; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/istream-unix.c Sat Apr 25 11:23:00 2015 +0300 @@ -0,0 +1,105 @@ +/* Copyright (c) 2014 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "fdpass.h" +#include "istream-file-private.h" +#include "istream-unix.h" + +struct unix_istream { + struct file_istream fstream; + bool next_read_fd; + int read_fd; +}; + +static void +i_stream_unix_close(struct iostream_private *stream, bool close_parent) +{ + struct unix_istream *ustream = (struct unix_istream *)stream; + + if (ustream->read_fd != -1) + i_close_fd(&ustream->read_fd); + i_stream_file_close(stream, close_parent); +} + +static ssize_t i_stream_unix_read(struct istream_private *stream) +{ + struct unix_istream *ustream = (struct unix_istream *)stream; + size_t size; + ssize_t ret; + + if (!ustream->next_read_fd) + return i_stream_file_read(stream); + + i_assert(ustream->read_fd == -1); + i_assert(ustream->fstream.skip_left == 0); /* not supported here.. */ + if (!i_stream_try_alloc(stream, 1, &size)) + return -2; + + do { + ret = fd_read(stream->fd, + stream->w_buffer + stream->pos, size, + &ustream->read_fd); + } while (unlikely(ret < 0 && errno == EINTR && + stream->istream.blocking)); + if (ustream->read_fd != -1) + ustream->next_read_fd = FALSE; + + if (unlikely(ret < 0)) { + if (errno == EINTR || errno == EAGAIN) { + i_assert(!stream->istream.blocking); + return 0; + } else { + i_assert(errno != 0); + /* if we get EBADF for a valid fd, it means something's + really wrong and we'd better just crash. */ + i_assert(errno != EBADF); + stream->istream.stream_errno = errno; + return -1; + } + } + stream->pos += ret; + return ret; +} + +struct istream *i_stream_create_unix(int fd, size_t max_buffer_size) +{ + struct unix_istream *ustream; + struct istream *input; + + i_assert(fd != -1); + + ustream = i_new(struct unix_istream, 1); + ustream->read_fd = -1; + input = i_stream_create_file_common(&ustream->fstream, fd, NULL, + max_buffer_size, FALSE); + input->real_stream->iostream.close = i_stream_unix_close; + input->real_stream->read = i_stream_unix_read; + return input; +} + +void i_stream_unix_set_read_fd(struct istream *input) +{ + struct unix_istream *ustream = + (struct unix_istream *)input->real_stream; + + ustream->next_read_fd = TRUE; +} + +void i_stream_unix_unset_read_fd(struct istream *input) +{ + struct unix_istream *ustream = + (struct unix_istream *)input->real_stream; + + ustream->next_read_fd = FALSE; +} + +int i_stream_unix_get_read_fd(struct istream *input) +{ + struct unix_istream *ustream = + (struct unix_istream *)input->real_stream; + int fd; + + fd = ustream->read_fd; + ustream->read_fd = -1; + return fd; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/istream-unix.h Sat Apr 25 11:23:00 2015 +0300 @@ -0,0 +1,15 @@ +#ifndef ISTREAM_UNIX_H +#define ISTREAM_UNIX_H + +struct istream *i_stream_create_unix(int fd, size_t max_buffer_size); +/* Start trying to read a file descriptor from the UNIX socket. */ +void i_stream_unix_set_read_fd(struct istream *input); +/* Stop trying to read a file descriptor from the UNIX socket. */ +void i_stream_unix_unset_read_fd(struct istream *input); +/* Returns the fd that the last i_stream_read() received, or -1 if no fd + was received. This function must be called before + i_stream_unix_set_read_fd() is called again after successfully receiving + a file descriptor. */ +int i_stream_unix_get_read_fd(struct istream *input); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/test-istream-unix.c Sat Apr 25 11:23:00 2015 +0300 @@ -0,0 +1,190 @@ +/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "net.h" +#include "fd-set-nonblock.h" +#include "fdpass.h" +#include "istream.h" +#include "istream-unix.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +static int send_fd, send_fd2; + +static void write_one(int fd) +{ + if (write(fd, "1", 1) < 0) + i_fatal("write() failed: %m"); +} + +static void read_one(int fd) +{ + char buf; + + if (read(fd, &buf, 1) < 0) + i_fatal("read() failed: m"); +} + +static void +test_server_read_nofd(struct istream *input, unsigned int idx) +{ + const unsigned char *data; + size_t size; + + test_assert_idx(i_stream_read_data(input, &data, &size, 0) == 1, idx); + i_stream_skip(input, 1); + test_assert_idx(i_stream_unix_get_read_fd(input) == -1, idx); +} + +static void +test_server_read_fd(struct istream *input, int wanted_fd, unsigned int idx) +{ + struct stat st1, st2; + const unsigned char *data; + size_t size; + int recv_fd; + + test_assert_idx(i_stream_read_data(input, &data, &size, 0) == 1, idx); + i_stream_skip(input, 1); + test_assert_idx((recv_fd = i_stream_unix_get_read_fd(input)) != -1, idx); + if (recv_fd != -1) { + if (fstat(recv_fd, &st1) < 0 || fstat(wanted_fd, &st2) < 0) + i_fatal("fstat() failed: %m"); + test_assert_idx(st1.st_ino == st2.st_ino, idx); + i_close_fd(&recv_fd); + } +} + +static void test_istream_unix_server(int fd) +{ + struct istream *input; + const unsigned char *data; + size_t size; + + input = i_stream_create_unix(fd, 1024); + /* 1) simple read */ + test_server_read_nofd(input, 1); + write_one(fd); + + /* 2) fd was sent but we won't get it */ + test_server_read_nofd(input, 2); + /* we still shouldn't have the fd */ + fd_set_nonblock(fd, TRUE); + i_stream_unix_set_read_fd(input); + test_assert(i_stream_read_data(input, &data, &size, 0) == 0); + test_assert(i_stream_unix_get_read_fd(input) == -1); + fd_set_nonblock(fd, FALSE); + write_one(fd); + + /* 3) the previous fd should be lost now */ + test_server_read_nofd(input, 3); + write_one(fd); + + /* 4) we should get the fd now */ + test_server_read_fd(input, send_fd2, 4); + write_one(fd); + + /* 5) the previous fd shouldn't be returned anymore */ + i_stream_unix_set_read_fd(input); + test_server_read_nofd(input, 5); + write_one(fd); + + /* 6) with i_stream_unix_unset_read_fd() we shouldn't get fd anymore */ + i_stream_unix_unset_read_fd(input); + test_server_read_nofd(input, 6); + write_one(fd); + + /* 7-8) two fds were sent, but we'll get only the first one */ + i_stream_unix_set_read_fd(input); + test_server_read_fd(input, send_fd, 7); + test_server_read_nofd(input, 8); + write_one(fd); + + /* 9-10) two fds were sent, and we'll get them both */ + i_stream_unix_set_read_fd(input); + test_server_read_fd(input, send_fd, 9); + i_stream_unix_set_read_fd(input); + test_server_read_fd(input, send_fd2, 10); + write_one(fd); + + i_stream_destroy(&input); + i_close_fd(&fd); +} + +static void test_istream_unix_client(int fd) +{ + char buf; + + /* 1) */ + write_one(fd); + read_one(fd); + + /* 2) */ + if (fd_send(fd, send_fd, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 3) */ + write_one(fd); + read_one(fd); + + /* 4) */ + if (fd_send(fd, send_fd2, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 5) */ + write_one(fd); + read_one(fd); + + /* 6) */ + if (fd_send(fd, send_fd, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 7-8) */ + if (fd_send(fd, send_fd, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + if (fd_send(fd, send_fd2, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + /* 9-10) */ + if (fd_send(fd, send_fd, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + if (fd_send(fd, send_fd2, &buf, 1) < 0) + i_fatal("fd_send() failed: %m"); + read_one(fd); + + i_close_fd(&fd); +} + +void test_istream_unix(void) +{ + int fd[2]; + + test_begin("istream unix"); + if ((send_fd = open("/dev/null", O_RDONLY)) == -1) + i_fatal("open(/dev/null) failed: %m"); + if ((send_fd2 = open("/dev/stderr", O_WRONLY)) == -1) + i_fatal("open(/dev/stderr) failed: %m"); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) + i_fatal("socketpair() failed: %m"); + switch (fork()) { + case -1: + i_fatal("fork() failed: %m"); + case 0: + i_close_fd(&fd[0]); + test_istream_unix_client(fd[1]); + _exit(0); + default: + i_close_fd(&fd[1]); + test_istream_unix_server(fd[0]); + break; + } + i_close_fd(&send_fd); + i_close_fd(&send_fd2); + test_end(); +}
--- a/src/lib/test-lib.c Sat Apr 25 11:22:39 2015 +0300 +++ b/src/lib/test-lib.c Sat Apr 25 11:23:00 2015 +0300 @@ -27,6 +27,7 @@ test_istream_crlf, test_istream_seekable, test_istream_tee, + test_istream_unix, test_json_parser, test_json_tree, test_llist,
--- a/src/lib/test-lib.h Sat Apr 25 11:22:39 2015 +0300 +++ b/src/lib/test-lib.h Sat Apr 25 11:23:00 2015 +0300 @@ -28,6 +28,7 @@ void test_istream_crlf(void); void test_istream_seekable(void); void test_istream_tee(void); +void test_istream_unix(void); void test_json_parser(void); void test_json_tree(void); void test_llist(void);