#include "ostream.h"
#include "iostream-private.h"
+struct iostream_fd *iostream_fd_init(int fd)
+{
+ struct iostream_fd *ref = i_new(struct iostream_fd, 1);
+ ref->refcount = 1;
+ ref->fd = fd;
+ return ref;
+}
+
+void iostream_fd_ref(struct iostream_fd *ref)
+{
+ i_assert(ref->refcount > 0);
+ ref->refcount++;
+}
+
+bool iostream_fd_unref(struct iostream_fd **_ref)
+{
+ struct iostream_fd *ref = *_ref;
+
+ i_assert(ref != NULL);
+ i_assert(ref->refcount > 0);
+
+ if (--ref->refcount > 0)
+ return TRUE;
+ i_free(ref);
+ return FALSE;
+}
+
+void io_stream_create_fd_autoclose(int *fd, size_t max_in_buffer_size,
+ size_t max_out_buffer_size,
+ struct istream **input_r,
+ struct ostream **output_r)
+{
+ struct iostream_fd *fd_ref = iostream_fd_init(*fd);
+ *input_r = i_stream_create_fd_ref_autoclose(fd_ref, max_in_buffer_size);
+ *output_r = o_stream_create_fd_ref_autoclose(fd_ref, max_out_buffer_size);
+ iostream_fd_unref(&fd_ref);
+ *fd = -1;
+}
+
static void
io_stream_default_close(struct iostream_private *stream ATTR_UNUSED,
bool close_parent ATTR_UNUSED)
#ifndef IOSTREAM_H
#define IOSTREAM_H
+struct iostream_fd {
+ int refcount;
+ int fd;
+};
+
+/* Used to allow autoclosing fds with istream-file and ostream-file without
+ requiring them to be closed in any specific order. */
+struct iostream_fd *iostream_fd_init(int fd);
+void iostream_fd_ref(struct iostream_fd *ref);
+bool iostream_fd_unref(struct iostream_fd **ref);
+
+/* Create i/ostreams for the given fd. The fd is set to -1 immediately to avoid
+ accidentally closing it twice. */
+void io_stream_create_fd_autoclose(int *fd, size_t max_in_buffer_size,
+ size_t max_out_buffer_size,
+ struct istream **input_r,
+ struct ostream **output_r);
+
/* Returns human-readable reason for why iostream was disconnected.
The output is either "Connection closed" for clean disconnections or
"Connection closed: <error>" for unclean disconnections. */
struct file_istream {
struct istream_private istream;
+ struct iostream_fd *fd_ref;
uoff_t skip_left;
bool file:1;
struct file_istream *fstream =
container_of(_stream, struct file_istream, istream);
- if (fstream->autoclose_fd && _stream->fd != -1) {
+ bool refs_left = fstream->fd_ref != NULL &&
+ iostream_fd_unref(&fstream->fd_ref);
+ if (fstream->autoclose_fd && _stream->fd != -1 && !refs_left) {
/* Ignore ECONNRESET because we don't really care about it here,
as we are closing the socket down in any case. There might be
unsent data but nothing we can do about that. */
return input;
}
+struct istream *i_stream_create_fd_ref_autoclose(struct iostream_fd *ref,
+ size_t max_buffer_size)
+{
+ struct file_istream *fstream;
+
+ fstream = i_new(struct file_istream, 1);
+ fstream->fd_ref = ref;
+ iostream_fd_ref(ref);
+ return i_stream_create_file_common(fstream, ref->fd, NULL,
+ max_buffer_size, TRUE);
+}
+
struct istream *i_stream_create_file(const char *path, size_t max_buffer_size)
{
struct file_istream *fstream;
#include <sys/stat.h>
struct ioloop;
+struct iostream_fd;
struct istream {
uoff_t v_offset;
struct istream *i_stream_create_fd(int fd, size_t max_buffer_size);
/* The fd is set to -1 immediately to avoid accidentally closing it twice. */
struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size);
+/* Autoclose the fd once ref's refcount drops to 0. This function increases the
+ refcount, so the caller is expected to unref it as well. */
+struct istream *i_stream_create_fd_ref_autoclose(struct iostream_fd *ref,
+ size_t max_buffer_size);
/* Open the given path only when something is actually tried to be read from
the stream. */
struct istream *i_stream_create_file(const char *path, size_t max_buffer_size);
unsigned int iov_count, const char **error_r);
int fd;
+ struct iostream_fd *fd_ref;
struct io *io;
uoff_t buffer_offset;
uoff_t real_offset;
((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
static void stream_send_io(struct file_ostream *fstream);
-static struct ostream * o_stream_create_fd_common(int fd,
- size_t max_buffer_size, bool autoclose_fd);
static void stream_closed(struct file_ostream *fstream)
{
io_remove(&fstream->io);
- if (fstream->autoclose_fd && fstream->fd != -1) {
+ bool refs_left = fstream->fd_ref != NULL &&
+ iostream_fd_unref(&fstream->fd_ref);
+ if (fstream->autoclose_fd && fstream->fd != -1 && !refs_left) {
/* Ignore ECONNRESET because we don't really care about it here,
as we are closing the socket down in any case. There might be
unsent data but nothing we can do about that. */
}
}
-static
-struct ostream * o_stream_create_fd_common(int fd, size_t max_buffer_size,
- bool autoclose_fd)
+static struct ostream *
+o_stream_create_fd_common(int fd, struct iostream_fd *ref,
+ size_t max_buffer_size, bool autoclose_fd)
{
struct file_ostream *fstream;
struct ostream *ostream;
off_t offset;
fstream = i_new(struct file_ostream, 1);
+ if (ref != NULL) {
+ fstream->fd_ref = ref;
+ iostream_fd_ref(ref);
+ }
ostream = o_stream_create_file_common
(fstream, fd, max_buffer_size, autoclose_fd);
struct ostream *
o_stream_create_fd(int fd, size_t max_buffer_size)
{
- return o_stream_create_fd_common(fd, max_buffer_size, FALSE);
+ return o_stream_create_fd_common(fd, NULL, max_buffer_size, FALSE);
}
struct ostream *
o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size)
{
- struct ostream *ostream = o_stream_create_fd_common(*fd,
+ struct ostream *ostream = o_stream_create_fd_common(*fd, NULL,
max_buffer_size, TRUE);
*fd = -1;
return ostream;
}
+struct ostream *o_stream_create_fd_ref_autoclose(struct iostream_fd *ref,
+ size_t max_buffer_size)
+{
+ return o_stream_create_fd_common(ref->fd, ref, max_buffer_size, TRUE);
+}
+
struct ostream *
o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd)
{
#include "ioloop.h"
+struct iostream_fd;
+
enum ostream_send_istream_result {
/* All of the istream was successfully sent to ostream. */
OSTREAM_SEND_ISTREAM_RESULT_FINISHED,
struct ostream *o_stream_create_fd(int fd, size_t max_buffer_size);
/* The fd is set to -1 immediately to avoid accidentally closing it twice. */
struct ostream *o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size);
+/* Autoclose the fd once ref's refcount drops to 0. This function increases the
+ refcount, so the caller is expected to unref it as well. */
+struct ostream *o_stream_create_fd_ref_autoclose(struct iostream_fd *ref,
+ size_t max_buffer_size);
/* Create an output stream from a regular file which begins at given offset.
If offset==UOFF_T_MAX, the current offset isn't known. */
struct ostream *