1/*	$NetBSD$	*/
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, char *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 for the
80/*	named buffer pair.  It is an error to enable a read
81/*	pseudothread while the read buffer is full, or while a read
82/*	or write pseudothread is still enabled.
83/*
84/*	nbbio_enable_write() enables a write pseudothread for the
85/*	named buffer pair.  It is an error to enable a write
86/*	pseudothread while the write buffer is empty, or while a
87/*	read or write pseudothread is still enabled.
88/*
89/*	nbbio_disable_readwrite() disables any read/write pseudothreads
90/*	for the named buffer pair, including timeouts. To ensure
91/*	buffer liveness, use nbbio_slumber() instead of
92/*	nbbio_disable_readwrite().  It is no error to call this
93/*	function while no read/write pseudothread is enabled.
94/*
95/*	nbbio_slumber() disables any read/write pseudothreads for
96/*	the named buffer pair, but keeps the timer active to ensure
97/*	buffer liveness. It is no error to call this function while
98/*	no read/write pseudothread is enabled.
99/*
100/*	NBBIO_ERROR_FLAGS() returns the error flags for the named buffer
101/*	pair: zero or more of NBBIO_FLAG_EOF (read EOF), NBBIO_FLAG_ERROR
102/*	(read/write error) or NBBIO_FLAG_TIMEOUT (time limit
103/*	exceeded).
104/*
105/*	NBBIO_ACTIVE_FLAGS() returns the pseudothread flags for the
106/*	named buffer pair: NBBIO_FLAG_READ (read pseudothread is
107/*	active), NBBIO_FLAG_WRITE (write pseudothread is active),
108/*	or zero (no pseudothread is active).
109/*
110/*	NBBIO_WRITE_PEND() and NBBIO_WRITE_BUF() evaluate to the
111/*	number of to-be-written bytes and the write buffer for the
112/*	named buffer pair. NBBIO_WRITE_PEND() must be updated by
113/*	the application code that fills the write buffer; no more
114/*	than NBBIO_BUFSIZE() bytes may be filled.
115/*
116/*	NBBIO_READ_PEND() and NBBIO_READ_BUF() evaluate to the
117/*	number of unread bytes and the read buffer for the named
118/*	buffer pair. NBBIO_READ_PEND() and NBBIO_READ_BUF() must
119/*	be updated by the application code that drains the read
120/*	buffer.
121/* SEE ALSO
122/*	events(3) event manager
123/* DIAGNOSTICS
124/*	Panic: interface violation.
125/*
126/*	Fatal: out of memory.
127/* LICENSE
128/* .ad
129/* .fi
130/*	The Secure Mailer license must be distributed with this software.
131/* AUTHOR(S)
132/*	Wietse Venema
133/*	IBM T.J. Watson Research
134/*	P.O. Box 704
135/*	Yorktown Heights, NY 10598, USA
136/*--*/
137
138 /*
139  * System library.
140  */
141#include <sys_defs.h>
142#include <unistd.h>
143#include <errno.h>
144#include <string.h>			/* memmove() */
145
146 /*
147  * Utility library.
148  */
149#include <mymalloc.h>
150#include <msg.h>
151#include <events.h>
152#include <nbbio.h>
153
154/* nbbio_event - non-blocking event handler */
155
156static void nbbio_event(int event, char *context)
157{
158    const char *myname = "nbbio_event";
159    NBBIO  *np = (NBBIO *) context;
160    ssize_t count;
161
162    switch (event) {
163
164	/*
165	 * Read data into the read buffer. Leave it up to the application to
166	 * drain the buffer until it is empty.
167	 */
168    case EVENT_READ:
169	if (np->read_pend == np->bufsize)
170	    msg_panic("%s: socket fd=%d: read buffer is full",
171		      myname, np->fd);
172	if (np->read_pend < 0 || np->read_pend > np->bufsize)
173	    msg_panic("%s: socket fd=%d: bad pending read count %ld",
174		      myname, np->fd, (long) np->read_pend);
175	count = read(np->fd, np->read_buf + np->read_pend,
176		     np->bufsize - np->read_pend);
177	if (count > 0) {
178	    np->read_pend += count;
179	    if (msg_verbose)
180		msg_info("%s: read %ld on %s fd=%d",
181			 myname, (long) count, np->label, np->fd);
182	} else if (count == 0) {
183	    np->flags |= NBBIO_FLAG_EOF;
184	    if (msg_verbose)
185		msg_info("%s: read EOF on %s fd=%d",
186			 myname, np->label, np->fd);
187	} else {
188	    if (errno == EAGAIN)
189		msg_warn("%s: read() returns EAGAIN on readable descriptor",
190			 myname);
191	    np->flags |= NBBIO_FLAG_ERROR;
192	    if (msg_verbose)
193		msg_info("%s: read %s fd=%d: %m", myname, np->label, np->fd);
194	}
195	break;
196
197	/*
198	 * Drain data from the output buffer.  Notify the application
199	 * whenever some bytes are written.
200	 *
201	 * XXX Enforce a total time limit to ensure liveness when a hostile
202	 * receiver sets a very small TCP window size.
203	 */
204    case EVENT_WRITE:
205	if (np->write_pend == 0)
206	    msg_panic("%s: socket fd=%d: empty write buffer", myname, np->fd);
207	if (np->write_pend < 0 || np->write_pend > np->bufsize)
208	    msg_panic("%s: socket fd=%d: bad pending write count %ld",
209		      myname, np->fd, (long) np->write_pend);
210	count = write(np->fd, np->write_buf, np->write_pend);
211	if (count > 0) {
212	    np->write_pend -= count;
213	    if (np->write_pend > 0)
214		memmove(np->write_buf, np->write_buf + count, np->write_pend);
215	} else {
216	    if (errno == EAGAIN)
217		msg_warn("%s: write() returns EAGAIN on writable descriptor",
218			 myname);
219	    np->flags |= NBBIO_FLAG_ERROR;
220	    if (msg_verbose)
221		msg_info("%s: write %s fd=%d: %m", myname, np->label, np->fd);
222	}
223	break;
224
225	/*
226	 * Something bad happened.
227	 */
228    case EVENT_XCPT:
229	np->flags |= NBBIO_FLAG_ERROR;
230	if (msg_verbose)
231	    msg_info("%s: error on %s fd=%d: %m", myname, np->label, np->fd);
232	break;
233
234	/*
235	 * Something good didn't happen.
236	 */
237    case EVENT_TIME:
238	np->flags |= NBBIO_FLAG_TIMEOUT;
239	if (msg_verbose)
240	    msg_info("%s: %s timeout on %s fd=%d",
241		     myname, NBBIO_OP_NAME(np), np->label, np->fd);
242	break;
243
244    default:
245	msg_panic("%s: unknown event %d", myname, event);
246    }
247
248    /*
249     * Application notification. The application will check for any error
250     * flags, copy application data from or to our buffer pair, and decide
251     * what I/O happens next.
252     */
253    np->action(event, np->context);
254}
255
256/* nbbio_enable_read - enable reading from socket into buffer */
257
258void    nbbio_enable_read(NBBIO *np, int timeout)
259{
260    const char *myname = "nbbio_enable_read";
261
262    /*
263     * Sanity checks.
264     */
265    if (np->flags & NBBIO_MASK_ACTIVE)
266	msg_panic("%s: socket fd=%d is enabled for %s",
267		  myname, np->fd, NBBIO_OP_NAME(np));
268    if (timeout <= 0)
269	msg_panic("%s: socket fd=%d: bad timeout %d",
270		  myname, np->fd, timeout);
271    if (np->read_pend >= np->bufsize)
272	msg_panic("%s: socket fd=%d: read buffer is full",
273		  myname, np->fd);
274
275    /*
276     * Enable events.
277     */
278    event_enable_read(np->fd, nbbio_event, (char *) np);
279    event_request_timer(nbbio_event, (char *) np, timeout);
280    np->flags |= NBBIO_FLAG_READ;
281}
282
283/* nbbio_enable_write - enable writing from buffer to socket */
284
285void    nbbio_enable_write(NBBIO *np, int timeout)
286{
287    const char *myname = "nbbio_enable_write";
288
289    /*
290     * Sanity checks.
291     */
292    if (np->flags & NBBIO_MASK_ACTIVE)
293	msg_panic("%s: socket fd=%d is enabled for %s",
294		  myname, np->fd, NBBIO_OP_NAME(np));
295    if (timeout <= 0)
296	msg_panic("%s: socket fd=%d bad timeout %d",
297		  myname, np->fd, timeout);
298    if (np->write_pend <= 0)
299	msg_panic("%s: socket fd=%d: empty write buffer",
300		  myname, np->fd);
301
302    /*
303     * Enable events.
304     */
305    event_enable_write(np->fd, nbbio_event, (char *) np);
306    event_request_timer(nbbio_event, (char *) np, timeout);
307    np->flags |= NBBIO_FLAG_WRITE;
308}
309
310/* nbbio_disable_readwrite - disable read/write/timer events */
311
312void    nbbio_disable_readwrite(NBBIO *np)
313{
314    np->flags &= ~NBBIO_MASK_ACTIVE;
315    event_disable_readwrite(np->fd);
316    event_cancel_timer(nbbio_event, (char *) np);
317}
318
319/* nbbio_slumber - disable read/write events, keep timer */
320
321void    nbbio_slumber(NBBIO *np, int timeout)
322{
323    np->flags &= ~NBBIO_MASK_ACTIVE;
324    event_disable_readwrite(np->fd);
325    event_request_timer(nbbio_event, (char *) np, timeout);
326}
327
328/* nbbio_create - create socket buffer */
329
330NBBIO  *nbbio_create(int fd, ssize_t bufsize, const char *label,
331		             NBBIO_ACTION action, char *context)
332{
333    NBBIO  *np;
334
335    /*
336     * Sanity checks.
337     */
338    if (fd < 0)
339	msg_panic("nbbio_create: bad file descriptor: %d", fd);
340    if (bufsize <= 0)
341	msg_panic("nbbio_create: bad buffer size: %ld", (long) bufsize);
342
343    /*
344     * Create a new buffer pair.
345     */
346    np = (NBBIO *) mymalloc(sizeof(*np));
347    np->fd = fd;
348    np->bufsize = bufsize;
349    np->label = mystrdup(label);
350    np->action = action;
351    np->context = context;
352    np->flags = 0;
353
354    np->read_buf = mymalloc(bufsize);
355    np->read_pend = 0;
356
357    np->write_buf = mymalloc(bufsize);
358    np->write_pend = 0;
359
360    return (np);
361}
362
363/* nbbio_free - destroy socket buffer */
364
365void    nbbio_free(NBBIO *np)
366{
367    nbbio_disable_readwrite(np);
368    (void) close(np->fd);
369    myfree(np->label);
370    myfree(np->read_buf);
371    myfree(np->write_buf);
372    myfree((char *) np);
373}
374