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