xref: /openbsd-src/usr.sbin/smtpd/ioev.c (revision 04fee6848cfe827855aa4ecd7f0a9da7d173e4d1)
1*04fee684Stb /*	$OpenBSD: ioev.c,v 1.49 2023/02/08 08:20:54 tb Exp $	*/
2d1ece852Seric /*
3d1ece852Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4d1ece852Seric  *
5d1ece852Seric  * Permission to use, copy, modify, and distribute this software for any
6d1ece852Seric  * purpose with or without fee is hereby granted, provided that the above
7d1ece852Seric  * copyright notice and this permission notice appear in all copies.
8d1ece852Seric  *
9d1ece852Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d1ece852Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d1ece852Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d1ece852Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d1ece852Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d1ece852Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d1ece852Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d1ece852Seric  */
17d1ece852Seric 
18d1ece852Seric #include <sys/socket.h>
19d1ece852Seric 
20d1ece852Seric #include <errno.h>
218d3f7f0dSeric #include <event.h>
22d1ece852Seric #include <fcntl.h>
230e63ed3bSchl #include <inttypes.h>
24d1ece852Seric #include <stdlib.h>
25d1ece852Seric #include <string.h>
26d1ece852Seric #include <stdio.h>
27eed85469Seric #ifdef IO_TLS
28eed85469Seric #include <tls.h>
29eed85469Seric #endif
30d1ece852Seric #include <unistd.h>
31d1ece852Seric 
32d1ece852Seric #include "ioev.h"
33d1ece852Seric #include "iobuf.h"
34ff01b044Seric #include "log.h"
35d1ece852Seric 
36d1ece852Seric enum {
37d1ece852Seric 	IO_STATE_NONE,
38d1ece852Seric 	IO_STATE_CONNECT,
399ac7cdfdSeric 	IO_STATE_CONNECT_TLS,
409ac7cdfdSeric 	IO_STATE_ACCEPT_TLS,
41d1ece852Seric 	IO_STATE_UP,
42d1ece852Seric 
43d1ece852Seric 	IO_STATE_MAX,
44d1ece852Seric };
45d1ece852Seric 
46f80a744fSeric #define IO_PAUSE_IN 		IO_IN
47f80a744fSeric #define IO_PAUSE_OUT		IO_OUT
48f80a744fSeric #define IO_READ			0x04
49f80a744fSeric #define IO_WRITE		0x08
50f80a744fSeric #define IO_RW			(IO_READ | IO_WRITE)
51f80a744fSeric #define IO_RESET		0x10  /* internal */
52f80a744fSeric #define IO_HELD			0x20  /* internal */
53f80a744fSeric 
548d3f7f0dSeric struct io {
558d3f7f0dSeric 	int		 sock;
568d3f7f0dSeric 	void		*arg;
578d3f7f0dSeric 	void		(*cb)(struct io*, int, void *);
5805afcc4dSeric 	struct iobuf	 iobuf;
598d3f7f0dSeric 	size_t		 lowat;
608d3f7f0dSeric 	int		 timeout;
618d3f7f0dSeric 	int		 flags;
628d3f7f0dSeric 	int		 state;
638d3f7f0dSeric 	struct event	 ev;
64eed85469Seric 	struct tls	*tls;
65eed85469Seric 
668d3f7f0dSeric 	const char	*error; /* only valid immediately on callback */
678d3f7f0dSeric };
688d3f7f0dSeric 
69d1ece852Seric const char* io_strflags(int);
70d1ece852Seric const char* io_evstr(short);
71d1ece852Seric 
72d1ece852Seric void	_io_init(void);
73d1ece852Seric void	io_hold(struct io *);
74d1ece852Seric void	io_release(struct io *);
75d1ece852Seric void	io_callback(struct io*, int);
76d1ece852Seric void	io_dispatch(int, short, void *);
77d1ece852Seric void	io_dispatch_connect(int, short, void *);
78d1ece852Seric size_t	io_pending(struct io *);
79d1ece852Seric size_t	io_queued(struct io*);
80d1ece852Seric void	io_reset(struct io *, short, void (*)(int, short, void*));
81d1ece852Seric void	io_frame_enter(const char *, struct io *, int);
82d1ece852Seric void	io_frame_leave(struct io *);
83d1ece852Seric 
849ac7cdfdSeric #ifdef IO_TLS
85eed85469Seric void	io_dispatch_handshake_tls(int, short, void *);
869ac7cdfdSeric void	io_dispatch_accept_tls(int, short, void *);
879ac7cdfdSeric void	io_dispatch_connect_tls(int, short, void *);
889ac7cdfdSeric void	io_dispatch_read_tls(int, short, void *);
899ac7cdfdSeric void	io_dispatch_write_tls(int, short, void *);
909ac7cdfdSeric void	io_reload_tls(struct io *io);
91d1ece852Seric #endif
92d1ece852Seric 
93d1ece852Seric static struct io	*current = NULL;
94d1ece852Seric static uint64_t		 frame = 0;
95d1ece852Seric static int		_io_debug = 0;
96d1ece852Seric 
97d1ece852Seric #define io_debug(args...) do { if (_io_debug) printf(args); } while(0)
98d1ece852Seric 
99d1ece852Seric 
100d1ece852Seric const char*
io_strio(struct io * io)101d1ece852Seric io_strio(struct io *io)
102d1ece852Seric {
103d1ece852Seric 	static char	buf[128];
10405a218a6Seric 	char		ssl[128];
105d1ece852Seric 
10605a218a6Seric 	ssl[0] = '\0';
1079ac7cdfdSeric #ifdef IO_TLS
1089ac7cdfdSeric 	if (io->tls) {
109eed85469Seric 		(void)snprintf(ssl, sizeof ssl, " tls=%s:%s",
110eed85469Seric 		    tls_conn_version(io->tls),
111eed85469Seric 		    tls_conn_cipher(io->tls));
11205a218a6Seric 	}
11305a218a6Seric #endif
11405a218a6Seric 
115fd77dd20Sgilles 	(void)snprintf(buf, sizeof buf,
116d7bcae4dSeric 	    "<io:%p fd=%d to=%d fl=%s%s ib=%zu ob=%zu>",
11705a218a6Seric 	    io, io->sock, io->timeout, io_strflags(io->flags), ssl,
118d1ece852Seric 	    io_pending(io), io_queued(io));
11905a218a6Seric 
120d1ece852Seric 	return (buf);
121d1ece852Seric }
122d1ece852Seric 
123d1ece852Seric #define CASE(x) case x : return #x
124d1ece852Seric 
125d1ece852Seric const char*
io_strevent(int evt)126d1ece852Seric io_strevent(int evt)
127d1ece852Seric {
128d1ece852Seric 	static char buf[32];
129d1ece852Seric 
130d1ece852Seric 	switch (evt) {
131d1ece852Seric 	CASE(IO_CONNECTED);
132d1ece852Seric 	CASE(IO_TLSREADY);
133d1ece852Seric 	CASE(IO_DATAIN);
134d1ece852Seric 	CASE(IO_LOWAT);
135d1ece852Seric 	CASE(IO_DISCONNECTED);
136d1ece852Seric 	CASE(IO_TIMEOUT);
137d1ece852Seric 	CASE(IO_ERROR);
138d1ece852Seric 	default:
139fd77dd20Sgilles 		(void)snprintf(buf, sizeof(buf), "IO_? %d", evt);
140d1ece852Seric 		return buf;
141d1ece852Seric 	}
142d1ece852Seric }
143d1ece852Seric 
144d1ece852Seric void
io_set_nonblocking(int fd)145907c4b99Skrw io_set_nonblocking(int fd)
146d1ece852Seric {
147d1ece852Seric 	int	flags;
148d1ece852Seric 
149907c4b99Skrw 	if ((flags = fcntl(fd, F_GETFL)) == -1)
150ff01b044Seric 		fatal("io_set_blocking:fcntl(F_GETFL)");
151d1ece852Seric 
152d1ece852Seric 	flags |= O_NONBLOCK;
153d1ece852Seric 
15432e1408cSmillert 	if (fcntl(fd, F_SETFL, flags) == -1)
155ff01b044Seric 		fatal("io_set_blocking:fcntl(F_SETFL)");
156d1ece852Seric }
157d1ece852Seric 
158d1ece852Seric void
io_set_nolinger(int fd)159907c4b99Skrw io_set_nolinger(int fd)
160d1ece852Seric {
161d1ece852Seric 	struct linger    l;
162d1ece852Seric 
163c1392a69Seric 	memset(&l, 0, sizeof(l));
164d1ece852Seric 	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
165ff01b044Seric 		fatal("io_set_linger:setsockopt");
166d1ece852Seric }
167d1ece852Seric 
168d1ece852Seric /*
169d1ece852Seric  * Event framing must not rely on an io pointer to refer to the "same" io
170af0dba2aSsobrado  * throughout the frame, because this is not always the case:
171d1ece852Seric  *
172d1ece852Seric  * 1) enter(addr0) -> free(addr0) -> leave(addr0) = SEGV
173d1ece852Seric  * 2) enter(addr0) -> free(addr0) -> malloc == addr0 -> leave(addr0) = BAD!
174d1ece852Seric  *
175d1ece852Seric  * In both case, the problem is that the io is freed in the callback, so
176d1ece852Seric  * the pointer becomes invalid. If that happens, the user is required to
177d1ece852Seric  * call io_clear, so we can adapt the frame state there.
178d1ece852Seric  */
179d1ece852Seric void
io_frame_enter(const char * where,struct io * io,int ev)180d1ece852Seric io_frame_enter(const char *where, struct io *io, int ev)
181d1ece852Seric {
182d1ece852Seric 	io_debug("\n=== %" PRIu64 " ===\n"
183d1ece852Seric 	    "io_frame_enter(%s, %s, %s)\n",
184d1ece852Seric 	    frame, where, io_evstr(ev), io_strio(io));
185d1ece852Seric 
186d1ece852Seric 	if (current)
187ff01b044Seric 		fatalx("io_frame_enter: interleaved frames");
188d1ece852Seric 
189d1ece852Seric 	current = io;
190d1ece852Seric 
191d1ece852Seric 	io_hold(io);
192d1ece852Seric }
193d1ece852Seric 
194d1ece852Seric void
io_frame_leave(struct io * io)195d1ece852Seric io_frame_leave(struct io *io)
196d1ece852Seric {
197d1ece852Seric 	io_debug("io_frame_leave(%" PRIu64 ")\n", frame);
198d1ece852Seric 
199d1ece852Seric 	if (current && current != io)
200ff01b044Seric 		fatalx("io_frame_leave: io mismatch");
201d1ece852Seric 
202d1ece852Seric 	/* io has been cleared */
203d1ece852Seric 	if (current == NULL)
204d1ece852Seric 		goto done;
205d1ece852Seric 
206d1ece852Seric 	/* TODO: There is a possible optimization there:
207d1ece852Seric 	 * In a typical half-duplex request/response scenario,
208d1ece852Seric 	 * the io is waiting to read a request, and when done, it queues
209d1ece852Seric 	 * the response in the output buffer and goes to write mode.
210d1ece852Seric 	 * There, the write event is set and will be triggered in the next
211d1ece852Seric 	 * event frame.  In most case, the write call could be done
212af0dba2aSsobrado 	 * immediately as part of the last read frame, thus avoiding to go
213d1ece852Seric 	 * through the event loop machinery. So, as an optimisation, we
214d1ece852Seric 	 * could detect that case here and force an event dispatching.
215d1ece852Seric 	 */
216d1ece852Seric 
217d1ece852Seric 	/* Reload the io if it has not been reset already. */
218d1ece852Seric 	io_release(io);
219d1ece852Seric 	current = NULL;
220d1ece852Seric     done:
221d1ece852Seric 	io_debug("=== /%" PRIu64 "\n", frame);
222d1ece852Seric 
223d1ece852Seric 	frame += 1;
224d1ece852Seric }
225d1ece852Seric 
226d1ece852Seric void
_io_init(void)227*04fee684Stb _io_init(void)
228d1ece852Seric {
229d1ece852Seric 	static int init = 0;
230d1ece852Seric 
231d1ece852Seric 	if (init)
232d1ece852Seric 		return;
233d1ece852Seric 
234d1ece852Seric 	init = 1;
235d1ece852Seric 	_io_debug = getenv("IO_DEBUG") != NULL;
236d1ece852Seric }
237d1ece852Seric 
2388d3f7f0dSeric struct io *
io_new(void)2398d3f7f0dSeric io_new(void)
240d1ece852Seric {
2418d3f7f0dSeric 	struct io *io;
2428d3f7f0dSeric 
243d1ece852Seric 	_io_init();
244d1ece852Seric 
2458d3f7f0dSeric 	if ((io = calloc(1, sizeof(*io))) == NULL)
2468d3f7f0dSeric 		return NULL;
247d1ece852Seric 
248f81b996bSeric 	io->sock = -1;
249d1ece852Seric 	io->timeout = -1;
2508d3f7f0dSeric 
25105afcc4dSeric 	if (iobuf_init(&io->iobuf, 0, 0) == -1) {
2528d3f7f0dSeric 		free(io);
2538d3f7f0dSeric 		return NULL;
2548d3f7f0dSeric 	}
2558d3f7f0dSeric 
2568d3f7f0dSeric 	return io;
257d1ece852Seric }
258d1ece852Seric 
259d1ece852Seric void
io_free(struct io * io)2608d3f7f0dSeric io_free(struct io *io)
261d1ece852Seric {
262d1ece852Seric 	io_debug("io_clear(%p)\n", io);
263d1ece852Seric 
264d1ece852Seric 	/* the current io is virtually dead */
265d1ece852Seric 	if (io == current)
266d1ece852Seric 		current = NULL;
267d1ece852Seric 
2689ac7cdfdSeric #ifdef IO_TLS
269eed85469Seric 	tls_free(io->tls);
2709ac7cdfdSeric 	io->tls = NULL;
271d1ece852Seric #endif
272d1ece852Seric 
2739ad25316Seric 	if (event_initialized(&io->ev))
274d1ece852Seric 		event_del(&io->ev);
275d1ece852Seric 	if (io->sock != -1) {
276d1ece852Seric 		close(io->sock);
277d1ece852Seric 		io->sock = -1;
278d1ece852Seric 	}
2798d3f7f0dSeric 
28005afcc4dSeric 	iobuf_clear(&io->iobuf);
2818d3f7f0dSeric 	free(io);
282d1ece852Seric }
283d1ece852Seric 
284d1ece852Seric void
io_hold(struct io * io)285d1ece852Seric io_hold(struct io *io)
286d1ece852Seric {
287d1ece852Seric 	io_debug("io_enter(%p)\n", io);
288d1ece852Seric 
289d1ece852Seric 	if (io->flags & IO_HELD)
290ff01b044Seric 		fatalx("io_hold: io is already held");
291d1ece852Seric 
292d1ece852Seric 	io->flags &= ~IO_RESET;
293d1ece852Seric 	io->flags |= IO_HELD;
294d1ece852Seric }
295d1ece852Seric 
296d1ece852Seric void
io_release(struct io * io)297d1ece852Seric io_release(struct io *io)
298d1ece852Seric {
299d1ece852Seric 	if (!(io->flags & IO_HELD))
300ff01b044Seric 		fatalx("io_release: io is not held");
301d1ece852Seric 
302d1ece852Seric 	io->flags &= ~IO_HELD;
303d1ece852Seric 	if (!(io->flags & IO_RESET))
304d1ece852Seric 		io_reload(io);
305d1ece852Seric }
306d1ece852Seric 
307d1ece852Seric void
io_set_fd(struct io * io,int fd)308f81b996bSeric io_set_fd(struct io *io, int fd)
309f81b996bSeric {
310f81b996bSeric 	io->sock = fd;
311f81b996bSeric 	if (fd != -1)
312f81b996bSeric 		io_reload(io);
313f81b996bSeric }
314f81b996bSeric 
315f81b996bSeric void
io_set_callback(struct io * io,void (* cb)(struct io *,int,void *),void * arg)316f81b996bSeric io_set_callback(struct io *io, void(*cb)(struct io *, int, void *), void *arg)
317f81b996bSeric {
318f81b996bSeric 	io->cb = cb;
319f81b996bSeric 	io->arg = arg;
320f81b996bSeric }
321f81b996bSeric 
322f81b996bSeric void
io_set_timeout(struct io * io,int msec)323d1ece852Seric io_set_timeout(struct io *io, int msec)
324d1ece852Seric {
325d7bcae4dSeric 	io_debug("io_set_timeout(%p, %d)\n", io, msec);
326d1ece852Seric 
327d1ece852Seric 	io->timeout = msec;
328d1ece852Seric }
329d1ece852Seric 
330d1ece852Seric void
io_set_lowat(struct io * io,size_t lowat)331d1ece852Seric io_set_lowat(struct io *io, size_t lowat)
332d1ece852Seric {
333d1ece852Seric 	io_debug("io_set_lowat(%p, %zu)\n", io, lowat);
334d1ece852Seric 
335d1ece852Seric 	io->lowat = lowat;
336d1ece852Seric }
337d1ece852Seric 
338d1ece852Seric void
io_pause(struct io * io,int dir)339d1ece852Seric io_pause(struct io *io, int dir)
340d1ece852Seric {
341d1ece852Seric 	io_debug("io_pause(%p, %x)\n", io, dir);
342d1ece852Seric 
343d1ece852Seric 	io->flags |= dir & (IO_PAUSE_IN | IO_PAUSE_OUT);
344d1ece852Seric 	io_reload(io);
345d1ece852Seric }
346d1ece852Seric 
347d1ece852Seric void
io_resume(struct io * io,int dir)348d1ece852Seric io_resume(struct io *io, int dir)
349d1ece852Seric {
350d1ece852Seric 	io_debug("io_resume(%p, %x)\n", io, dir);
351d1ece852Seric 
352d1ece852Seric 	io->flags &= ~(dir & (IO_PAUSE_IN | IO_PAUSE_OUT));
353d1ece852Seric 	io_reload(io);
354d1ece852Seric }
355d1ece852Seric 
356d1ece852Seric void
io_set_read(struct io * io)357d1ece852Seric io_set_read(struct io *io)
358d1ece852Seric {
359d1ece852Seric 	int	mode;
360d1ece852Seric 
361d1ece852Seric 	io_debug("io_set_read(%p)\n", io);
362d1ece852Seric 
363d1ece852Seric 	mode = io->flags & IO_RW;
364d1ece852Seric 	if (!(mode == 0 || mode == IO_WRITE))
365ff01b044Seric 		fatalx("io_set_read: full-duplex or reading");
366d1ece852Seric 
367d1ece852Seric 	io->flags &= ~IO_RW;
368d1ece852Seric 	io->flags |= IO_READ;
369d1ece852Seric 	io_reload(io);
370d1ece852Seric }
371d1ece852Seric 
372d1ece852Seric void
io_set_write(struct io * io)373d1ece852Seric io_set_write(struct io *io)
374d1ece852Seric {
375d1ece852Seric 	int	mode;
376d1ece852Seric 
377d1ece852Seric 	io_debug("io_set_write(%p)\n", io);
378d1ece852Seric 
379d1ece852Seric 	mode = io->flags & IO_RW;
380d1ece852Seric 	if (!(mode == 0 || mode == IO_READ))
381ff01b044Seric 		fatalx("io_set_write: full-duplex or writing");
382d1ece852Seric 
383d1ece852Seric 	io->flags &= ~IO_RW;
384d1ece852Seric 	io->flags |= IO_WRITE;
385d1ece852Seric 	io_reload(io);
386d1ece852Seric }
387d1ece852Seric 
388219e2fd6Seric const char *
io_error(struct io * io)389219e2fd6Seric io_error(struct io *io)
390219e2fd6Seric {
391219e2fd6Seric 	return io->error;
392219e2fd6Seric }
393219e2fd6Seric 
394eed85469Seric struct tls *
io_tls(struct io * io)3959ac7cdfdSeric io_tls(struct io *io)
3960a90943cSeric {
3979ac7cdfdSeric 	return io->tls;
3980a90943cSeric }
3990a90943cSeric 
40040f95b7eSeric int
io_fileno(struct io * io)40140f95b7eSeric io_fileno(struct io *io)
40240f95b7eSeric {
40340f95b7eSeric 	return io->sock;
40440f95b7eSeric }
40540f95b7eSeric 
406c13965bbSeric int
io_paused(struct io * io,int what)407c13965bbSeric io_paused(struct io *io, int what)
408c13965bbSeric {
409c13965bbSeric 	return (io->flags & (IO_PAUSE_IN | IO_PAUSE_OUT)) == what;
410c13965bbSeric }
411c13965bbSeric 
41266802da1Seric /*
41366802da1Seric  * Buffered output functions
41466802da1Seric  */
41566802da1Seric 
41666802da1Seric int
io_write(struct io * io,const void * buf,size_t len)41766802da1Seric io_write(struct io *io, const void *buf, size_t len)
41866802da1Seric {
419c57f8f28Seric 	int r;
420c57f8f28Seric 
42105afcc4dSeric 	r = iobuf_queue(&io->iobuf, buf, len);
422c57f8f28Seric 
423c57f8f28Seric 	io_reload(io);
424c57f8f28Seric 
425c57f8f28Seric 	return r;
42666802da1Seric }
42766802da1Seric 
42866802da1Seric int
io_writev(struct io * io,const struct iovec * iov,int iovcount)42966802da1Seric io_writev(struct io *io, const struct iovec *iov, int iovcount)
43066802da1Seric {
431c57f8f28Seric 	int r;
432c57f8f28Seric 
43305afcc4dSeric 	r = iobuf_queuev(&io->iobuf, iov, iovcount);
434c57f8f28Seric 
435c57f8f28Seric 	io_reload(io);
436c57f8f28Seric 
437c57f8f28Seric 	return r;
43866802da1Seric }
43966802da1Seric 
44066802da1Seric int
io_print(struct io * io,const char * s)44166802da1Seric io_print(struct io *io, const char *s)
44266802da1Seric {
44366802da1Seric 	return io_write(io, s, strlen(s));
44466802da1Seric }
44566802da1Seric 
44666802da1Seric int
io_printf(struct io * io,const char * fmt,...)44766802da1Seric io_printf(struct io *io, const char *fmt, ...)
44866802da1Seric {
44966802da1Seric 	va_list ap;
45066802da1Seric 	int r;
45166802da1Seric 
45266802da1Seric 	va_start(ap, fmt);
45366802da1Seric 	r = io_vprintf(io, fmt, ap);
45466802da1Seric 	va_end(ap);
45566802da1Seric 
45666802da1Seric 	return r;
45766802da1Seric }
45866802da1Seric 
45966802da1Seric int
io_vprintf(struct io * io,const char * fmt,va_list ap)46066802da1Seric io_vprintf(struct io *io, const char *fmt, va_list ap)
46166802da1Seric {
46266802da1Seric 
46366802da1Seric 	char *buf;
46466802da1Seric 	int len;
46566802da1Seric 
46666802da1Seric 	len = vasprintf(&buf, fmt, ap);
46766802da1Seric 	if (len == -1)
46866802da1Seric 		return -1;
46966802da1Seric 	len = io_write(io, buf, len);
47066802da1Seric 	free(buf);
47166802da1Seric 
47266802da1Seric 	return len;
47366802da1Seric }
47466802da1Seric 
47566802da1Seric size_t
io_queued(struct io * io)47666802da1Seric io_queued(struct io *io)
47766802da1Seric {
47805afcc4dSeric 	return iobuf_queued(&io->iobuf);
47966802da1Seric }
48066802da1Seric 
48166802da1Seric /*
48266802da1Seric  * Buffered input functions
48366802da1Seric  */
48466802da1Seric 
48566802da1Seric void *
io_data(struct io * io)48666802da1Seric io_data(struct io *io)
48766802da1Seric {
48805afcc4dSeric 	return iobuf_data(&io->iobuf);
48966802da1Seric }
49066802da1Seric 
49166802da1Seric size_t
io_datalen(struct io * io)49266802da1Seric io_datalen(struct io *io)
49366802da1Seric {
49405afcc4dSeric 	return iobuf_len(&io->iobuf);
49566802da1Seric }
49666802da1Seric 
49766802da1Seric char *
io_getline(struct io * io,size_t * sz)49866802da1Seric io_getline(struct io *io, size_t *sz)
49966802da1Seric {
50005afcc4dSeric 	return iobuf_getline(&io->iobuf, sz);
50166802da1Seric }
50266802da1Seric 
50366802da1Seric void
io_drop(struct io * io,size_t sz)50466802da1Seric io_drop(struct io *io, size_t sz)
50566802da1Seric {
50605afcc4dSeric 	return iobuf_drop(&io->iobuf, sz);
50766802da1Seric }
50866802da1Seric 
50966802da1Seric 
510d1ece852Seric #define IO_READING(io) (((io)->flags & IO_RW) != IO_WRITE)
511d1ece852Seric #define IO_WRITING(io) (((io)->flags & IO_RW) != IO_READ)
512d1ece852Seric 
513d1ece852Seric /*
514d1ece852Seric  * Setup the necessary events as required by the current io state,
515d1ece852Seric  * honouring duplex mode and i/o pauses.
516d1ece852Seric  */
517d1ece852Seric void
io_reload(struct io * io)518d1ece852Seric io_reload(struct io *io)
519d1ece852Seric {
520d1ece852Seric 	short	events;
521d1ece852Seric 
522d1ece852Seric 	/* io will be reloaded at release time */
523d1ece852Seric 	if (io->flags & IO_HELD)
524d1ece852Seric 		return;
525d1ece852Seric 
52605afcc4dSeric 	iobuf_normalize(&io->iobuf);
5270e198904Seric 
5289ac7cdfdSeric #ifdef IO_TLS
5299ac7cdfdSeric 	if (io->tls) {
5309ac7cdfdSeric 		io_reload_tls(io);
531d1ece852Seric 		return;
532d1ece852Seric 	}
533d1ece852Seric #endif
534d1ece852Seric 
535d1ece852Seric 	io_debug("io_reload(%p)\n", io);
536d1ece852Seric 
537d1ece852Seric 	events = 0;
538d1ece852Seric 	if (IO_READING(io) && !(io->flags & IO_PAUSE_IN))
539d1ece852Seric 		events = EV_READ;
540d1ece852Seric 	if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io))
541d1ece852Seric 		events |= EV_WRITE;
542d1ece852Seric 
543d1ece852Seric 	io_reset(io, events, io_dispatch);
544d1ece852Seric }
545d1ece852Seric 
546d1ece852Seric /* Set the requested event. */
547d1ece852Seric void
io_reset(struct io * io,short events,void (* dispatch)(int,short,void *))548d1ece852Seric io_reset(struct io *io, short events, void (*dispatch)(int, short, void*))
549d1ece852Seric {
550d1ece852Seric 	struct timeval	tv, *ptv;
551d1ece852Seric 
552d1ece852Seric 	io_debug("io_reset(%p, %s, %p) -> %s\n",
553d1ece852Seric 	    io, io_evstr(events), dispatch, io_strio(io));
554d1ece852Seric 
555d1ece852Seric 	/*
556d1ece852Seric 	 * Indicate that the event has already been reset so that reload
557d1ece852Seric 	 * is not called on frame_leave.
558d1ece852Seric 	 */
559d1ece852Seric 	io->flags |= IO_RESET;
560d1ece852Seric 
5619ad25316Seric 	if (event_initialized(&io->ev))
562d1ece852Seric 		event_del(&io->ev);
563d1ece852Seric 
564d1ece852Seric 	/*
565d1ece852Seric 	 * The io is paused by the user, so we don't want the timeout to be
566d1ece852Seric 	 * effective.
567d1ece852Seric 	 */
568d1ece852Seric 	if (events == 0)
569d1ece852Seric 		return;
570d1ece852Seric 
571d1ece852Seric 	event_set(&io->ev, io->sock, events, dispatch, io);
572d1ece852Seric 	if (io->timeout >= 0) {
573d1ece852Seric 		tv.tv_sec = io->timeout / 1000;
574d1ece852Seric 		tv.tv_usec = (io->timeout % 1000) * 1000;
575d1ece852Seric 		ptv = &tv;
576d1ece852Seric 	} else
577d1ece852Seric 		ptv = NULL;
578d1ece852Seric 
579d1ece852Seric 	event_add(&io->ev, ptv);
580d1ece852Seric }
581d1ece852Seric 
582d1ece852Seric size_t
io_pending(struct io * io)583d1ece852Seric io_pending(struct io *io)
584d1ece852Seric {
58505afcc4dSeric 	return iobuf_len(&io->iobuf);
586d1ece852Seric }
587d1ece852Seric 
588d1ece852Seric const char*
io_strflags(int flags)589d1ece852Seric io_strflags(int flags)
590d1ece852Seric {
591d1ece852Seric 	static char	buf[64];
592d1ece852Seric 
593d1ece852Seric 	buf[0] = '\0';
594d1ece852Seric 
595d1ece852Seric 	switch (flags & IO_RW) {
596d1ece852Seric 	case 0:
597eb54aa8aSgilles 		(void)strlcat(buf, "rw", sizeof buf);
598d1ece852Seric 		break;
599d1ece852Seric 	case IO_READ:
600eb54aa8aSgilles 		(void)strlcat(buf, "R", sizeof buf);
601d1ece852Seric 		break;
602d1ece852Seric 	case IO_WRITE:
603eb54aa8aSgilles 		(void)strlcat(buf, "W", sizeof buf);
604d1ece852Seric 		break;
605d1ece852Seric 	case IO_RW:
606eb54aa8aSgilles 		(void)strlcat(buf, "RW", sizeof buf);
607d1ece852Seric 		break;
608d1ece852Seric 	}
609d1ece852Seric 
610d1ece852Seric 	if (flags & IO_PAUSE_IN)
611eb54aa8aSgilles 		(void)strlcat(buf, ",F_PI", sizeof buf);
612d1ece852Seric 	if (flags & IO_PAUSE_OUT)
613eb54aa8aSgilles 		(void)strlcat(buf, ",F_PO", sizeof buf);
614d1ece852Seric 
615d1ece852Seric 	return buf;
616d1ece852Seric }
617d1ece852Seric 
618d1ece852Seric const char*
io_evstr(short ev)619d1ece852Seric io_evstr(short ev)
620d1ece852Seric {
621d1ece852Seric 	static char	buf[64];
622d1ece852Seric 	char		buf2[16];
623d1ece852Seric 	int		n;
624d1ece852Seric 
625d1ece852Seric 	n = 0;
626d1ece852Seric 	buf[0] = '\0';
627d1ece852Seric 
628d1ece852Seric 	if (ev == 0) {
629eb54aa8aSgilles 		(void)strlcat(buf, "<NONE>", sizeof(buf));
630d1ece852Seric 		return buf;
631d1ece852Seric 	}
632d1ece852Seric 
633d1ece852Seric 	if (ev & EV_TIMEOUT) {
634eb54aa8aSgilles 		(void)strlcat(buf, "EV_TIMEOUT", sizeof(buf));
635d1ece852Seric 		ev &= ~EV_TIMEOUT;
636d1ece852Seric 		n++;
637d1ece852Seric 	}
638d1ece852Seric 
639d1ece852Seric 	if (ev & EV_READ) {
640d1ece852Seric 		if (n)
641eb54aa8aSgilles 			(void)strlcat(buf, "|", sizeof(buf));
642eb54aa8aSgilles 		(void)strlcat(buf, "EV_READ", sizeof(buf));
643d1ece852Seric 		ev &= ~EV_READ;
644d1ece852Seric 		n++;
645d1ece852Seric 	}
646d1ece852Seric 
647d1ece852Seric 	if (ev & EV_WRITE) {
648d1ece852Seric 		if (n)
649eb54aa8aSgilles 			(void)strlcat(buf, "|", sizeof(buf));
650eb54aa8aSgilles 		(void)strlcat(buf, "EV_WRITE", sizeof(buf));
651d1ece852Seric 		ev &= ~EV_WRITE;
652d1ece852Seric 		n++;
653d1ece852Seric 	}
654d1ece852Seric 
655d1ece852Seric 	if (ev & EV_SIGNAL) {
656d1ece852Seric 		if (n)
657eb54aa8aSgilles 			(void)strlcat(buf, "|", sizeof(buf));
658eb54aa8aSgilles 		(void)strlcat(buf, "EV_SIGNAL", sizeof(buf));
659d1ece852Seric 		ev &= ~EV_SIGNAL;
660d1ece852Seric 		n++;
661d1ece852Seric 	}
662d1ece852Seric 
663d1ece852Seric 	if (ev) {
664d1ece852Seric 		if (n)
665eb54aa8aSgilles 			(void)strlcat(buf, "|", sizeof(buf));
666eb54aa8aSgilles 		(void)strlcat(buf, "EV_?=0x", sizeof(buf));
667eb54aa8aSgilles 		(void)snprintf(buf2, sizeof(buf2), "%hx", ev);
668eb54aa8aSgilles 		(void)strlcat(buf, buf2, sizeof(buf));
669d1ece852Seric 	}
670d1ece852Seric 
671d1ece852Seric 	return buf;
672d1ece852Seric }
673d1ece852Seric 
674d1ece852Seric void
io_dispatch(int fd,short ev,void * humppa)675d1ece852Seric io_dispatch(int fd, short ev, void *humppa)
676d1ece852Seric {
677d1ece852Seric 	struct io	*io = humppa;
678d1ece852Seric 	size_t		 w;
679d1ece852Seric 	ssize_t		 n;
68065c4fdfbSgilles 	int		 saved_errno;
681d1ece852Seric 
682d1ece852Seric 	io_frame_enter("io_dispatch", io, ev);
683d1ece852Seric 
684d1ece852Seric 	if (ev == EV_TIMEOUT) {
685d1ece852Seric 		io_callback(io, IO_TIMEOUT);
686d1ece852Seric 		goto leave;
687d1ece852Seric 	}
688d1ece852Seric 
689d1ece852Seric 	if (ev & EV_WRITE && (w = io_queued(io))) {
69005afcc4dSeric 		if ((n = iobuf_write(&io->iobuf, io->sock)) < 0) {
691a80802bcSeric 			if (n == IOBUF_WANT_WRITE) /* kqueue bug? */
692a80802bcSeric 				goto read;
69365c4fdfbSgilles 			if (n == IOBUF_CLOSED)
69465c4fdfbSgilles 				io_callback(io, IO_DISCONNECTED);
69565c4fdfbSgilles 			else {
69665c4fdfbSgilles 				saved_errno = errno;
69765c4fdfbSgilles 				io->error = strerror(errno);
69865c4fdfbSgilles 				errno = saved_errno;
69965c4fdfbSgilles 				io_callback(io, IO_ERROR);
70065c4fdfbSgilles 			}
701d1ece852Seric 			goto leave;
702d1ece852Seric 		}
703d1ece852Seric 		if (w > io->lowat && w - n <= io->lowat)
704d1ece852Seric 			io_callback(io, IO_LOWAT);
705d1ece852Seric 	}
706a80802bcSeric     read:
707d1ece852Seric 
708d1ece852Seric 	if (ev & EV_READ) {
70905afcc4dSeric 		iobuf_normalize(&io->iobuf);
71005afcc4dSeric 		if ((n = iobuf_read(&io->iobuf, io->sock)) < 0) {
71165c4fdfbSgilles 			if (n == IOBUF_CLOSED)
71265c4fdfbSgilles 				io_callback(io, IO_DISCONNECTED);
71365c4fdfbSgilles 			else {
71465c4fdfbSgilles 				saved_errno = errno;
71565c4fdfbSgilles 				io->error = strerror(errno);
71665c4fdfbSgilles 				errno = saved_errno;
71765c4fdfbSgilles 				io_callback(io, IO_ERROR);
71865c4fdfbSgilles 			}
719d1ece852Seric 			goto leave;
720d1ece852Seric 		}
721d1ece852Seric 		if (n)
722d1ece852Seric 			io_callback(io, IO_DATAIN);
723d1ece852Seric 	}
724d1ece852Seric 
725d1ece852Seric leave:
726d1ece852Seric 	io_frame_leave(io);
727d1ece852Seric }
728d1ece852Seric 
729d1ece852Seric void
io_callback(struct io * io,int evt)730d1ece852Seric io_callback(struct io *io, int evt)
731d1ece852Seric {
732b556a8d3Seric 	io->cb(io, evt, io->arg);
733d1ece852Seric }
734d1ece852Seric 
735d1ece852Seric int
io_connect(struct io * io,const struct sockaddr * sa,const struct sockaddr * bsa)7362c1d98b8Seric io_connect(struct io *io, const struct sockaddr *sa, const struct sockaddr *bsa)
737d1ece852Seric {
738d1ece852Seric 	int	sock, errno_save;
739d1ece852Seric 
740d1ece852Seric 	if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) == -1)
741d1ece852Seric 		goto fail;
742d1ece852Seric 
743907c4b99Skrw 	io_set_nonblocking(sock);
744907c4b99Skrw 	io_set_nolinger(sock);
745d1ece852Seric 
7462c1d98b8Seric 	if (bsa && bind(sock, bsa, bsa->sa_len) == -1)
7472c1d98b8Seric 		goto fail;
7482c1d98b8Seric 
749d1ece852Seric 	if (connect(sock, sa, sa->sa_len) == -1)
750d1ece852Seric 		if (errno != EINPROGRESS)
751d1ece852Seric 			goto fail;
752d1ece852Seric 
753d1ece852Seric 	io->sock = sock;
754d1ece852Seric 	io_reset(io, EV_WRITE, io_dispatch_connect);
755d1ece852Seric 
756d1ece852Seric 	return (sock);
757d1ece852Seric 
758d1ece852Seric     fail:
759d1ece852Seric 	if (sock != -1) {
760d1ece852Seric 		errno_save = errno;
761d1ece852Seric 		close(sock);
762d1ece852Seric 		errno = errno_save;
76365c4fdfbSgilles 		io->error = strerror(errno);
764d1ece852Seric 	}
765d1ece852Seric 	return (-1);
766d1ece852Seric }
767d1ece852Seric 
768d1ece852Seric void
io_dispatch_connect(int fd,short ev,void * humppa)769d1ece852Seric io_dispatch_connect(int fd, short ev, void *humppa)
770d1ece852Seric {
771d1ece852Seric 	struct io	*io = humppa;
772299c4efeSeric 	int		 r, e;
773299c4efeSeric 	socklen_t	 sl;
774d1ece852Seric 
775d1ece852Seric 	io_frame_enter("io_dispatch_connect", io, ev);
776d1ece852Seric 
777d1ece852Seric 	if (ev == EV_TIMEOUT) {
778d1ece852Seric 		close(fd);
779d1ece852Seric 		io->sock = -1;
780d1ece852Seric 		io_callback(io, IO_TIMEOUT);
781d1ece852Seric 	} else {
782299c4efeSeric 		sl = sizeof(e);
783299c4efeSeric 		r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &e, &sl);
784299c4efeSeric 		if (r == -1)  {
785ff01b044Seric 			log_warn("io_dispatch_connect: getsockopt");
786299c4efeSeric 			e = errno;
787299c4efeSeric 		}
788299c4efeSeric 		if (e) {
789299c4efeSeric 			close(fd);
790299c4efeSeric 			io->sock = -1;
791299c4efeSeric 			io->error = strerror(e);
792299c4efeSeric 			io_callback(io, e == ETIMEDOUT ? IO_TIMEOUT : IO_ERROR);
793299c4efeSeric 		}
794299c4efeSeric 		else {
795d1ece852Seric 			io->state = IO_STATE_UP;
796d1ece852Seric 			io_callback(io, IO_CONNECTED);
797d1ece852Seric 		}
798299c4efeSeric 	}
799d1ece852Seric 
800d1ece852Seric 	io_frame_leave(io);
801d1ece852Seric }
802d1ece852Seric 
8039ac7cdfdSeric #ifdef IO_TLS
804d1ece852Seric int
io_connect_tls(struct io * io,struct tls * tls,const char * hostname)805eed85469Seric io_connect_tls(struct io *io, struct tls *tls, const char *hostname)
806d1ece852Seric {
807d1ece852Seric 	int	mode;
808d1ece852Seric 
809d1ece852Seric 	mode = io->flags & IO_RW;
810eed85469Seric 	if (mode != IO_WRITE)
811ff01b044Seric 		fatalx("io_connect_tls: expect IO_WRITE mode");
812d1ece852Seric 
8139ac7cdfdSeric 	if (io->tls)
814ff01b044Seric 		fatalx("io_connect_tls: TLS already started");
815eed85469Seric 
816c11a901bSeric 	if (tls_connect_socket(tls, io->sock, hostname) == -1) {
817c11a901bSeric 		io->error = tls_error(tls);
818c11a901bSeric 		return (-1);
819eed85469Seric 	}
820eed85469Seric 
8219ac7cdfdSeric 	io->tls = tls;
8229ac7cdfdSeric 	io->state = IO_STATE_CONNECT_TLS;
823c11a901bSeric 	io_reset(io, EV_READ|EV_WRITE, io_dispatch_handshake_tls);
824d1ece852Seric 
825d1ece852Seric 	return (0);
826d1ece852Seric }
827d1ece852Seric 
828eed85469Seric int
io_accept_tls(struct io * io,struct tls * tls)829eed85469Seric io_accept_tls(struct io *io, struct tls *tls)
830eed85469Seric {
831eed85469Seric 	int	mode;
832eed85469Seric 
833eed85469Seric 	mode = io->flags & IO_RW;
834eed85469Seric 	if (mode != IO_READ)
835ff01b044Seric 		fatalx("io_accept_tls: expect IO_READ mode");
836eed85469Seric 
837eed85469Seric 	if (io->tls)
838ff01b044Seric 		fatalx("io_accept_tls: TLS already started");
839c11a901bSeric 
840c11a901bSeric 	if (tls_accept_socket(tls, &io->tls, io->sock) == -1) {
841c11a901bSeric 		io->error = tls_error(tls);
842c11a901bSeric 		return (-1);
843c11a901bSeric 	}
844c11a901bSeric 
845eed85469Seric 	io->state = IO_STATE_ACCEPT_TLS;
846c11a901bSeric 	io_reset(io, EV_READ|EV_WRITE, io_dispatch_handshake_tls);
847eed85469Seric 
848eed85469Seric 	return (0);
849eed85469Seric }
850eed85469Seric 
851eed85469Seric void
io_dispatch_handshake_tls(int fd,short event,void * humppa)852eed85469Seric io_dispatch_handshake_tls(int fd, short event, void *humppa)
853eed85469Seric {
854eed85469Seric 	struct io	*io = humppa;
855eed85469Seric 	int		ret;
856eed85469Seric 
857eed85469Seric 	io_frame_enter("io_dispatch_handshake_tls", io, event);
858eed85469Seric 
859eed85469Seric 	if (event == EV_TIMEOUT) {
860eed85469Seric 		io_callback(io, IO_TIMEOUT);
861eed85469Seric 		goto leave;
862eed85469Seric 	}
863eed85469Seric 
864eed85469Seric 	if ((ret = tls_handshake(io->tls)) == 0) {
865eed85469Seric 		io->state = IO_STATE_UP;
866eed85469Seric 		io_callback(io, IO_TLSREADY);
867eed85469Seric 		goto leave;
868eed85469Seric 	}
869eed85469Seric 	if (ret == TLS_WANT_POLLIN)
870eed85469Seric 		io_reset(io, EV_READ, io_dispatch_handshake_tls);
871eed85469Seric 	else if (ret == TLS_WANT_POLLOUT)
872eed85469Seric 		io_reset(io, EV_WRITE, io_dispatch_handshake_tls);
873eed85469Seric 	else {
874eed85469Seric 		io->error = tls_error(io->tls);
875eed85469Seric 		io_callback(io, IO_ERROR);
876eed85469Seric 	}
877eed85469Seric 
878eed85469Seric  leave:
879eed85469Seric 	io_frame_leave(io);
880eed85469Seric 	return;
881eed85469Seric }
882eed85469Seric 
883d1ece852Seric void
io_dispatch_read_tls(int fd,short event,void * humppa)8849ac7cdfdSeric io_dispatch_read_tls(int fd, short event, void *humppa)
885d1ece852Seric {
886d1ece852Seric 	struct io	*io = humppa;
887eed85469Seric 	int		 n;
888d1ece852Seric 
8899ac7cdfdSeric 	io_frame_enter("io_dispatch_read_tls", io, event);
890d1ece852Seric 
891d1ece852Seric 	if (event == EV_TIMEOUT) {
892d1ece852Seric 		io_callback(io, IO_TIMEOUT);
893d1ece852Seric 		goto leave;
894d1ece852Seric 	}
895d1ece852Seric 
896b0e8ad58Seric again:
89705afcc4dSeric 	iobuf_normalize(&io->iobuf);
898eed85469Seric 	switch ((n = iobuf_read_tls(&io->iobuf, io->tls))) {
899d1ece852Seric 	case IOBUF_WANT_READ:
9009ac7cdfdSeric 		io_reset(io, EV_READ, io_dispatch_read_tls);
901d1ece852Seric 		break;
902d1ece852Seric 	case IOBUF_WANT_WRITE:
9039ac7cdfdSeric 		io_reset(io, EV_WRITE, io_dispatch_read_tls);
904d1ece852Seric 		break;
905d1ece852Seric 	case IOBUF_CLOSED:
906d1ece852Seric 		io_callback(io, IO_DISCONNECTED);
907d1ece852Seric 		break;
908d1ece852Seric 	case IOBUF_ERROR:
909eed85469Seric 		io->error = tls_error(io->tls);
910d1ece852Seric 		io_callback(io, IO_ERROR);
911d1ece852Seric 		break;
912d1ece852Seric 	default:
9139ac7cdfdSeric 		io_debug("io_dispatch_read_tls(...) -> r=%d\n", n);
914d1ece852Seric 		io_callback(io, IO_DATAIN);
915eed85469Seric 		if (current == io && IO_READING(io))
916b0e8ad58Seric 			goto again;
917d1ece852Seric 	}
918d1ece852Seric 
919d1ece852Seric     leave:
920d1ece852Seric 	io_frame_leave(io);
921d1ece852Seric }
922d1ece852Seric 
923d1ece852Seric void
io_dispatch_write_tls(int fd,short event,void * humppa)9249ac7cdfdSeric io_dispatch_write_tls(int fd, short event, void *humppa)
925d1ece852Seric {
926d1ece852Seric 	struct io	*io = humppa;
927eed85469Seric 	int		 n;
928d1ece852Seric 	size_t		 w2, w;
929d1ece852Seric 
9309ac7cdfdSeric 	io_frame_enter("io_dispatch_write_tls", io, event);
931d1ece852Seric 
932d1ece852Seric 	if (event == EV_TIMEOUT) {
933d1ece852Seric 		io_callback(io, IO_TIMEOUT);
934d1ece852Seric 		goto leave;
935d1ece852Seric 	}
936d1ece852Seric 
937d1ece852Seric 	w = io_queued(io);
938eed85469Seric 	switch ((n = iobuf_write_tls(&io->iobuf, io->tls))) {
939d1ece852Seric 	case IOBUF_WANT_READ:
9409ac7cdfdSeric 		io_reset(io, EV_READ, io_dispatch_write_tls);
941d1ece852Seric 		break;
942d1ece852Seric 	case IOBUF_WANT_WRITE:
9439ac7cdfdSeric 		io_reset(io, EV_WRITE, io_dispatch_write_tls);
944d1ece852Seric 		break;
945d1ece852Seric 	case IOBUF_CLOSED:
946d1ece852Seric 		io_callback(io, IO_DISCONNECTED);
947d1ece852Seric 		break;
948d1ece852Seric 	case IOBUF_ERROR:
949eed85469Seric 		io->error = tls_error(io->tls);
950d1ece852Seric 		io_callback(io, IO_ERROR);
951d1ece852Seric 		break;
952d1ece852Seric 	default:
9539ac7cdfdSeric 		io_debug("io_dispatch_write_tls(...) -> w=%d\n", n);
954d1ece852Seric 		w2 = io_queued(io);
955d1ece852Seric 		if (w > io->lowat && w2 <= io->lowat)
956d1ece852Seric 			io_callback(io, IO_LOWAT);
957d1ece852Seric 		break;
958d1ece852Seric 	}
959d1ece852Seric 
960d1ece852Seric     leave:
961d1ece852Seric 	io_frame_leave(io);
962d1ece852Seric }
963d1ece852Seric 
964d1ece852Seric void
io_reload_tls(struct io * io)9659ac7cdfdSeric io_reload_tls(struct io *io)
966d1ece852Seric {
967c11a901bSeric 	if (io->state != IO_STATE_UP)
968ff01b044Seric 		fatalx("io_reload_tls: bad state");
969c11a901bSeric 
970c11a901bSeric 	if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) {
971c11a901bSeric 		io_reset(io, EV_READ, io_dispatch_read_tls);
972c11a901bSeric 		return;
973d1ece852Seric 	}
974d1ece852Seric 
975c11a901bSeric 	if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io)) {
976c11a901bSeric 		io_reset(io, EV_WRITE, io_dispatch_write_tls);
977c11a901bSeric 		return;
978c11a901bSeric 	}
979c11a901bSeric 
980c11a901bSeric 	/* paused */
981d1ece852Seric }
982d1ece852Seric 
9839ac7cdfdSeric #endif /* IO_TLS */
984