bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "lib.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "ioloop.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "time-util.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "istream-private.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen#include "istream-timeout.h"
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct timeout_istream {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct istream_private istream;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeout *to;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeval last_read_timestamp;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen time_t created;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen unsigned int timeout_msecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool update_timestamp;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen};
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstatic void i_stream_timeout_close(struct iostream_private *stream,
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen bool close_parent)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen{
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek timeout_remove(&tstream->to);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen if (close_parent)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen i_stream_close(tstream->istream.parent);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen}
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
038c2831447440bf0bef89b43dd0968afc298abcStephan Boschstatic void i_stream_timeout_switch_ioloop_to(struct istream_private *stream,
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch struct ioloop *ioloop)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen{
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen if (tstream->to != NULL)
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch tstream->to = io_loop_move_timeout_to(ioloop, &tstream->to);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen}
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstatic void i_stream_timeout(struct timeout_istream *tstream)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen{
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch struct iostream_private *iostream = &tstream->istream.iostream;
9766afd8dc3976d238e1c71712d6bafa316f2e06Timo Sirainen unsigned int over_msecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen int diff;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen if (tstream->update_timestamp) {
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen /* we came here after a long-running code. timeouts are handled
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen before IOs, so wait for i_stream_read() to be called again
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen before assuming that we've timed out. */
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen return;
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen }
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen timeout_remove(&tstream->to);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen if (diff < (int)tstream->timeout_msecs) {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* we haven't reached the read timeout yet, update it */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen if (diff < 0)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen diff = 0;
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch tstream->to = timeout_add_to(io_stream_get_ioloop(iostream),
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch tstream->timeout_msecs - diff,
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch i_stream_timeout, tstream);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen return;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen }
9766afd8dc3976d238e1c71712d6bafa316f2e06Timo Sirainen over_msecs = diff - tstream->timeout_msecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen io_stream_set_error(&tstream->istream.iostream,
9766afd8dc3976d238e1c71712d6bafa316f2e06Timo Sirainen "Read timeout in %u.%03u s after %"PRIuUOFF_T" bytes%s",
9766afd8dc3976d238e1c71712d6bafa316f2e06Timo Sirainen diff/1000, diff%1000,
9766afd8dc3976d238e1c71712d6bafa316f2e06Timo Sirainen tstream->istream.istream.v_offset,
9766afd8dc3976d238e1c71712d6bafa316f2e06Timo Sirainen over_msecs < 1000 ? "" : t_strdup_printf(
a77d7cc71b6a1d66fa33516582890c2256516c26Timo Sirainen " (requested timeout in %u ms)", tstream->timeout_msecs));
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.istream.stream_errno = ETIMEDOUT;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen i_stream_set_input_pending(tstream->istream.parent, TRUE);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen}
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainenstatic void i_stream_timeout_set_pending(struct timeout_istream *tstream)
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen{
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen /* make sure we get called again on the next ioloop run. this updates
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen the timeout to the timestamp where we actually would have wanted to
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen start waiting for more data (so if there is long-running code
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen outside the ioloop it's not counted) */
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen tstream->update_timestamp = TRUE;
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen tstream->last_read_timestamp = ioloop_timeval;
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen i_stream_set_input_pending(&tstream->istream.istream, TRUE);
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen}
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstatic ssize_t
854b4074ac77a138b3983d72510b4d8779d15040Timo Siraineni_stream_timeout_read(struct istream_private *stream)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen{
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch struct iostream_private *iostream = &tstream->istream.iostream;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen ssize_t ret;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset +
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen stream->istream.v_offset);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen ret = i_stream_read_copy_from_parent(&stream->istream);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen if (ret < 0) {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* failed */
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen if (errno == ECONNRESET || errno == EPIPE) {
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen int diff = ioloop_time - tstream->created;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen io_stream_set_error(&tstream->istream.iostream,
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen "%s (opened %d secs ago)",
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen i_stream_get_error(stream->parent), diff);
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen }
8e0eb6a0cfc2cb66385bdf05a70a38d753bc1e24Timo Sirainen } else if (tstream->to == NULL && tstream->timeout_msecs > 0) {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* first read. add the timeout here instead of in init
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen in case the stream is created long before it's actually
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen read from. */
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch tstream->to = timeout_add_to(io_stream_get_ioloop(iostream),
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch tstream->timeout_msecs,
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch i_stream_timeout, tstream);
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen i_stream_timeout_set_pending(tstream);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen } else if (ret > 0 && tstream->to != NULL) {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen /* we read something, reset the timeout */
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen timeout_reset(tstream->to);
29cda2d6eb57b7fb65a55115ea8bcb6ab6938477Timo Sirainen i_stream_timeout_set_pending(tstream);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen } else if (tstream->update_timestamp) {
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->update_timestamp = FALSE;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->last_read_timestamp = ioloop_timeval;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen }
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen return ret;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen}
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainenstruct istream *
854b4074ac77a138b3983d72510b4d8779d15040Timo Siraineni_stream_create_timeout(struct istream *input, unsigned int timeout_msecs)
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen{
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen struct timeout_istream *tstream;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream = i_new(struct timeout_istream, 1);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->timeout_msecs = timeout_msecs;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.stream_size_passthrough = TRUE;
decdff03c32cb5d0e99d71c5678fd008714de70bTimo Sirainen tstream->created = ioloop_time;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.read = i_stream_timeout_read;
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch tstream->istream.switch_ioloop_to = i_stream_timeout_switch_ioloop_to;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.iostream.close = i_stream_timeout_close;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen
25fb397382c6f7d39bfeee85774e7675f02bfb3cTimo Sirainen tstream->istream.istream.readable_fd = input->readable_fd;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.istream.blocking = input->blocking;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen tstream->istream.istream.seekable = input->seekable;
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen return i_stream_create(&tstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
854b4074ac77a138b3983d72510b4d8779d15040Timo Sirainen}