Home | History | Annotate | Line # | Download | only in util
      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 
    163 static 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 
    265 void    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 
    294 void    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 
    323 void    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 
    332 void    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 
    341 NBBIO  *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 
    376 void    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