1/*	$NetBSD: nbbio.c,v 1.3 2020/03/18 19:05:21 christos Exp $	*/
2
3/*++
4/* NAME
5/*	nbbio 3
6/* SUMMARY
7/*	non-blocking buffered I/O
8/* SYNOPSIS
9/*	#include <nbbio.h>
10/*
11/*	NBBIO	*nbbio_create(fd, bufsize, label, action, context)
12/*	int	fd;
13/*	ssize_t	bufsize;
14/*	const char *label;
15/*	void	(*action)(int event, void *context);
16/*	char	*context;
17/*
18/*	void	nbbio_free(np)
19/*	NBBIO	*np;
20/*
21/*	void	nbbio_enable_read(np, timeout)
22/*	NBBIO	*np;
23/*	int	timeout;
24/*
25/*	void	nbbio_enable_write(np, timeout)
26/*	NBBIO	*np;
27/*	int	timeout;
28/*
29/*	void	nbbio_disable_readwrite(np)
30/*	NBBIO	*np;
31/*
32/*	void	nbbio_slumber(np, timeout)
33/*	NBBIO	*np;
34/*	int	timeout;
35/*
36/*	int	NBBIO_ACTIVE_FLAGS(np)
37/*	NBBIO	*np;
38/*
39/*	int	NBBIO_ERROR_FLAGS(np)
40/*	NBBIO	*np;
41/*
42/*	const ssize_t NBBIO_BUFSIZE(np)
43/*	NBBIO	*np;
44/*
45/*	ssize_t	NBBIO_READ_PEND(np)
46/*	NBBIO	*np;
47/*
48/*	char	*NBBIO_READ_BUF(np)
49/*	NBBIO	*np;
50/*
51/*	const ssize_t NBBIO_WRITE_PEND(np)
52/*	NBBIO	*np;
53/*
54/*	char	*NBBIO_WRITE_BUF(np)
55/*	NBBIO	*np;
56/* DESCRIPTION
57/*	This module implements low-level support for event-driven
58/*	I/O on a full-duplex stream. Read/write events are handled
59/*	by pseudothreads that run under control by the events(5)
60/*	module.  After each I/O operation, the application is
61/*	notified via a call-back routine.
62/*
63/*	It is up to the call-back routine to turn on/off read/write
64/*	events as appropriate.  It is an error to leave read events
65/*	enabled for a buffer that is full, or to leave write events
66/*	enabled for a buffer that is empty.
67/*
68/*	nbbio_create() creates a pair of buffers of the named size
69/*	for the named stream. The label specifies the purpose of
70/*	the stream, and is used for diagnostic messages.  The
71/*	nbbio(3) event handler invokes the application call-back
72/*	routine with the current event type (EVENT_READ etc.) and
73/*	with the application-specified context.
74/*
75/*	nbbio_free() terminates any pseudothreads associated with
76/*	the named buffer pair, closes the stream, and destroys the
77/*	buffer pair.
78/*
79/*	nbbio_enable_read() enables a read pseudothread (if one
80/*	does not already exist) for the named buffer pair, and
81/*	(re)starts the buffer pair's timer. It is an error to enable
82/*	a read pseudothread while the read buffer is full, or while
83/*	a write pseudothread is still enabled.
84/*
85/*	nbbio_enable_write() enables a write pseudothread (if one
86/*	does not already exist) for the named buffer pair, and
87/*	(re)starts the buffer pair's timer. It is an error to enable
88/*	a write pseudothread while the write buffer is empty, or
89/*	while a read pseudothread is still enabled.
90/*
91/*	nbbio_disable_readwrite() disables any read/write pseudothreads
92/*	for the named buffer pair, including timeouts. To ensure
93/*	buffer liveness, use nbbio_slumber() instead of
94/*	nbbio_disable_readwrite().  It is no error to call this
95/*	function while no read/write pseudothread is enabled.
96/*
97/*	nbbio_slumber() disables any read/write pseudothreads for
98/*	the named buffer pair, but keeps the timer active to ensure
99/*	buffer liveness. It is no error to call this function while
100/*	no read/write pseudothread is enabled.
101/*
102/*	NBBIO_ERROR_FLAGS() returns the error flags for the named buffer
103/*	pair: zero or more of NBBIO_FLAG_EOF (read EOF), NBBIO_FLAG_ERROR
104/*	(read/write error) or NBBIO_FLAG_TIMEOUT (time limit
105/*	exceeded).
106/*
107/*	NBBIO_ACTIVE_FLAGS() returns the pseudothread flags for the
108/*	named buffer pair: NBBIO_FLAG_READ (read pseudothread is
109/*	active), NBBIO_FLAG_WRITE (write pseudothread is active),
110/*	or zero (no pseudothread is active).
111/*
112/*	NBBIO_WRITE_PEND() and NBBIO_WRITE_BUF() evaluate to the
113/*	number of to-be-written bytes and the write buffer for the
114/*	named buffer pair. NBBIO_WRITE_PEND() must be updated by
115/*	the application code that fills the write buffer; no more
116/*	than NBBIO_BUFSIZE() bytes may be filled.
117/*
118/*	NBBIO_READ_PEND() and NBBIO_READ_BUF() evaluate to the
119/*	number of unread bytes and the read buffer for the named
120/*	buffer pair. NBBIO_READ_PEND() and NBBIO_READ_BUF() must
121/*	be updated by the application code that drains the read
122/*	buffer.
123/* SEE ALSO
124/*	events(3) event manager
125/* DIAGNOSTICS
126/*	Panic: interface violation.
127/*
128/*	Fatal: out of memory.
129/* LICENSE
130/* .ad
131/* .fi
132/*	The Secure Mailer license must be distributed with this software.
133/* AUTHOR(S)
134/*	Wietse Venema
135/*	IBM T.J. Watson Research
136/*	P.O. Box 704
137/*	Yorktown Heights, NY 10598, USA
138/*
139/*	Wietse Venema
140/*	Google, Inc.
141/*	111 8th Avenue
142/*	New York, NY 10011, USA
143/*--*/
144
145 /*
146  * System library.
147  */
148#include <sys_defs.h>
149#include <unistd.h>
150#include <errno.h>
151#include <string.h>			/* memmove() */
152
153 /*
154  * Utility library.
155  */
156#include <mymalloc.h>
157#include <msg.h>
158#include <events.h>
159#include <nbbio.h>
160
161/* nbbio_event - non-blocking event handler */
162
163static void nbbio_event(int event, void *context)
164{
165    const char *myname = "nbbio_event";
166    NBBIO  *np = (NBBIO *) context;
167    ssize_t count;
168
169    switch (event) {
170
171	/*
172	 * Read data into the read buffer. Leave it up to the application to
173	 * drain the buffer until it is empty.
174	 */
175    case EVENT_READ:
176	if (np->read_pend == np->bufsize)
177	    msg_panic("%s: socket fd=%d: read buffer is full",
178		      myname, np->fd);
179	if (np->read_pend < 0 || np->read_pend > np->bufsize)
180	    msg_panic("%s: socket fd=%d: bad pending read count %ld",
181		      myname, np->fd, (long) np->read_pend);
182	count = read(np->fd, np->read_buf + np->read_pend,
183		     np->bufsize - np->read_pend);
184	if (count > 0) {
185	    np->read_pend += count;
186	    if (msg_verbose)
187		msg_info("%s: read %ld on %s fd=%d",
188			 myname, (long) count, np->label, np->fd);
189	} else if (count == 0) {
190	    np->flags |= NBBIO_FLAG_EOF;
191	    if (msg_verbose)
192		msg_info("%s: read EOF on %s fd=%d",
193			 myname, np->label, np->fd);
194	} else {
195	    if (errno == EAGAIN)
196		msg_warn("%s: read() returns EAGAIN on readable descriptor",
197			 myname);
198	    np->flags |= NBBIO_FLAG_ERROR;
199	    if (msg_verbose)
200		msg_info("%s: read %s fd=%d: %m", myname, np->label, np->fd);
201	}
202	break;
203
204	/*
205	 * Drain data from the output buffer.  Notify the application
206	 * whenever some bytes are written.
207	 *
208	 * XXX Enforce a total time limit to ensure liveness when a hostile
209	 * receiver sets a very small TCP window size.
210	 */
211    case EVENT_WRITE:
212	if (np->write_pend == 0)
213	    msg_panic("%s: socket fd=%d: empty write buffer", myname, np->fd);
214	if (np->write_pend < 0 || np->write_pend > np->bufsize)
215	    msg_panic("%s: socket fd=%d: bad pending write count %ld",
216		      myname, np->fd, (long) np->write_pend);
217	count = write(np->fd, np->write_buf, np->write_pend);
218	if (count > 0) {
219	    np->write_pend -= count;
220	    if (np->write_pend > 0)
221		memmove(np->write_buf, np->write_buf + count, np->write_pend);
222	} else {
223	    if (errno == EAGAIN)
224		msg_warn("%s: write() returns EAGAIN on writable descriptor",
225			 myname);
226	    np->flags |= NBBIO_FLAG_ERROR;
227	    if (msg_verbose)
228		msg_info("%s: write %s fd=%d: %m", myname, np->label, np->fd);
229	}
230	break;
231
232	/*
233	 * Something bad happened.
234	 */
235    case EVENT_XCPT:
236	np->flags |= NBBIO_FLAG_ERROR;
237	if (msg_verbose)
238	    msg_info("%s: error on %s fd=%d: %m", myname, np->label, np->fd);
239	break;
240
241	/*
242	 * Something good didn't happen.
243	 */
244    case EVENT_TIME:
245	np->flags |= NBBIO_FLAG_TIMEOUT;
246	if (msg_verbose)
247	    msg_info("%s: %s timeout on %s fd=%d",
248		     myname, NBBIO_OP_NAME(np), np->label, np->fd);
249	break;
250
251    default:
252	msg_panic("%s: unknown event %d", myname, event);
253    }
254
255    /*
256     * Application notification. The application will check for any error
257     * flags, copy application data from or to our buffer pair, and decide
258     * what I/O happens next.
259     */
260    np->action(event, np->context);
261}
262
263/* nbbio_enable_read - enable reading from socket into buffer */
264
265void    nbbio_enable_read(NBBIO *np, int timeout)
266{
267    const char *myname = "nbbio_enable_read";
268
269    /*
270     * Sanity checks.
271     */
272    if (np->flags & (NBBIO_MASK_ACTIVE & ~NBBIO_FLAG_READ))
273	msg_panic("%s: socket fd=%d is enabled for %s",
274		  myname, np->fd, NBBIO_OP_NAME(np));
275    if (timeout <= 0)
276	msg_panic("%s: socket fd=%d: bad timeout %d",
277		  myname, np->fd, timeout);
278    if (np->read_pend >= np->bufsize)
279	msg_panic("%s: socket fd=%d: read buffer is full",
280		  myname, np->fd);
281
282    /*
283     * Enable events.
284     */
285    if ((np->flags & NBBIO_FLAG_READ) == 0) {
286	event_enable_read(np->fd, nbbio_event, (void *) np);
287	np->flags |= NBBIO_FLAG_READ;
288    }
289    event_request_timer(nbbio_event, (void *) np, timeout);
290}
291
292/* nbbio_enable_write - enable writing from buffer to socket */
293
294void    nbbio_enable_write(NBBIO *np, int timeout)
295{
296    const char *myname = "nbbio_enable_write";
297
298    /*
299     * Sanity checks.
300     */
301    if (np->flags & (NBBIO_MASK_ACTIVE & ~NBBIO_FLAG_WRITE))
302	msg_panic("%s: socket fd=%d is enabled for %s",
303		  myname, np->fd, NBBIO_OP_NAME(np));
304    if (timeout <= 0)
305	msg_panic("%s: socket fd=%d: bad timeout %d",
306		  myname, np->fd, timeout);
307    if (np->write_pend <= 0)
308	msg_panic("%s: socket fd=%d: empty write buffer",
309		  myname, np->fd);
310
311    /*
312     * Enable events.
313     */
314    if ((np->flags & NBBIO_FLAG_WRITE) == 0) {
315	event_enable_write(np->fd, nbbio_event, (void *) np);
316	np->flags |= NBBIO_FLAG_WRITE;
317    }
318    event_request_timer(nbbio_event, (void *) np, timeout);
319}
320
321/* nbbio_disable_readwrite - disable read/write/timer events */
322
323void    nbbio_disable_readwrite(NBBIO *np)
324{
325    np->flags &= ~NBBIO_MASK_ACTIVE;
326    event_disable_readwrite(np->fd);
327    event_cancel_timer(nbbio_event, (void *) np);
328}
329
330/* nbbio_slumber - disable read/write events, keep timer */
331
332void    nbbio_slumber(NBBIO *np, int timeout)
333{
334    np->flags &= ~NBBIO_MASK_ACTIVE;
335    event_disable_readwrite(np->fd);
336    event_request_timer(nbbio_event, (void *) np, timeout);
337}
338
339/* nbbio_create - create socket buffer */
340
341NBBIO  *nbbio_create(int fd, ssize_t bufsize, const char *label,
342		             NBBIO_ACTION action, void *context)
343{
344    NBBIO  *np;
345
346    /*
347     * Sanity checks.
348     */
349    if (fd < 0)
350	msg_panic("nbbio_create: bad file descriptor: %d", fd);
351    if (bufsize <= 0)
352	msg_panic("nbbio_create: bad buffer size: %ld", (long) bufsize);
353
354    /*
355     * Create a new buffer pair.
356     */
357    np = (NBBIO *) mymalloc(sizeof(*np));
358    np->fd = fd;
359    np->bufsize = bufsize;
360    np->label = mystrdup(label);
361    np->action = action;
362    np->context = context;
363    np->flags = 0;
364
365    np->read_buf = mymalloc(bufsize);
366    np->read_pend = 0;
367
368    np->write_buf = mymalloc(bufsize);
369    np->write_pend = 0;
370
371    return (np);
372}
373
374/* nbbio_free - destroy socket buffer */
375
376void    nbbio_free(NBBIO *np)
377{
378    nbbio_disable_readwrite(np);
379    (void) close(np->fd);
380    myfree(np->label);
381    myfree(np->read_buf);
382    myfree(np->write_buf);
383    myfree((void *) np);
384}
385