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