ostream.c revision b3fc5293379feb3640b23622bcc8f5f8d7f1e81d
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi#include "lib.h"
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi#include "istream.h"
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi#include "ostream-private.h"
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_set_name(struct ostream *stream, const char *name)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_free(stream->real_stream->iostream.name);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream->real_stream->iostream.name = i_strdup(name);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomiconst char *o_stream_get_name(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi while (stream->real_stream->iostream.name == NULL) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream = stream->real_stream->parent;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (stream == NULL)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return "";
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return stream->real_stream->iostream.name;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomiint o_stream_get_fd(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return stream->real_stream->fd;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomiconst char *o_stream_get_error(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream *s;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi /* we'll only return errors for streams that have stream_errno set.
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi we might be returning unintended error otherwise. */
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (stream->stream_errno == 0)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return "<no error>";
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi for (s = stream; s != NULL; s = s->real_stream->parent) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (s->stream_errno == 0)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi break;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (s->real_stream->iostream.error != NULL)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return s->real_stream->iostream.error;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return strerror(stream->stream_errno);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomistatic void o_stream_close_full(struct ostream *stream, bool close_parents)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (!stream->closed && !stream->real_stream->closing) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi /* first mark the stream as being closed so the
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi o_stream_copy_error_from_parent() won't recurse us back
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi here. but don't immediately mark the stream closed, because
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi we may still want to write something to it. */
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream->real_stream->closing = TRUE;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi io_stream_close(&stream->real_stream->iostream, close_parents);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream->closed = TRUE;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (stream->stream_errno == 0)
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen stream->stream_errno = EPIPE;
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen}
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainenvoid o_stream_destroy(struct ostream **stream)
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen{
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen if (*stream == NULL)
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen return;
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen
45ba3f8b55734aa2c96b6fc510919565ba70038cTimo Sirainen o_stream_close_full(*stream, FALSE);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi o_stream_unref(stream);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_ref(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
f01fe9df4240c5b15d0b1c4b485a38f7c5736781Timo Sirainen io_stream_ref(&stream->real_stream->iostream);
f01fe9df4240c5b15d0b1c4b485a38f7c5736781Timo Sirainen}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_unref(struct ostream **_stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream *stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (*_stream == NULL)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream = *_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
b78f4649c236bf4746c81c1d0e877675f0842ac8Timo Sirainen if (stream->real_stream->last_errors_not_checked &&
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi !stream->real_stream->error_handling_disabled &&
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream->real_stream->iostream.refcount == 1) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_panic("output stream %s is missing error handling",
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi o_stream_get_name(stream));
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (!io_stream_unref(&stream->real_stream->iostream))
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi io_stream_free(&stream->real_stream->iostream);
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen *_stream = NULL;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi#undef o_stream_add_destroy_callback
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_add_destroy_callback(struct ostream *stream,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi ostream_callback_t *callback, void *context)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi io_stream_add_destroy_callback(&stream->real_stream->iostream,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi callback, context);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
c50acc5547b9d92784825a09e3de97db0a806f4eTimo Sirainen
c50acc5547b9d92784825a09e3de97db0a806f4eTimo Sirainenvoid o_stream_remove_destroy_callback(struct ostream *stream,
c50acc5547b9d92784825a09e3de97db0a806f4eTimo Sirainen void (*callback)())
f01fe9df4240c5b15d0b1c4b485a38f7c5736781Timo Sirainen{
c50acc5547b9d92784825a09e3de97db0a806f4eTimo Sirainen io_stream_remove_destroy_callback(&stream->real_stream->iostream,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi callback);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_close(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi o_stream_close_full(stream, TRUE);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi#undef o_stream_set_flush_callback
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_set_flush_callback(struct ostream *stream,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream_flush_callback_t *callback,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi void *context)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
f01fe9df4240c5b15d0b1c4b485a38f7c5736781Timo Sirainen _stream->set_flush_callback(_stream, callback, context);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_unset_flush_callback(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi _stream->set_flush_callback(_stream, NULL, NULL);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
f01fe9df4240c5b15d0b1c4b485a38f7c5736781Timo Sirainenvoid o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomisize_t o_stream_get_max_buffer_size(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return stream->real_stream->max_buffer_size;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_cork(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(stream->closed || stream->stream_errno != 0))
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi _stream->cork(_stream, TRUE);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_uncork(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(stream->closed || stream->stream_errno != 0))
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi _stream->cork(_stream, FALSE);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomibool o_stream_is_corked(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return _stream->corked;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomiint o_stream_flush(struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi int ret = 1;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(stream->closed || stream->stream_errno != 0)) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi errno = stream->stream_errno;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return -1;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely((ret = _stream->flush(_stream)) < 0)) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_assert(stream->stream_errno != 0);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi errno = stream->stream_errno;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return ret;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
f01fe9df4240c5b15d0b1c4b485a38f7c5736781Timo Sirainenvoid o_stream_set_flush_pending(struct ostream *stream, bool set)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
36aa1b351b8e9d02a93aa17e5de8a06ac5440084Timo Sirainen
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch if (unlikely(stream->closed || stream->stream_errno != 0))
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch return;
36aa1b351b8e9d02a93aa17e5de8a06ac5440084Timo Sirainen
36aa1b351b8e9d02a93aa17e5de8a06ac5440084Timo Sirainen _stream->flush_pending(_stream, set);
36aa1b351b8e9d02a93aa17e5de8a06ac5440084Timo Sirainen}
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch
36aa1b351b8e9d02a93aa17e5de8a06ac5440084Timo Sirainensize_t o_stream_get_buffer_used_size(const struct ostream *stream)
36aa1b351b8e9d02a93aa17e5de8a06ac5440084Timo Sirainen{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi const struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return _stream->get_used_size(_stream);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomisize_t o_stream_get_buffer_avail_size(const struct ostream *stream)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi size_t used = o_stream_get_buffer_used_size(stream);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return stream->real_stream->max_buffer_size <= used ? 0 :
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream->real_stream->max_buffer_size - used;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomiint o_stream_seek(struct ostream *stream, uoff_t offset)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(stream->closed || stream->stream_errno != 0)) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi errno = stream->stream_errno;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return -1;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(_stream->seek(_stream, offset) < 0)) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_assert(stream->stream_errno != 0);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi errno = stream->stream_errno;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return -1;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return 1;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomissize_t o_stream_send(struct ostream *stream, const void *data, size_t size)
54bd0fec0be357266e299466a582f3c9269884e9Timo Sirainen{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct const_iovec iov;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_zero(&iov);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi iov.iov_base = data;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi iov.iov_len = size;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return o_stream_sendv(stream, &iov, 1);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomistatic ssize_t
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomio_stream_sendv_int(struct ostream *stream, const struct const_iovec *iov,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi unsigned int iov_count, bool *overflow_r)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct ostream_private *_stream = stream->real_stream;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi unsigned int i;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi size_t total_size;
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch ssize_t ret;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi *overflow_r = FALSE;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi for (i = 0, total_size = 0; i < iov_count; i++)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi total_size += iov[i].iov_len;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (total_size == 0)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return 0;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi ret = _stream->sendv(_stream, iov, iov_count);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(ret != (ssize_t)total_size)) {
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen if (ret < 0) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_assert(stream->stream_errno != 0);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi errno = stream->stream_errno;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi } else {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi i_assert(!stream->blocking);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi stream->overflow = TRUE;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi *overflow_r = TRUE;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return ret;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomissize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov,
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi unsigned int iov_count)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi bool overflow;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi if (unlikely(stream->closed || stream->stream_errno != 0)) {
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi errno = stream->stream_errno;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return -1;
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi }
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return o_stream_sendv_int(stream, iov, iov_count, &overflow);
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomissize_t o_stream_send_str(struct ostream *stream, const char *str)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi return o_stream_send(stream, str, strlen(str));
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi}
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomivoid o_stream_nsend(struct ostream *stream, const void *data, size_t size)
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi{
1de7b73a11afda43323410a4481d866930b1e632Aki Tuomi struct const_iovec iov;
i_zero(&iov);
iov.iov_base = data;
iov.iov_len = size;
o_stream_nsendv(stream, &iov, 1);
}
void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov,
unsigned int iov_count)
{
bool overflow;
if (unlikely(stream->closed || stream->stream_errno != 0 ||
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));
}
void o_stream_nflush(struct ostream *stream)
{
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
(void)o_stream_flush(stream);
stream->real_stream->last_errors_not_checked = TRUE;
}
int o_stream_nfinish(struct ostream *stream)
{
o_stream_nflush(stream);
o_stream_ignore_last_errors(stream);
if (stream->stream_errno == 0 && stream->real_stream->noverflow) {
io_stream_set_error(&stream->real_stream->iostream,
"Output stream buffer was full (%"PRIuSIZE_T" bytes)",
o_stream_get_max_buffer_size(stream));
stream->stream_errno = ENOBUFS;
}
return stream->stream_errno != 0 ? -1 : 0;
}
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;
}
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;
}
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;
if (_stream->parent != NULL)
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);
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;
}
static int o_stream_default_flush(struct ostream_private *_stream)
{
int ret;
if (_stream->parent == NULL)
return 1;
if ((ret = o_stream_flush(_stream->parent)) < 0)
o_stream_copy_error_from_parent(_stream);
return ret;
}
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->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));
}