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