ostream.c revision d6193a892452ae87548f5745dada01f82816765d
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "lib.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "istream.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "ostream-private.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenvoid o_stream_set_name(struct ostream *stream, const char *name)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_free(stream->real_stream->iostream.name);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->real_stream->iostream.name = i_strdup(name);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenconst char *o_stream_get_name(struct ostream *stream)
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen while (stream->real_stream->iostream.name == NULL) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream = stream->real_stream->parent;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (stream == NULL)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return "";
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return stream->real_stream->iostream.name;
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenint o_stream_get_fd(struct ostream *stream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen return stream->real_stream->fd;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenconst char *o_stream_get_error(struct ostream *stream)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen struct ostream *s;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen /* we'll only return errors for streams that have stream_errno set.
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen we might be returning unintended error otherwise. */
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (stream->stream_errno == 0)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen return "<no error>";
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen for (s = stream; s != NULL; s = s->real_stream->parent) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (s->stream_errno == 0)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen break;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (s->real_stream->iostream.error != NULL)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return s->real_stream->iostream.error;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return strerror(stream->stream_errno);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstatic void o_stream_close_full(struct ostream *stream, bool close_parents)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen /* Ideally o_stream_finish() would be called for all non-failed
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen ostreams, but strictly requiring it would cause unnecessary
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen complexity for many callers. Just require that at this point
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen after flushing there isn't anything in the output buffer or that
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen we're ignoring all errors. */
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (o_stream_flush(stream) == 0)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_assert(stream->real_stream->error_handling_disabled);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (!stream->closed && !stream->real_stream->closing) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen /* first mark the stream as being closed so the
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen o_stream_copy_error_from_parent() won't recurse us back
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen here. but don't immediately mark the stream closed, because
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen we may still want to write something to it. */
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->real_stream->closing = TRUE;
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen io_stream_close(&stream->real_stream->iostream, close_parents);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->closed = TRUE;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (stream->stream_errno == 0)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->stream_errno = EPIPE;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenvoid o_stream_destroy(struct ostream **stream)
e245fb1302121d2bc2580f61e040c2c8a558ee9eTimo Sirainen{
e245fb1302121d2bc2580f61e040c2c8a558ee9eTimo Sirainen if (*stream == NULL)
e245fb1302121d2bc2580f61e040c2c8a558ee9eTimo Sirainen return;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen o_stream_close_full(*stream, FALSE);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen o_stream_unref(stream);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenvoid o_stream_ref(struct ostream *stream)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen io_stream_ref(&stream->real_stream->iostream);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenvoid o_stream_unref(struct ostream **_stream)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct ostream *stream;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (*_stream == NULL)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream = *_stream;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (stream->real_stream->last_errors_not_checked &&
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen !stream->real_stream->error_handling_disabled &&
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->real_stream->iostream.refcount == 1) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_panic("output stream %s is missing error handling",
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen o_stream_get_name(stream));
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (!io_stream_unref(&stream->real_stream->iostream))
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen io_stream_free(&stream->real_stream->iostream);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen *_stream = NULL;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#undef o_stream_add_destroy_callback
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenvoid o_stream_add_destroy_callback(struct ostream *stream,
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen ostream_callback_t *callback, void *context)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen io_stream_add_destroy_callback(&stream->real_stream->iostream,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen callback, context);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenvoid o_stream_remove_destroy_callback(struct ostream *stream,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen void (*callback)())
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen io_stream_remove_destroy_callback(&stream->real_stream->iostream,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen callback);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenvoid o_stream_close(struct ostream *stream)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen o_stream_close_full(stream, TRUE);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#undef o_stream_set_flush_callback
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenvoid o_stream_set_flush_callback(struct ostream *stream,
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen stream_flush_callback_t *callback,
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen void *context)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct ostream_private *_stream = stream->real_stream;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen _stream->set_flush_callback(_stream, callback, context);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenvoid o_stream_unset_flush_callback(struct ostream *stream)
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen{
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen struct ostream_private *_stream = stream->real_stream;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen _stream->set_flush_callback(_stream, NULL, NULL);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenvoid o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size);
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainensize_t o_stream_get_max_buffer_size(struct ostream *stream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return stream->real_stream->max_buffer_size;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenvoid o_stream_cork(struct ostream *stream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct ostream_private *_stream = stream->real_stream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0))
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen _stream->cork(_stream, TRUE);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenvoid o_stream_uncork(struct ostream *stream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct ostream_private *_stream = stream->real_stream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0))
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen _stream->cork(_stream, FALSE);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenbool o_stream_is_corked(struct ostream *stream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen struct ostream_private *_stream = stream->real_stream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return _stream->corked;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen}
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenint o_stream_flush(struct ostream *stream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct ostream_private *_stream = stream->real_stream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen int ret = 1;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen o_stream_ignore_last_errors(stream);
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0)) {
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen errno = stream->stream_errno;
46631c1d903c409444b1b1c4a1d41a033c09ee37Timo Sirainen return -1;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen if (unlikely(_stream->noverflow)) {
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen io_stream_set_error(&_stream->iostream,
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen "Output stream buffer was full (%"PRIuSIZE_T" bytes)",
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen o_stream_get_max_buffer_size(stream));
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen errno = stream->stream_errno = ENOBUFS;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen return -1;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen }
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen if (unlikely((ret = _stream->flush(_stream)) < 0)) {
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen i_assert(stream->stream_errno != 0);
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen errno = stream->stream_errno;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen }
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen return ret;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen}
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainenvoid o_stream_set_flush_pending(struct ostream *stream, bool set)
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen{
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen struct ostream_private *_stream = stream->real_stream;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0))
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen return;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen _stream->flush_pending(_stream, set);
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen}
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainensize_t o_stream_get_buffer_used_size(const struct ostream *stream)
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen{
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen const struct ostream_private *_stream = stream->real_stream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return _stream->get_used_size(_stream);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen}
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainensize_t o_stream_get_buffer_avail_size(const struct ostream *stream)
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen{
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen size_t used = o_stream_get_buffer_used_size(stream);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen return stream->real_stream->max_buffer_size <= used ? 0 :
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen stream->real_stream->max_buffer_size - used;
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen}
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainenint o_stream_seek(struct ostream *stream, uoff_t offset)
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen{
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen struct ostream_private *_stream = stream->real_stream;
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0)) {
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen errno = stream->stream_errno;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return -1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (unlikely(_stream->seek(_stream, offset) < 0)) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_assert(stream->stream_errno != 0);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen errno = stream->stream_errno;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return -1;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen }
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen return 1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenssize_t o_stream_send(struct ostream *stream, const void *data, size_t size)
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct const_iovec iov;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_zero(&iov);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen iov.iov_base = data;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen iov.iov_len = size;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen return o_stream_sendv(stream, &iov, 1);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen}
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainenstatic ssize_t
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Siraineno_stream_sendv_int(struct ostream *stream, const struct const_iovec *iov,
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen unsigned int iov_count, bool *overflow_r)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct ostream_private *_stream = stream->real_stream;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen unsigned int i;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen size_t total_size;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen ssize_t ret;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen *overflow_r = FALSE;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen for (i = 0, total_size = 0; i < iov_count; i++)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen total_size += iov[i].iov_len;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (total_size == 0)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return 0;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_assert(!_stream->finished);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen ret = _stream->sendv(_stream, iov, iov_count);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen if (unlikely(ret != (ssize_t)total_size)) {
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (ret < 0) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_assert(stream->stream_errno != 0);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen errno = stream->stream_errno;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen } else {
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen i_assert(!stream->blocking);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen stream->overflow = TRUE;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen *overflow_r = TRUE;
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return ret;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov,
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen unsigned int iov_count)
b3f4c31f1533e25380f49f77d5bb1251bf43db2aTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen bool overflow;
bace943c67e6cd14ce6c994f533d82a3caad5bf1Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0)) {
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen errno = stream->stream_errno;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen return -1;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
c096257fbdaf4b9fcf8eb97aae94afdbb4e71ed4Timo Sirainen return o_stream_sendv_int(stream, iov, iov_count, &overflow);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen}
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenssize_t o_stream_send_str(struct ostream *stream, const char *str)
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen{
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen return o_stream_send(stream, str, strlen(str));
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
bace943c67e6cd14ce6c994f533d82a3caad5bf1Timo Sirainenvoid o_stream_nsend(struct ostream *stream, const void *data, size_t size)
bace943c67e6cd14ce6c994f533d82a3caad5bf1Timo Sirainen{
c096257fbdaf4b9fcf8eb97aae94afdbb4e71ed4Timo Sirainen struct const_iovec iov;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen i_zero(&iov);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen iov.iov_base = data;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen iov.iov_len = size;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
c096257fbdaf4b9fcf8eb97aae94afdbb4e71ed4Timo Sirainen o_stream_nsendv(stream, &iov, 1);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen}
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenvoid o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen unsigned int iov_count)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen bool overflow;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0 ||
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->real_stream->noverflow))
return;
(void)o_stream_sendv_int(stream, iov, iov_count, &overflow);
if (overflow)
stream->real_stream->noverflow = TRUE;
stream->real_stream->last_errors_not_checked = TRUE;
}
void o_stream_nsend_str(struct ostream *stream, const char *str)
{
o_stream_nsend(stream, str, strlen(str));
}
int o_stream_finish(struct ostream *stream)
{
stream->real_stream->finished = TRUE;
return o_stream_flush(stream);
}
void o_stream_set_finish_also_parent(struct ostream *stream, bool set)
{
stream->real_stream->finish_also_parent = set;
}
void o_stream_set_finish_via_child(struct ostream *stream, bool set)
{
stream->real_stream->finish_via_child = set;
}
void o_stream_ignore_last_errors(struct ostream *stream)
{
while (stream != NULL) {
stream->real_stream->last_errors_not_checked = FALSE;
stream = stream->real_stream->parent;
}
}
void o_stream_set_no_error_handling(struct ostream *stream, bool set)
{
stream->real_stream->error_handling_disabled = set;
}
enum ostream_send_istream_result
o_stream_send_istream(struct ostream *outstream, struct istream *instream)
{
struct ostream_private *_outstream = outstream->real_stream;
uoff_t old_outstream_offset = outstream->offset;
uoff_t old_instream_offset = instream->v_offset;
enum ostream_send_istream_result res;
if (unlikely(instream->closed || instream->stream_errno != 0)) {
errno = instream->stream_errno;
return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT;
}
if (unlikely(outstream->closed || outstream->stream_errno != 0)) {
errno = outstream->stream_errno;
return OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
}
i_assert(!_outstream->finished);
res = _outstream->send_istream(_outstream, instream);
switch (res) {
case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
i_assert(instream->stream_errno == 0);
i_assert(outstream->stream_errno == 0);
i_assert(!i_stream_have_bytes_left(instream));
break;
case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
i_assert(!instream->blocking);
break;
case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
i_assert(!outstream->blocking);
o_stream_set_flush_pending(outstream, TRUE);
break;
case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
i_assert(instream->stream_errno != 0);
return res;
case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
i_assert(outstream->stream_errno != 0);
return res;
}
/* non-failure - make sure stream offsets match */
i_assert((outstream->offset - old_outstream_offset) ==
(instream->v_offset - old_instream_offset));
return res;
}
void o_stream_nsend_istream(struct ostream *outstream, struct istream *instream)
{
i_assert(instream->blocking);
switch (o_stream_send_istream(outstream, instream)) {
case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
break;
case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
i_unreached();
case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
outstream->real_stream->noverflow = TRUE;
break;
case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
outstream->stream_errno = instream->stream_errno;
io_stream_set_error(&outstream->real_stream->iostream,
"nsend-istream: read(%s) failed: %s",
i_stream_get_name(instream),
i_stream_get_error(instream));
break;
case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
break;
}
outstream->real_stream->last_errors_not_checked = TRUE;
}
int o_stream_pwrite(struct ostream *stream, const void *data, size_t size,
uoff_t offset)
{
int ret;
if (unlikely(stream->closed || stream->stream_errno != 0)) {
errno = stream->stream_errno;
return -1;
}
i_assert(!stream->real_stream->finished);
ret = stream->real_stream->write_at(stream->real_stream,
data, size, offset);
if (unlikely(ret < 0)) {
i_assert(stream->stream_errno != 0);
errno = stream->stream_errno;
}
return ret;
}
enum ostream_send_istream_result
io_stream_copy(struct ostream *outstream, struct istream *instream)
{
struct const_iovec iov;
const unsigned char *data;
ssize_t ret;
while (i_stream_read_more(instream, &data, &iov.iov_len) > 0) {
iov.iov_base = data;
if ((ret = o_stream_sendv(outstream, &iov, 1)) < 0)
return OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
else if (ret == 0)
return OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
i_stream_skip(instream, ret);
}
if (instream->stream_errno != 0)
return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT;
if (i_stream_have_bytes_left(instream))
return OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT;
return OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
}
void o_stream_switch_ioloop(struct ostream *stream)
{
struct ostream_private *_stream = stream->real_stream;
_stream->switch_ioloop(_stream);
}
static void o_stream_default_close(struct iostream_private *stream,
bool close_parent)
{
struct ostream_private *_stream = (struct ostream_private *)stream;
(void)o_stream_flush(&_stream->ostream);
if (close_parent && _stream->parent != NULL)
o_stream_close(_stream->parent);
}
static void o_stream_default_destroy(struct iostream_private *stream)
{
struct ostream_private *_stream = (struct ostream_private *)stream;
o_stream_unref(&_stream->parent);
}
static void
o_stream_default_set_max_buffer_size(struct iostream_private *stream,
size_t max_size)
{
struct ostream_private *_stream = (struct ostream_private *)stream;
if (_stream->parent != NULL)
o_stream_set_max_buffer_size(_stream->parent, max_size);
_stream->max_buffer_size = max_size;
}
static void o_stream_default_cork(struct ostream_private *_stream, bool set)
{
_stream->corked = set;
if (set) {
if (_stream->parent != NULL)
o_stream_cork(_stream->parent);
} else {
(void)o_stream_flush(&_stream->ostream);
_stream->last_errors_not_checked = TRUE;
if (_stream->parent != NULL)
o_stream_uncork(_stream->parent);
}
}
void o_stream_copy_error_from_parent(struct ostream_private *_stream)
{
struct ostream *src = _stream->parent;
struct ostream *dest = &_stream->ostream;
dest->stream_errno = src->stream_errno;
dest->overflow = src->overflow;
if (src->closed)
o_stream_close(dest);
}
int o_stream_flush_parent_if_needed(struct ostream_private *_stream)
{
if (o_stream_get_buffer_used_size(_stream->parent) >= IO_BLOCK_SIZE) {
/* we already have quite a lot of data in parent stream.
unless we can flush it, don't add any more to it or we
could keep wasting memory by just increasing the buffer
size all the time. */
if (o_stream_flush(_stream->parent) < 0) {
o_stream_copy_error_from_parent(_stream);
return -1;
}
if (o_stream_get_buffer_used_size(_stream->parent) >= IO_BLOCK_SIZE)
return 0;
}
return 1;
}
int o_stream_flush_parent(struct ostream_private *_stream)
{
int ret;
i_assert(_stream->parent != NULL);
if (!_stream->finished || !_stream->finish_also_parent ||
!_stream->parent->real_stream->finish_via_child)
ret = o_stream_flush(_stream->parent);
else
ret = o_stream_finish(_stream->parent);
if (ret < 0)
o_stream_copy_error_from_parent(_stream);
return ret;
}
static int o_stream_default_flush(struct ostream_private *_stream)
{
if (_stream->parent == NULL)
return 1;
return o_stream_flush_parent(_stream);
}
static void
o_stream_default_set_flush_callback(struct ostream_private *_stream,
stream_flush_callback_t *callback,
void *context)
{
if (_stream->parent != NULL)
o_stream_set_flush_callback(_stream->parent, callback, context);
_stream->callback = callback;
_stream->context = context;
}
static void
o_stream_default_set_flush_pending(struct ostream_private *_stream, bool set)
{
if (_stream->parent != NULL)
o_stream_set_flush_pending(_stream->parent, set);
}
static size_t
o_stream_default_get_used_size(const struct ostream_private *_stream)
{
if (_stream->parent == NULL)
return 0;
else
return o_stream_get_buffer_used_size(_stream->parent);
}
static int
o_stream_default_seek(struct ostream_private *_stream,
uoff_t offset ATTR_UNUSED)
{
_stream->ostream.stream_errno = ESPIPE;
return -1;
}
static ssize_t
o_stream_default_sendv(struct ostream_private *stream,
const struct const_iovec *iov, unsigned int iov_count)
{
ssize_t ret;
if ((ret = o_stream_sendv(stream->parent, iov, iov_count)) < 0) {
o_stream_copy_error_from_parent(stream);
return -1;
}
stream->ostream.offset += ret;
return ret;
}
static int
o_stream_default_write_at(struct ostream_private *_stream,
const void *data ATTR_UNUSED,
size_t size ATTR_UNUSED, uoff_t offset ATTR_UNUSED)
{
_stream->ostream.stream_errno = ESPIPE;
return -1;
}
static enum ostream_send_istream_result
o_stream_default_send_istream(struct ostream_private *outstream,
struct istream *instream)
{
return io_stream_copy(&outstream->ostream, instream);
}
static void o_stream_default_switch_ioloop(struct ostream_private *_stream)
{
if (_stream->parent != NULL)
o_stream_switch_ioloop(_stream->parent);
}
struct ostream *
o_stream_create(struct ostream_private *_stream, struct ostream *parent, int fd)
{
_stream->finish_also_parent = TRUE;
_stream->finish_via_child = TRUE;
_stream->fd = fd;
_stream->ostream.real_stream = _stream;
if (parent != NULL) {
_stream->ostream.blocking = parent->blocking;
_stream->parent = parent;
o_stream_ref(parent);
_stream->callback = parent->real_stream->callback;
_stream->context = parent->real_stream->context;
_stream->max_buffer_size = parent->real_stream->max_buffer_size;
_stream->error_handling_disabled =
parent->real_stream->error_handling_disabled;
}
if (_stream->iostream.close == NULL)
_stream->iostream.close = o_stream_default_close;
if (_stream->iostream.destroy == NULL)
_stream->iostream.destroy = o_stream_default_destroy;
if (_stream->iostream.set_max_buffer_size == NULL) {
_stream->iostream.set_max_buffer_size =
o_stream_default_set_max_buffer_size;
}
if (_stream->cork == NULL)
_stream->cork = o_stream_default_cork;
if (_stream->flush == NULL)
_stream->flush = o_stream_default_flush;
if (_stream->set_flush_callback == NULL) {
_stream->set_flush_callback =
o_stream_default_set_flush_callback;
}
if (_stream->flush_pending == NULL)
_stream->flush_pending = o_stream_default_set_flush_pending;
if (_stream->get_used_size == NULL)
_stream->get_used_size = o_stream_default_get_used_size;
if (_stream->seek == NULL)
_stream->seek = o_stream_default_seek;
if (_stream->sendv == NULL)
_stream->sendv = o_stream_default_sendv;
if (_stream->write_at == NULL)
_stream->write_at = o_stream_default_write_at;
if (_stream->send_istream == NULL)
_stream->send_istream = o_stream_default_send_istream;
if (_stream->switch_ioloop == NULL)
_stream->switch_ioloop = o_stream_default_switch_ioloop;
io_stream_init(&_stream->iostream);
return &_stream->ostream;
}
struct ostream *o_stream_create_error(int stream_errno)
{
struct ostream_private *stream;
struct ostream *output;
stream = i_new(struct ostream_private, 1);
stream->ostream.blocking = TRUE;
stream->ostream.closed = TRUE;
stream->ostream.stream_errno = stream_errno;
output = o_stream_create(stream, NULL, -1);
o_stream_set_no_error_handling(output, TRUE);
o_stream_set_name(output, "(error)");
return output;
}
struct ostream *
o_stream_create_error_str(int stream_errno, const char *fmt, ...)
{
struct ostream *output;
va_list args;
va_start(args, fmt);
output = o_stream_create_error(stream_errno);
io_stream_set_verror(&output->real_stream->iostream, fmt, args);
va_end(args);
return output;
}
struct ostream *o_stream_create_passthrough(struct ostream *output)
{
struct ostream_private *stream;
stream = i_new(struct ostream_private, 1);
return o_stream_create(stream, output, o_stream_get_fd(output));
}