Coverage Report

Created: 2026-07-30 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/iostream.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "array.h"
5
#include "istream.h"
6
#include "ostream.h"
7
#include "iostream-private.h"
8
9
struct iostream_fd *iostream_fd_init(int fd)
10
0
{
11
0
  struct iostream_fd *ref = i_new(struct iostream_fd, 1);
12
0
  ref->refcount = 1;
13
0
  ref->fd = fd;
14
0
  return ref;
15
0
}
16
17
void iostream_fd_ref(struct iostream_fd *ref)
18
0
{
19
0
  i_assert(ref->refcount > 0);
20
0
  ref->refcount++;
21
0
}
22
23
bool iostream_fd_unref(struct iostream_fd **_ref)
24
0
{
25
0
  struct iostream_fd *ref = *_ref;
26
27
0
  i_assert(ref != NULL);
28
0
  i_assert(ref->refcount > 0);
29
30
0
  *_ref = NULL;
31
32
0
  if (--ref->refcount > 0)
33
0
    return TRUE;
34
0
  i_free(ref);
35
0
  return FALSE;
36
0
}
37
38
void io_stream_create_fd_autoclose(int *fd, size_t max_in_buffer_size,
39
           size_t max_out_buffer_size,
40
           struct istream **input_r,
41
           struct ostream **output_r)
42
0
{
43
0
  struct iostream_fd *fd_ref = iostream_fd_init(*fd);
44
0
  *input_r = i_stream_create_fd_ref_autoclose(fd_ref, max_in_buffer_size);
45
0
  *output_r = o_stream_create_fd_ref_autoclose(fd_ref, max_out_buffer_size);
46
0
  iostream_fd_unref(&fd_ref);
47
0
  *fd = -1;
48
0
}
49
50
static void
51
io_stream_default_close(struct iostream_private *stream ATTR_UNUSED,
52
      bool close_parent ATTR_UNUSED)
53
0
{
54
0
}
55
56
static void
57
io_stream_default_destroy(struct iostream_private *stream ATTR_UNUSED)
58
0
{
59
0
}
60
61
void io_stream_init(struct iostream_private *stream)
62
8.63k
{
63
8.63k
  if (stream->close == NULL)
64
0
    stream->close = io_stream_default_close;
65
8.63k
  if (stream->destroy == NULL)
66
0
    stream->destroy = io_stream_default_destroy;
67
8.63k
  stream->ioloop = current_ioloop;
68
69
8.63k
  stream->refcount = 1;
70
8.63k
}
71
72
void io_stream_ref(struct iostream_private *stream)
73
0
{
74
0
  i_assert(stream->refcount > 0);
75
76
0
  stream->refcount++;
77
0
}
78
79
bool io_stream_unref(struct iostream_private *stream)
80
8.63k
{
81
8.63k
  i_assert(stream->refcount > 0);
82
8.63k
  if (--stream->refcount != 0)
83
0
    return TRUE;
84
85
8.63k
  stream->close(stream, FALSE);
86
8.63k
  stream->destroy(stream);
87
8.63k
  return FALSE;
88
8.63k
}
89
90
void io_stream_free(struct iostream_private *stream)
91
8.63k
{
92
8.63k
  const struct iostream_destroy_callback *dc;
93
94
8.63k
  if (array_is_created(&stream->destroy_callbacks)) {
95
0
    array_foreach(&stream->destroy_callbacks, dc)
96
0
      dc->callback(dc->context);
97
0
    array_free(&stream->destroy_callbacks);
98
0
  }
99
100
8.63k
        i_free(stream->error);
101
8.63k
        i_free(stream->name);
102
8.63k
        i_free(stream);
103
8.63k
}
104
105
void io_stream_close(struct iostream_private *stream, bool close_parent)
106
0
{
107
0
  stream->close(stream, close_parent);
108
0
}
109
110
void io_stream_set_max_buffer_size(struct iostream_private *stream,
111
           size_t max_size)
112
0
{
113
0
  stream->set_max_buffer_size(stream, max_size);
114
0
}
115
116
void io_stream_add_destroy_callback(struct iostream_private *stream,
117
            void (*callback)(void *), void *context)
118
0
{
119
0
  struct iostream_destroy_callback *dc;
120
121
0
  if (!array_is_created(&stream->destroy_callbacks))
122
0
    i_array_init(&stream->destroy_callbacks, 2);
123
0
  dc = array_append_space(&stream->destroy_callbacks);
124
0
  dc->callback = callback;
125
0
  dc->context = context;
126
0
}
127
128
void io_stream_remove_destroy_callback(struct iostream_private *stream,
129
               void (*callback)(void *))
130
0
{
131
0
  const struct iostream_destroy_callback *dcs;
132
0
  unsigned int i, count;
133
134
0
  dcs = array_get(&stream->destroy_callbacks, &count);
135
0
  for (i = 0; i < count; i++) {
136
0
    if (dcs[i].callback == callback) {
137
0
      array_delete(&stream->destroy_callbacks, i, 1);
138
0
      return;
139
0
    }
140
0
  }
141
0
  i_unreached();
142
0
}
143
144
void io_stream_set_error(struct iostream_private *stream,
145
       const char *fmt, ...)
146
0
{
147
0
  va_list args;
148
149
0
  va_start(args, fmt);
150
0
  io_stream_set_verror(stream, fmt, args);
151
0
  va_end(args);
152
0
}
153
154
void io_stream_set_verror(struct iostream_private *stream,
155
        const char *fmt, va_list args)
156
0
{
157
  /* one of the parameters may be the old stream->error, so don't free
158
     it before the new error is created. */
159
0
  char *error = i_strdup_vprintf(fmt, args);
160
0
  i_free(stream->error);
161
0
  stream->error = error;
162
0
}
163
164
const char *io_stream_get_disconnect_reason(struct istream *input,
165
              struct ostream *output)
166
0
{
167
0
  const char *errstr;
168
169
0
  if (input != NULL && input->stream_errno != 0) {
170
0
    errno = input->stream_errno;
171
0
    errstr = i_stream_get_error(input);
172
0
  } else if (output != NULL && output->stream_errno != 0) {
173
0
    errno = output->stream_errno;
174
0
    errstr = o_stream_get_error(output);
175
0
  } else {
176
0
    errno = 0;
177
0
    errstr = "";
178
0
  }
179
180
0
  if (errno == 0 || errno == EPIPE)
181
0
    return "Connection closed";
182
0
  else
183
0
    return t_strdup_printf("Connection closed: %s", errstr);
184
0
}
185
186
void io_stream_switch_ioloop_to(struct iostream_private *stream,
187
        struct ioloop *ioloop)
188
0
{
189
0
  stream->ioloop = ioloop;
190
0
}
191
192
struct ioloop *io_stream_get_ioloop(struct iostream_private *stream)
193
0
{
194
0
  return (stream->ioloop == NULL ? current_ioloop : stream->ioloop);
195
0
}