1/* $NetBSD$ */ 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 156static 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 258void 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 285void 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 312void 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 321void 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 330NBBIO *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 365void 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