1 /* $NetBSD: nbbio.c,v 1.1.1.1 2011/03/02 19:32:44 tron 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, 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 156 static 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 258 void 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 285 void 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 312 void 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 321 void 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 330 NBBIO *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 365 void 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