ostream.c revision 981139bb2e446bb2050c1158614725f8413fd709
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "istream.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "ostream-private.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
c5794838af9995f50bfecb06a3cd4f9a0ac77858Timo Sirainenvoid o_stream_set_name(struct ostream *stream, const char *name)
c5794838af9995f50bfecb06a3cd4f9a0ac77858Timo Sirainen{
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen i_free(stream->real_stream->iostream.name);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen stream->real_stream->iostream.name = i_strdup(name);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen}
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenconst char *o_stream_get_name(struct ostream *stream)
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen{
81a5d8714c566ce50c4a2409f59e82e716d576d2Timo Sirainen while (stream->real_stream->iostream.name == NULL) {
81a5d8714c566ce50c4a2409f59e82e716d576d2Timo Sirainen stream = stream->real_stream->parent;
81a5d8714c566ce50c4a2409f59e82e716d576d2Timo Sirainen if (stream == NULL)
81a5d8714c566ce50c4a2409f59e82e716d576d2Timo Sirainen return "";
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen }
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen return stream->real_stream->iostream.name;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenint o_stream_get_fd(struct ostream *stream)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return stream->real_stream->fd;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainenconst char *o_stream_get_error(struct ostream *stream)
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen{
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen struct ostream *s;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen /* we'll only return errors for streams that have stream_errno set.
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen we might be returning unintended error otherwise. */
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen if (stream->stream_errno == 0)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return "<no error>";
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (s = stream; s != NULL; s = s->real_stream->parent) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (s->stream_errno == 0)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen break;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (s->real_stream->iostream.error != NULL)
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen return s->real_stream->iostream.error;
cf93189100a9136f2fd508c26e1573410bc8c1aaTimo Sirainen }
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen return strerror(stream->stream_errno);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenstatic void o_stream_close_full(struct ostream *stream, bool close_parents)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen{
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen if (!stream->closed && !stream->real_stream->closing) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* first mark the stream as being closed so the
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen o_stream_copy_error_from_parent() won't recurse us back
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen here. but don't immediately mark the stream closed, because
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen we may still want to write something to it. */
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen stream->real_stream->closing = TRUE;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen io_stream_close(&stream->real_stream->iostream, close_parents);
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen stream->closed = TRUE;
81a5d8714c566ce50c4a2409f59e82e716d576d2Timo Sirainen }
c5794838af9995f50bfecb06a3cd4f9a0ac77858Timo Sirainen
c5794838af9995f50bfecb06a3cd4f9a0ac77858Timo Sirainen if (stream->stream_errno != 0)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen i_assert(stream->last_failed_errno != 0);
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen else {
893e5bbd5184ec5c21f47c67c8ea6efbea41f7d0Timo Sirainen stream->stream_errno = EPIPE;
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen stream->last_failed_errno = EPIPE;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen }
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen}
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenvoid o_stream_destroy(struct ostream **stream)
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen{
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen o_stream_close_full(*stream, FALSE);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen o_stream_unref(stream);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen}
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenvoid o_stream_ref(struct ostream *stream)
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen{
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen io_stream_ref(&stream->real_stream->iostream);
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainenvoid o_stream_unref(struct ostream **_stream)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
f153a2cec0319f549388d28f8cfd4d50229d1132Timo Sirainen struct ostream *stream = *_stream;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen if (stream->real_stream->last_errors_not_checked &&
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen !stream->real_stream->error_handling_disabled &&
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen stream->real_stream->iostream.refcount == 1) {
a022088674a5ae72ed29ae001834cbad62a4f19fTimo Sirainen i_panic("output stream %s is missing error handling",
a022088674a5ae72ed29ae001834cbad62a4f19fTimo Sirainen o_stream_get_name(stream));
a022088674a5ae72ed29ae001834cbad62a4f19fTimo Sirainen }
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainen
a2cbf1d392ee983520451bc9b849a490f28ac298Timo Sirainen io_stream_unref(&stream->real_stream->iostream);
a2cbf1d392ee983520451bc9b849a490f28ac298Timo Sirainen *_stream = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen#undef o_stream_add_destroy_callback
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainenvoid o_stream_add_destroy_callback(struct ostream *stream,
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen ostream_callback_t *callback, void *context)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen io_stream_add_destroy_callback(&stream->real_stream->iostream,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen callback, context);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenvoid o_stream_remove_destroy_callback(struct ostream *stream,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen void (*callback)())
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen io_stream_remove_destroy_callback(&stream->real_stream->iostream,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen callback);
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen}
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainenvoid o_stream_close(struct ostream *stream)
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen{
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen o_stream_close_full(stream, TRUE);
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen}
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen#undef o_stream_set_flush_callback
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainenvoid o_stream_set_flush_callback(struct ostream *stream,
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen stream_flush_callback_t *callback,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen void *context)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen{
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen struct ostream_private *_stream = stream->real_stream;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen _stream->set_flush_callback(_stream, callback, context);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen}
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainenvoid o_stream_unset_flush_callback(struct ostream *stream)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen{
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen struct ostream_private *_stream = stream->real_stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
_stream->set_flush_callback(_stream, NULL, NULL);
}
void o_stream_set_max_buffer_size(struct ostream *stream, size_t max_size)
{
io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size);
}
size_t o_stream_get_max_buffer_size(struct ostream *stream)
{
return stream->real_stream->max_buffer_size;
}
void o_stream_cork(struct ostream *stream)
{
struct ostream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
_stream->cork(_stream, TRUE);
}
void o_stream_uncork(struct ostream *stream)
{
struct ostream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
_stream->cork(_stream, FALSE);
if (stream->stream_errno != 0)
errno = stream->last_failed_errno = stream->stream_errno;
}
bool o_stream_is_corked(struct ostream *stream)
{
struct ostream_private *_stream = stream->real_stream;
return _stream->corked;
}
int o_stream_flush(struct ostream *stream)
{
struct ostream_private *_stream = stream->real_stream;
int ret = 1;
if (unlikely(stream->closed || stream->stream_errno != 0)) {
errno = stream->stream_errno;
return -1;
}
if (unlikely((ret = _stream->flush(_stream)) < 0)) {
i_assert(stream->stream_errno != 0);
stream->last_failed_errno = stream->stream_errno;
errno = stream->stream_errno;
}
return ret;
}
void o_stream_set_flush_pending(struct ostream *stream, bool set)
{
struct ostream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
_stream->flush_pending(_stream, set);
}
size_t o_stream_get_buffer_used_size(const struct ostream *stream)
{
const struct ostream_private *_stream = stream->real_stream;
return _stream->get_used_size(_stream);
}
size_t o_stream_get_buffer_avail_size(const struct ostream *stream)
{
size_t used = o_stream_get_buffer_used_size(stream);
return stream->real_stream->max_buffer_size <= used ? 0 :
stream->real_stream->max_buffer_size - used;
}
int o_stream_seek(struct ostream *stream, uoff_t offset)
{
struct ostream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0)) {
errno = stream->stream_errno;
return -1;
}
if (unlikely(_stream->seek(_stream, offset) < 0)) {
i_assert(stream->stream_errno != 0);
stream->last_failed_errno = stream->stream_errno;
errno = stream->stream_errno;
return -1;
}
return 1;
}
ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size)
{
struct const_iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = data;
iov.iov_len = size;
return o_stream_sendv(stream, &iov, 1);
}
ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov,
unsigned int iov_count)
{
struct ostream_private *_stream = stream->real_stream;
unsigned int i;
size_t total_size;
ssize_t ret;
if (unlikely(stream->closed || stream->stream_errno != 0)) {
errno = stream->stream_errno;
return -1;
}
for (i = 0, total_size = 0; i < iov_count; i++)
total_size += iov[i].iov_len;
if (total_size == 0)
return 0;
ret = _stream->sendv(_stream, iov, iov_count);
if (unlikely(ret != (ssize_t)total_size)) {
if (ret < 0) {
i_assert(stream->stream_errno != 0);
stream->last_failed_errno = stream->stream_errno;
errno = stream->stream_errno;
} else {
stream->overflow = TRUE;
}
}
return ret;
}
ssize_t o_stream_send_str(struct ostream *stream, const char *str)
{
return o_stream_send(stream, str, strlen(str));
}
void o_stream_nsend(struct ostream *stream, const void *data, size_t size)
{
struct const_iovec iov;
memset(&iov, 0, sizeof(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)
{
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
(void)o_stream_sendv(stream, iov, iov_count);
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);
errno = stream->last_failed_errno;
return stream->last_failed_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;
}
off_t o_stream_send_istream(struct ostream *outstream,
struct istream *instream)
{
struct ostream_private *_outstream = outstream->real_stream;
off_t ret;
if (unlikely(outstream->closed || instream->closed ||
outstream->stream_errno != 0)) {
errno = outstream->stream_errno;
return -1;
}
ret = _outstream->send_istream(_outstream, instream);
if (unlikely(ret < 0)) {
if (outstream->stream_errno != 0) {
outstream->last_failed_errno = outstream->stream_errno;
errno = outstream->stream_errno;
} else {
i_assert(instream->stream_errno != 0);
}
}
return ret;
}
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);
stream->last_failed_errno = stream->stream_errno;
errno = stream->stream_errno;
}
return ret;
}
off_t io_stream_copy(struct ostream *outstream, struct istream *instream)
{
uoff_t start_offset;
struct const_iovec iov;
const unsigned char *data;
ssize_t ret;
start_offset = instream->v_offset;
do {
(void)i_stream_read_data(instream, &data, &iov.iov_len, 0);
if (iov.iov_len == 0) {
/* all sent */
if (instream->stream_errno != 0)
return -1;
break;
}
iov.iov_base = data;
ret = o_stream_sendv(outstream, &iov, 1);
if (ret <= 0) {
if (ret == 0)
break;
return -1;
}
i_stream_skip(instream, ret);
} while ((size_t)ret == iov.iov_len);
return (off_t)(instream->v_offset - start_offset);
}
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->last_failed_errno = src->last_failed_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;
_stream->ostream.last_failed_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;
_stream->ostream.last_failed_errno = ESPIPE;
return -1;
}
static off_t 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->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.closed = TRUE;
stream->ostream.stream_errno = stream_errno;
stream->ostream.last_failed_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));
}