1 /* $NetBSD: vstream.c,v 1.1.1.4 2013/09/25 19:06:38 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* vstream 3 6 /* SUMMARY 7 /* light-weight buffered I/O package 8 /* SYNOPSIS 9 /* #include <vstream.h> 10 /* 11 /* VSTREAM *vstream_fopen(path, flags, mode) 12 /* const char *path; 13 /* int flags; 14 /* mode_t mode; 15 /* 16 /* VSTREAM *vstream_fdopen(fd, flags) 17 /* int fd; 18 /* int flags; 19 /* 20 /* int vstream_fclose(stream) 21 /* VSTREAM *stream; 22 /* 23 /* int vstream_fdclose(stream) 24 /* VSTREAM *stream; 25 /* 26 /* VSTREAM *vstream_printf(format, ...) 27 /* const char *format; 28 /* 29 /* VSTREAM *vstream_fprintf(stream, format, ...) 30 /* VSTREAM *stream; 31 /* const char *format; 32 /* 33 /* int VSTREAM_GETC(stream) 34 /* VSTREAM *stream; 35 /* 36 /* int VSTREAM_PUTC(ch, stream) 37 /* int ch; 38 /* 39 /* int VSTREAM_GETCHAR(void) 40 /* 41 /* int VSTREAM_PUTCHAR(ch) 42 /* int ch; 43 /* 44 /* int vstream_ungetc(stream, ch) 45 /* VSTREAM *stream; 46 /* int ch; 47 /* 48 /* int vstream_fputs(str, stream) 49 /* const char *str; 50 /* VSTREAM *stream; 51 /* 52 /* off_t vstream_ftell(stream) 53 /* VSTREAM *stream; 54 /* 55 /* off_t vstream_fseek(stream, offset, whence) 56 /* VSTREAM *stream; 57 /* off_t offset; 58 /* int whence; 59 /* 60 /* int vstream_fflush(stream) 61 /* VSTREAM *stream; 62 /* 63 /* int vstream_fpurge(stream, direction) 64 /* VSTREAM *stream; 65 /* int direction; 66 /* 67 /* ssize_t vstream_fread(stream, buf, len) 68 /* VSTREAM *stream; 69 /* char *buf; 70 /* ssize_t len; 71 /* 72 /* ssize_t vstream_fwrite(stream, buf, len) 73 /* VSTREAM *stream; 74 /* const char *buf; 75 /* ssize_t len; 76 /* 77 /* void vstream_control(stream, name, ...) 78 /* VSTREAM *stream; 79 /* int name; 80 /* 81 /* int vstream_fileno(stream) 82 /* VSTREAM *stream; 83 /* 84 /* const ssize_t vstream_req_bufsize(stream) 85 /* VSTREAM *stream; 86 /* 87 /* void *vstream_context(stream) 88 /* VSTREAM *stream; 89 /* 90 /* int vstream_ferror(stream) 91 /* VSTREAM *stream; 92 /* 93 /* int vstream_ftimeout(stream) 94 /* VSTREAM *stream; 95 /* 96 /* int vstream_feof(stream) 97 /* VSTREAM *stream; 98 /* 99 /* int vstream_clearerr(stream) 100 /* VSTREAM *stream; 101 /* 102 /* const char *VSTREAM_PATH(stream) 103 /* VSTREAM *stream; 104 /* 105 /* char *vstream_vprintf(format, ap) 106 /* const char *format; 107 /* va_list *ap; 108 /* 109 /* char *vstream_vfprintf(stream, format, ap) 110 /* VSTREAM *stream; 111 /* const char *format; 112 /* va_list *ap; 113 /* 114 /* ssize_t vstream_bufstat(stream, command) 115 /* VSTREAM *stream; 116 /* int command; 117 /* 118 /* ssize_t vstream_peek(stream) 119 /* VSTREAM *stream; 120 /* 121 /* const char *vstream_peek_data(stream) 122 /* VSTREAM *stream; 123 /* 124 /* int vstream_setjmp(stream) 125 /* VSTREAM *stream; 126 /* 127 /* void vstream_longjmp(stream, val) 128 /* VSTREAM *stream; 129 /* int val; 130 /* 131 /* time_t vstream_ftime(stream) 132 /* VSTREAM *stream; 133 /* 134 /* struct timeval vstream_ftimeval(stream) 135 /* VSTREAM *stream; 136 /* 137 /* int vstream_rd_error(stream) 138 /* VSTREAM *stream; 139 /* 140 /* int vstream_wr_error(stream) 141 /* VSTREAM *stream; 142 /* 143 /* int vstream_rd_timeout(stream) 144 /* VSTREAM *stream; 145 /* 146 /* int vstream_wr_timeout(stream) 147 /* VSTREAM *stream; 148 /* 149 /* int vstream_fstat(stream, flags) 150 /* VSTREAM *stream; 151 /* int flags; 152 /* DESCRIPTION 153 /* The \fIvstream\fR module implements light-weight buffered I/O 154 /* similar to the standard I/O routines. 155 /* 156 /* The interface is implemented in terms of VSTREAM structure 157 /* pointers, also called streams. For convenience, three streams 158 /* are predefined: VSTREAM_IN, VSTREAM_OUT, and VSTREAM_ERR. These 159 /* streams are connected to the standard input, output and error 160 /* file descriptors, respectively. 161 /* 162 /* Although the interface is patterned after the standard I/O 163 /* library, there are some major differences: 164 /* .IP \(bu 165 /* File descriptors are not limited to the range 0..255. This 166 /* was reason #1 to write these routines in the first place. 167 /* .IP \(bu 168 /* The application can switch between reading and writing on 169 /* the same stream without having to perform a flush or seek 170 /* operation, and can change write position without having to 171 /* flush. This was reason #2. Upon position or direction change, 172 /* unread input is discarded, and unwritten output is flushed 173 /* automatically. Exception: with double-buffered streams, unread 174 /* input is not discarded upon change of I/O direction, and 175 /* output flushing is delayed until the read buffer must be refilled. 176 /* .IP \(bu 177 /* A bidirectional stream can read and write with the same buffer 178 /* and file descriptor, or it can have separate read/write 179 /* buffers and/or file descriptors. 180 /* .IP \(bu 181 /* No automatic flushing of VSTREAM_OUT upon program exit, or of 182 /* VSTREAM_ERR at any time. No unbuffered or line buffered modes. 183 /* This functionality may be added when it is really needed. 184 /* .PP 185 /* vstream_fopen() opens the named file and associates a buffered 186 /* stream with it. The \fIpath\fR, \fIflags\fR and \fImode\fR 187 /* arguments are passed on to the open(2) routine. The result is 188 /* a null pointer in case of problems. The \fIpath\fR argument is 189 /* copied and can be looked up with VSTREAM_PATH(). 190 /* 191 /* vstream_fdopen() takes an open file and associates a buffered 192 /* stream with it. The \fIflags\fR argument specifies how the file 193 /* was opened. vstream_fdopen() either succeeds or never returns. 194 /* 195 /* vstream_fclose() closes the named buffered stream. The result 196 /* is 0 in case of success, VSTREAM_EOF in case of problems. 197 /* vstream_fclose() reports the same errors as vstream_ferror(). 198 /* 199 /* vstream_fdclose() leaves the file(s) open but is otherwise 200 /* identical to vstream_fclose(). 201 /* 202 /* vstream_fprintf() formats its arguments according to the 203 /* \fIformat\fR argument and writes the result to the named stream. 204 /* The result is the stream argument. It understands the s, c, d, u, 205 /* o, x, X, e, f and g format types, the l modifier, field width and 206 /* precision, sign, and padding with zeros or spaces. In addition, 207 /* vstream_fprintf() recognizes the %m format specifier and expands 208 /* it to the error message corresponding to the current value of the 209 /* global \fIerrno\fR variable. 210 /* 211 /* vstream_printf() performs formatted output to the standard output 212 /* stream. 213 /* 214 /* VSTREAM_GETC() reads the next character from the named stream. 215 /* The result is VSTREAM_EOF when end-of-file is reached or if a read 216 /* error was detected. VSTREAM_GETC() is an unsafe macro that 217 /* evaluates some arguments more than once. 218 /* 219 /* VSTREAM_GETCHAR() is an alias for VSTREAM_GETC(VSTREAM_IN). 220 /* 221 /* VSTREAM_PUTC() appends the specified character to the specified 222 /* stream. The result is the stored character, or VSTREAM_EOF in 223 /* case of problems. VSTREAM_PUTC() is an unsafe macro that 224 /* evaluates some arguments more than once. 225 /* 226 /* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT). 227 /* 228 /* vstream_ungetc() pushes back a character onto the specified stream 229 /* and returns the character, or VSTREAM_EOF in case of problems. 230 /* It is an error to push back before reading (or immediately after 231 /* changing the stream offset via vstream_fseek()). Upon successful 232 /* return, vstream_ungetc() clears the end-of-file stream flag. 233 /* 234 /* vstream_fputs() appends the given null-terminated string to the 235 /* specified buffered stream. The result is 0 in case of success, 236 /* VSTREAM_EOF in case of problems. 237 /* 238 /* vstream_ftell() returns the file offset for the specified stream, 239 /* -1 if the stream is connected to a non-seekable file. 240 /* 241 /* vstream_fseek() changes the file position for the next read or write 242 /* operation. Unwritten output is flushed. With unidirectional streams, 243 /* unread input is discarded. The \fIoffset\fR argument specifies the file 244 /* position from the beginning of the file (\fIwhence\fR is SEEK_SET), 245 /* from the current file position (\fIwhence\fR is SEEK_CUR), or from 246 /* the file end (SEEK_END). The result value is the file offset 247 /* from the beginning of the file, -1 in case of problems. 248 /* 249 /* vstream_fflush() flushes unwritten data to a file that was 250 /* opened in read-write or write-only mode. 251 /* vstream_fflush() returns 0 in case of success, VSTREAM_EOF in 252 /* case of problems. It is an error to flush a read-only stream. 253 /* vstream_fflush() reports the same errors as vstream_ferror(). 254 /* 255 /* vstream_fpurge() discards the contents of the stream buffer. 256 /* If direction is VSTREAM_PURGE_READ, it discards unread data, 257 /* else if direction is VSTREAM_PURGE_WRITE, it discards unwritten 258 /* data. In the case of a double-buffered stream, if direction is 259 /* VSTREAM_PURGE_BOTH, it discards the content of both the read 260 /* and write buffers. vstream_fpurge() returns 0 in case of success, 261 /* VSTREAM_EOF in case of problems. 262 /* 263 /* vstream_fread() and vstream_fwrite() perform unformatted I/O 264 /* on the named stream. The result value is the number of bytes 265 /* transferred. A short count is returned in case of end-of-file 266 /* or error conditions. 267 /* 268 /* vstream_control() allows the user to fine tune the behavior of 269 /* the specified stream. The arguments are a list of (name, 270 /* value) pairs, terminated with VSTREAM_CTL_END. 271 /* The following lists the names and the types of the corresponding 272 /* value arguments. 273 /* .IP "VSTREAM_CTL_READ_FN (ssize_t (*)(int, void *, size_t, int, void *))" 274 /* The argument specifies an alternative for the timed_read(3) function, 275 /* for example, a read function that performs decryption. 276 /* This function receives as arguments a file descriptor, buffer pointer, 277 /* buffer length, timeout value, and the VSTREAM's context value. 278 /* A timeout value <= 0 disables the time limit. 279 /* This function should return the positive number of bytes transferred, 280 /* 0 upon EOF, and -1 upon error with errno set appropriately. 281 /* .IP "VSTREAM_CTL_WRITE_FN (ssize_t (*)(int, void *, size_t, int, void *))" 282 /* The argument specifies an alternative for the timed_write(3) function, 283 /* for example, a write function that performs encryption. 284 /* This function receives as arguments a file descriptor, buffer pointer, 285 /* buffer length, timeout value, and the VSTREAM's context value. 286 /* A timeout value <= 0 disables the time limit. 287 /* This function should return the positive number of bytes transferred, 288 /* and -1 upon error with errno set appropriately. Instead of -1 it may 289 /* also return 0, e.g., upon remote party-initiated protocol shutdown. 290 /* .IP "VSTREAM_CTL_CONTEXT (char *)" 291 /* The argument specifies application context that is passed on to 292 /* the application-specified read/write routines. No copy is made. 293 /* .IP "VSTREAM_CTL_PATH (char *)" 294 /* Updates the stored pathname of the specified stream. The pathname 295 /* is copied. 296 /* .IP "VSTREAM_CTL_DOUBLE (no value)" 297 /* Use separate buffers for reading and for writing. This prevents 298 /* unread input from being discarded upon change of I/O direction. 299 /* .IP "VSTREAM_CTL_READ_FD (int) 300 /* The argument specifies the file descriptor to be used for reading. 301 /* This feature is limited to double-buffered streams, and makes the 302 /* stream non-seekable. 303 /* .IP "VSTREAM_CTL_WRITE_FD (int) 304 /* The argument specifies the file descriptor to be used for writing. 305 /* This feature is limited to double-buffered streams, and makes the 306 /* stream non-seekable. 307 /* .IP "VSTREAM_CTL_SWAP_FD (VSTREAM *)" 308 /* The argument specifies a VSTREAM pointer; the request swaps the 309 /* file descriptor members of the two streams. This feature is limited 310 /* to streams that are both double-buffered or both single-buffered. 311 /* .IP "VSTREAM_CTL_DUPFD (int)" 312 /* The argument specifies a minimum file descriptor value. If 313 /* the actual stream's file descriptors are below the minimum, 314 /* reallocate the descriptors to the first free value greater 315 /* than or equal to the minimum. The VSTREAM_CTL_DUPFD macro 316 /* is defined only on systems with fcntl() F_DUPFD support. 317 /* .IP "VSTREAM_CTL_WAITPID_FN (int (*)(pid_t, WAIT_STATUS_T *, int))" 318 /* A pointer to function that behaves like waitpid(). This information 319 /* is used by the vstream_pclose() routine. 320 /* .IP "VSTREAM_CTL_TIMEOUT (int) 321 /* The deadline for a descriptor to become readable in case of a read 322 /* request, or writable in case of a write request. Specify a value 323 /* of 0 to disable deadlines. 324 /* .IP "VSTREAM_CTL_EXCEPT (no value)" 325 /* Enable exception handling with vstream_setjmp() and vstream_longjmp(). 326 /* This involves allocation of additional memory that normally isn't 327 /* used. 328 /* .IP "VSTREAM_CTL_BUFSIZE (ssize_t)" 329 /* Specify a non-default buffer size for the next read(2) or 330 /* write(2) operation, or zero to implement a no-op. Requests 331 /* to reduce the buffer size are silently ignored (i.e. any 332 /* positive value <= vstream_req_bufsize()). To get a buffer 333 /* size smaller than VSTREAM_BUFSIZE, make the VSTREAM_CTL_BUFSIZE 334 /* request before the first stream read or write operation 335 /* (i.e., vstream_req_bufsize() returns zero). Requests to 336 /* change a fixed-size buffer (i.e., VSTREAM_ERR) are not 337 /* allowed. 338 /* 339 /* NOTE: the vstream_*printf() routines may silently expand a 340 /* buffer, so that the result of some %letter specifiers can 341 /* be written to contiguous memory. 342 /* 343 /* NOTE: the VSTREAM_CTL_BUFSIZE argument type is ssize_t, not 344 /* int. Use an explicit cast to avoid problems on LP64 345 /* environments and other environments where ssize_t is larger 346 /* than int. 347 /* .IP VSTREAM_CTL_START_DEADLINE 348 /* Change the VSTREAM_CTL_TIMEOUT behavior, to limit the total 349 /* time for all subsequent file descriptor read or write 350 /* operations, and recharge the deadline timer. 351 /* .IP VSTREAM_CTL_STOP_DEADLINE 352 /* Revert VSTREAM_CTL_TIMEOUT behavior to the default, i.e. 353 /* a time limit for individual file descriptor read or write 354 /* operations. 355 /* .PP 356 /* vstream_fileno() gives access to the file handle associated with 357 /* a buffered stream. With streams that have separate read/write 358 /* file descriptors, the result is the current descriptor. 359 /* 360 /* vstream_req_bufsize() returns the buffer size that will be 361 /* used for the next read(2) or write(2) operation on the named 362 /* stream. A zero result means that the next read(2) or write(2) 363 /* operation will use the default buffer size (VSTREAM_BUFSIZE). 364 /* 365 /* vstream_context() returns the application context that is passed on to 366 /* the application-specified read/write routines. 367 /* 368 /* VSTREAM_PATH() is an unsafe macro that returns the name stored 369 /* with vstream_fopen() or with vstream_control(). The macro is 370 /* unsafe because it evaluates some arguments more than once. 371 /* 372 /* vstream_feof() returns non-zero when a previous operation on the 373 /* specified stream caused an end-of-file condition. 374 /* Although further read requests after EOF may complete 375 /* succesfully, vstream_feof() will keep returning non-zero 376 /* until vstream_clearerr() is called for that stream. 377 /* 378 /* vstream_ferror() returns non-zero when a previous operation on the 379 /* specified stream caused a non-EOF error condition, including timeout. 380 /* After a non-EOF error on a stream, no I/O request will 381 /* complete until after vstream_clearerr() is called for that stream. 382 /* 383 /* vstream_ftimeout() returns non-zero when a previous operation on the 384 /* specified stream caused a timeout error condition. See 385 /* vstream_ferror() for error persistence details. 386 /* 387 /* vstream_clearerr() resets the timeout, error and end-of-file indication 388 /* of the specified stream, and returns no useful result. 389 /* 390 /* vstream_vfprintf() provides an alternate interface 391 /* for formatting an argument list according to a format string. 392 /* 393 /* vstream_vprintf() provides a similar alternative interface. 394 /* 395 /* vstream_bufstat() provides input and output buffer status 396 /* information. The command is one of the following: 397 /* .IP VSTREAM_BST_IN_PEND 398 /* Return the number of characters that can be read without 399 /* refilling the read buffer. 400 /* .IP VSTREAM_BST_OUT_PEND 401 /* Return the number of characters that are waiting in the 402 /* write buffer. 403 /* .PP 404 /* vstream_peek() returns the number of characters that can be 405 /* read from the named stream without refilling the read buffer. 406 /* This is an alias for vstream_bufstat(stream, VSTREAM_BST_IN_PEND). 407 /* 408 /* vstream_peek_data() returns a pointer to the unread bytes 409 /* that exist according to vstream_peek(). 410 /* 411 /* vstream_setjmp() saves processing context and makes that context 412 /* available for use with vstream_longjmp(). Normally, vstream_setjmp() 413 /* returns zero. A non-zero result means that vstream_setjmp() returned 414 /* through a vstream_longjmp() call; the result is the \fIval\fR argment 415 /* given to vstream_longjmp(). 416 /* 417 /* NB: non-local jumps such as vstream_longjmp() are not safe 418 /* for jumping out of any routine that manipulates VSTREAM data. 419 /* longjmp() like calls are best avoided in signal handlers. 420 /* 421 /* vstream_ftime() returns the time of initialization, the last buffer 422 /* fill operation, or the last buffer flush operation for the specified 423 /* stream. This information is maintained only when stream timeouts are 424 /* enabled. 425 /* 426 /* vstream_ftimeval() is like vstream_ftime() but returns more 427 /* detail. 428 /* 429 /* vstream_rd_mumble() and vstream_wr_mumble() report on 430 /* read and write error conditions, respectively. 431 /* 432 /* vstream_fstat() queries stream status information about 433 /* user-requested features. The \fIflags\fR argument is the 434 /* bitwise OR of one or more of the following, and the result 435 /* value is the bitwise OR of the features that are activated. 436 /* .IP VSTREAM_FLAG_DEADLINE 437 /* The deadline feature is activated. 438 /* .IP VSTREAM_FLAG_DOUBLE 439 /* The double-buffering feature is activated. 440 /* DIAGNOSTICS 441 /* Panics: interface violations. Fatal errors: out of memory. 442 /* SEE ALSO 443 /* timed_read(3) default read routine 444 /* timed_write(3) default write routine 445 /* vbuf_print(3) formatting engine 446 /* setjmp(3) non-local jumps 447 /* BUGS 448 /* Should use mmap() on reasonable systems. 449 /* LICENSE 450 /* .ad 451 /* .fi 452 /* The Secure Mailer license must be distributed with this software. 453 /* AUTHOR(S) 454 /* Wietse Venema 455 /* IBM T.J. Watson Research 456 /* P.O. Box 704 457 /* Yorktown Heights, NY 10598, USA 458 /*--*/ 459 460 /* System library. */ 461 462 #include <sys_defs.h> 463 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */ 464 #include <stdarg.h> 465 #include <stddef.h> 466 #include <unistd.h> 467 #include <fcntl.h> 468 #include <time.h> 469 #include <errno.h> 470 #include <string.h> 471 472 /* Utility library. */ 473 474 #include "mymalloc.h" 475 #include "msg.h" 476 #include "vbuf_print.h" 477 #include "iostuff.h" 478 #include "vstring.h" 479 #include "vstream.h" 480 481 /* Application-specific. */ 482 483 /* 484 * Forward declarations. 485 */ 486 static int vstream_buf_get_ready(VBUF *); 487 static int vstream_buf_put_ready(VBUF *); 488 static int vstream_buf_space(VBUF *, ssize_t); 489 490 /* 491 * Initialization of the three pre-defined streams. Pre-allocate a static 492 * I/O buffer for the standard error stream, so that the error handler can 493 * produce a diagnostic even when memory allocation fails. 494 */ 495 static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE]; 496 497 VSTREAM vstream_fstd[] = { 498 {{ 499 0, /* flags */ 500 0, 0, 0, 0, /* buffer */ 501 vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space, 502 }, STDIN_FILENO, (VSTREAM_FN) timed_read, (VSTREAM_FN) timed_write, 503 0,}, 504 {{ 505 0, /* flags */ 506 0, 0, 0, 0, /* buffer */ 507 vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space, 508 }, STDOUT_FILENO, (VSTREAM_FN) timed_read, (VSTREAM_FN) timed_write, 509 0,}, 510 {{ 511 VBUF_FLAG_FIXED | VSTREAM_FLAG_WRITE, 512 vstream_fstd_buf, VSTREAM_BUFSIZE, VSTREAM_BUFSIZE, vstream_fstd_buf, 513 vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space, 514 }, STDERR_FILENO, (VSTREAM_FN) timed_read, (VSTREAM_FN) timed_write, 515 VSTREAM_BUFSIZE,}, 516 }; 517 518 #define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR) 519 520 /* 521 * A bunch of macros to make some expressions more readable. XXX We're 522 * assuming that O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2. 523 */ 524 #define VSTREAM_ACC_MASK(f) ((f) & (O_APPEND | O_WRONLY | O_RDWR)) 525 526 #define VSTREAM_CAN_READ(f) (VSTREAM_ACC_MASK(f) == O_RDONLY \ 527 || VSTREAM_ACC_MASK(f) == O_RDWR) 528 #define VSTREAM_CAN_WRITE(f) (VSTREAM_ACC_MASK(f) & O_WRONLY \ 529 || VSTREAM_ACC_MASK(f) & O_RDWR \ 530 || VSTREAM_ACC_MASK(f) & O_APPEND) 531 532 #define VSTREAM_BUF_COUNT(bp, n) \ 533 ((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n)) 534 535 #define VSTREAM_BUF_AT_START(bp) { \ 536 (bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \ 537 (bp)->ptr = (bp)->data; \ 538 } 539 540 #define VSTREAM_BUF_AT_OFFSET(bp, offset) { \ 541 (bp)->ptr = (bp)->data + (offset); \ 542 (bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \ 543 } 544 545 #define VSTREAM_BUF_AT_END(bp) { \ 546 (bp)->cnt = 0; \ 547 (bp)->ptr = (bp)->data + (bp)->len; \ 548 } 549 550 #define VSTREAM_BUF_ZERO(bp) { \ 551 (bp)->flags = 0; \ 552 (bp)->data = (bp)->ptr = 0; \ 553 (bp)->len = (bp)->cnt = 0; \ 554 } 555 556 #define VSTREAM_BUF_ACTIONS(bp, get_action, put_action, space_action) { \ 557 (bp)->get_ready = (get_action); \ 558 (bp)->put_ready = (put_action); \ 559 (bp)->space = (space_action); \ 560 } 561 562 #define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \ 563 stream->buffer = stream->buf; \ 564 stream->filedes = stream->fd; \ 565 } 566 567 #define VSTREAM_RESTORE_STATE(stream, buffer, filedes) do { \ 568 stream->buffer.flags = stream->buf.flags; \ 569 stream->buf = stream->buffer; \ 570 stream->fd = stream->filedes; \ 571 } while(0) 572 573 #define VSTREAM_FORK_STATE(stream, buffer, filedes) { \ 574 stream->buffer = stream->buf; \ 575 stream->filedes = stream->fd; \ 576 stream->buffer.data = stream->buffer.ptr = 0; \ 577 stream->buffer.len = stream->buffer.cnt = 0; \ 578 stream->buffer.flags &= ~VSTREAM_FLAG_FIXED; \ 579 }; 580 581 #define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE) 582 #define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE) 583 584 #define VSTREAM_FFLUSH_SOME(stream) \ 585 vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt) 586 587 /* Note: this does not change a negative result into a zero result. */ 588 #define VSTREAM_SUB_TIME(x, y, z) \ 589 do { \ 590 (x).tv_sec = (y).tv_sec - (z).tv_sec; \ 591 (x).tv_usec = (y).tv_usec - (z).tv_usec; \ 592 while ((x).tv_usec < 0) { \ 593 (x).tv_usec += 1000000; \ 594 (x).tv_sec -= 1; \ 595 } \ 596 while ((x).tv_usec >= 1000000) { \ 597 (x).tv_usec -= 1000000; \ 598 (x).tv_sec += 1; \ 599 } \ 600 } while (0) 601 602 /* vstream_buf_init - initialize buffer */ 603 604 static void vstream_buf_init(VBUF *bp, int flags) 605 { 606 607 /* 608 * Initialize the buffer such that the first data access triggers a 609 * buffer boundary action. 610 */ 611 VSTREAM_BUF_ZERO(bp); 612 VSTREAM_BUF_ACTIONS(bp, 613 VSTREAM_CAN_READ(flags) ? vstream_buf_get_ready : 0, 614 VSTREAM_CAN_WRITE(flags) ? vstream_buf_put_ready : 0, 615 vstream_buf_space); 616 } 617 618 /* vstream_buf_alloc - allocate buffer memory */ 619 620 static void vstream_buf_alloc(VBUF *bp, ssize_t len) 621 { 622 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); 623 ssize_t used = bp->ptr - bp->data; 624 const char *myname = "vstream_buf_alloc"; 625 626 if (len < bp->len) 627 msg_panic("%s: attempt to shrink buffer", myname); 628 if (bp->flags & VSTREAM_FLAG_FIXED) 629 msg_panic("%s: unable to extend fixed-size buffer", myname); 630 631 /* 632 * Late buffer allocation allows the user to override the default policy. 633 * If a buffer already exists, allow for the presence of (output) data. 634 */ 635 bp->data = (unsigned char *) 636 (bp->data ? myrealloc((char *) bp->data, len) : mymalloc(len)); 637 bp->len = len; 638 if (bp->flags & VSTREAM_FLAG_READ) { 639 bp->ptr = bp->data + used; 640 if (bp->flags & VSTREAM_FLAG_DOUBLE) 641 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 642 } else { 643 VSTREAM_BUF_AT_OFFSET(bp, used); 644 if (bp->flags & VSTREAM_FLAG_DOUBLE) 645 VSTREAM_SAVE_STATE(stream, write_buf, write_fd); 646 } 647 } 648 649 /* vstream_buf_wipe - reset buffer to initial state */ 650 651 static void vstream_buf_wipe(VBUF *bp) 652 { 653 if ((bp->flags & VBUF_FLAG_FIXED) == 0 && bp->data) 654 myfree((char *) bp->data); 655 VSTREAM_BUF_ZERO(bp); 656 VSTREAM_BUF_ACTIONS(bp, 0, 0, 0); 657 } 658 659 /* vstream_fflush_some - flush some buffered data */ 660 661 static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush) 662 { 663 const char *myname = "vstream_fflush_some"; 664 VBUF *bp = &stream->buf; 665 ssize_t used; 666 ssize_t left_over; 667 char *data; 668 ssize_t len; 669 ssize_t n; 670 int timeout; 671 struct timeval before; 672 struct timeval elapsed; 673 674 /* 675 * Sanity checks. It is illegal to flush a read-only stream. Otherwise, 676 * if there is buffered input, discard the input. If there is buffered 677 * output, require that the amount to flush is larger than the amount to 678 * keep, so that we can memcpy() the residue. 679 */ 680 if (bp->put_ready == 0) 681 msg_panic("%s: read-only stream", myname); 682 switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) { 683 case VSTREAM_FLAG_READ: /* discard input */ 684 VSTREAM_BUF_AT_END(bp); 685 /* FALLTHROUGH */ 686 case 0: /* flush after seek? */ 687 return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0); 688 case VSTREAM_FLAG_WRITE: /* output buffered */ 689 break; 690 case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ: 691 msg_panic("%s: read/write stream", myname); 692 } 693 used = bp->len - bp->cnt; 694 left_over = used - to_flush; 695 696 if (msg_verbose > 2 && stream != VSTREAM_ERR) 697 msg_info("%s: fd %d flush %ld", myname, stream->fd, (long) to_flush); 698 if (to_flush < 0 || left_over < 0) 699 msg_panic("%s: bad to_flush %ld", myname, (long) to_flush); 700 if (to_flush < left_over) 701 msg_panic("%s: to_flush < left_over", myname); 702 if (to_flush == 0) 703 return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0); 704 if (bp->flags & VSTREAM_FLAG_ERR) 705 return (VSTREAM_EOF); 706 707 /* 708 * When flushing a buffer, allow for partial writes. These can happen 709 * while talking to a network. Update the cached file seek position, if 710 * any. 711 * 712 * When deadlines are enabled, we count the elapsed time for each write 713 * operation instead of simply comparing the time-of-day clock with a 714 * per-stream deadline. The latter could result in anomalies when an 715 * application does lengthy processing between write operations. Keep in 716 * mind that a receiver may not be able to keep up when a sender suddenly 717 * floods it with a lot of data as it tries to catch up with a deadline. 718 */ 719 for (data = (char *) bp->data, len = to_flush; len > 0; len -= n, data += n) { 720 if (bp->flags & VSTREAM_FLAG_DEADLINE) { 721 timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0); 722 if (timeout <= 0) { 723 bp->flags |= (VSTREAM_FLAG_WR_ERR | VSTREAM_FLAG_WR_TIMEOUT); 724 errno = ETIMEDOUT; 725 return (VSTREAM_EOF); 726 } 727 if (len == to_flush) 728 GETTIMEOFDAY(&before); 729 else 730 before = stream->iotime; 731 } else 732 timeout = stream->timeout; 733 if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) { 734 bp->flags |= VSTREAM_FLAG_WR_ERR; 735 if (errno == ETIMEDOUT) { 736 bp->flags |= VSTREAM_FLAG_WR_TIMEOUT; 737 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0; 738 } 739 return (VSTREAM_EOF); 740 } 741 if (timeout) { 742 GETTIMEOFDAY(&stream->iotime); 743 if (bp->flags & VSTREAM_FLAG_DEADLINE) { 744 VSTREAM_SUB_TIME(elapsed, stream->iotime, before); 745 VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed); 746 } 747 } 748 if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush) 749 msg_info("%s: %d flushed %ld/%ld", myname, stream->fd, 750 (long) n, (long) to_flush); 751 } 752 if (bp->flags & VSTREAM_FLAG_SEEK) 753 stream->offset += to_flush; 754 755 /* 756 * Allow for partial buffer flush requests. We use memcpy() for reasons 757 * of portability to pre-ANSI environments (SunOS 4.x or Ultrix 4.x :-). 758 * This is OK because we have already verified that the to_flush count is 759 * larger than the left_over count. 760 */ 761 if (left_over > 0) 762 memcpy(bp->data, bp->data + to_flush, left_over); 763 bp->cnt += to_flush; 764 bp->ptr -= to_flush; 765 return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0); 766 } 767 768 /* vstream_fflush_delayed - delayed stream flush for double-buffered stream */ 769 770 static int vstream_fflush_delayed(VSTREAM *stream) 771 { 772 int status; 773 774 /* 775 * Sanity check. 776 */ 777 if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE) 778 msg_panic("vstream_fflush_delayed: bad flags"); 779 780 /* 781 * Temporarily swap buffers and flush unwritten data. This may seem like 782 * a lot of work, but it's peanuts compared to the write(2) call that we 783 * already have avoided. For example, delayed flush is never used on a 784 * non-pipelined SMTP connection. 785 */ 786 stream->buf.flags &= ~VSTREAM_FLAG_READ; 787 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 788 stream->buf.flags |= VSTREAM_FLAG_WRITE; 789 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); 790 791 status = VSTREAM_FFLUSH_SOME(stream); 792 793 stream->buf.flags &= ~VSTREAM_FLAG_WRITE; 794 VSTREAM_SAVE_STATE(stream, write_buf, write_fd); 795 stream->buf.flags |= VSTREAM_FLAG_READ; 796 VSTREAM_RESTORE_STATE(stream, read_buf, read_fd); 797 798 return (status); 799 } 800 801 /* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */ 802 803 static int vstream_buf_get_ready(VBUF *bp) 804 { 805 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); 806 const char *myname = "vstream_buf_get_ready"; 807 ssize_t n; 808 struct timeval before; 809 struct timeval elapsed; 810 int timeout; 811 812 /* 813 * Detect a change of I/O direction or position. If so, flush any 814 * unwritten output immediately when the stream is single-buffered, or 815 * when the stream is double-buffered and the read buffer is empty. 816 */ 817 switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) { 818 case VSTREAM_FLAG_WRITE: /* change direction */ 819 if (bp->ptr > bp->data) 820 if ((bp->flags & VSTREAM_FLAG_DOUBLE) == 0 821 || stream->read_buf.cnt >= 0) 822 if (VSTREAM_FFLUSH_SOME(stream)) 823 return (VSTREAM_EOF); 824 bp->flags &= ~VSTREAM_FLAG_WRITE; 825 if (bp->flags & VSTREAM_FLAG_DOUBLE) 826 VSTREAM_SAVE_STATE(stream, write_buf, write_fd); 827 /* FALLTHROUGH */ 828 case 0: /* change position */ 829 bp->flags |= VSTREAM_FLAG_READ; 830 if (bp->flags & VSTREAM_FLAG_DOUBLE) { 831 VSTREAM_RESTORE_STATE(stream, read_buf, read_fd); 832 if (bp->cnt < 0) 833 return (0); 834 } 835 /* FALLTHROUGH */ 836 case VSTREAM_FLAG_READ: /* no change */ 837 break; 838 case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ: 839 msg_panic("%s: read/write stream", myname); 840 } 841 842 /* 843 * If this is the first GET operation, allocate a buffer. Late buffer 844 * allocation gives the application a chance to override the default 845 * buffering policy. 846 * 847 * XXX Subtle code to set the preferred buffer size as late as possible. 848 */ 849 if (stream->req_bufsize == 0) 850 stream->req_bufsize = VSTREAM_BUFSIZE; 851 if (bp->len < stream->req_bufsize) 852 vstream_buf_alloc(bp, stream->req_bufsize); 853 854 /* 855 * If the stream is double-buffered and the write buffer is not empty, 856 * this is the time to flush the write buffer. Delayed flushes reduce 857 * system call overhead, and on TCP sockets, avoid triggering Nagle's 858 * algorithm. 859 */ 860 if ((bp->flags & VSTREAM_FLAG_DOUBLE) 861 && stream->write_buf.len > stream->write_buf.cnt) 862 if (vstream_fflush_delayed(stream)) 863 return (VSTREAM_EOF); 864 865 /* 866 * Did we receive an EOF indication? 867 */ 868 if (bp->flags & VSTREAM_FLAG_EOF) 869 return (VSTREAM_EOF); 870 871 /* 872 * Fill the buffer with as much data as we can handle, or with as much 873 * data as is available right now, whichever is less. Update the cached 874 * file seek position, if any. 875 * 876 * When deadlines are enabled, we count the elapsed time for each read 877 * operation instead of simply comparing the time-of-day clock with a 878 * per-stream deadline. The latter could result in anomalies when an 879 * application does lengthy processing between read operations. Keep in 880 * mind that a sender may get blocked, and may not be able to keep up 881 * when a receiver suddenly wants to read a lot of data as it tries to 882 * catch up with a deadline. 883 */ 884 if (bp->flags & VSTREAM_FLAG_DEADLINE) { 885 timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0); 886 if (timeout <= 0) { 887 bp->flags |= (VSTREAM_FLAG_RD_ERR | VSTREAM_FLAG_RD_TIMEOUT); 888 errno = ETIMEDOUT; 889 return (VSTREAM_EOF); 890 } 891 GETTIMEOFDAY(&before); 892 } else 893 timeout = stream->timeout; 894 switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) { 895 case -1: 896 bp->flags |= VSTREAM_FLAG_RD_ERR; 897 if (errno == ETIMEDOUT) { 898 bp->flags |= VSTREAM_FLAG_RD_TIMEOUT; 899 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0; 900 } 901 return (VSTREAM_EOF); 902 case 0: 903 bp->flags |= VSTREAM_FLAG_EOF; 904 return (VSTREAM_EOF); 905 default: 906 if (timeout) { 907 GETTIMEOFDAY(&stream->iotime); 908 if (bp->flags & VSTREAM_FLAG_DEADLINE) { 909 VSTREAM_SUB_TIME(elapsed, stream->iotime, before); 910 VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed); 911 } 912 } 913 if (msg_verbose > 2) 914 msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n); 915 bp->cnt = -n; 916 bp->ptr = bp->data; 917 if (bp->flags & VSTREAM_FLAG_SEEK) 918 stream->offset += n; 919 return (0); 920 } 921 } 922 923 /* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */ 924 925 static int vstream_buf_put_ready(VBUF *bp) 926 { 927 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); 928 const char *myname = "vstream_buf_put_ready"; 929 930 /* 931 * Sanity checks. Detect a change of I/O direction or position. If so, 932 * discard unread input, and reset the buffer to the beginning. 933 */ 934 switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) { 935 case VSTREAM_FLAG_READ: /* change direction */ 936 bp->flags &= ~VSTREAM_FLAG_READ; 937 if (bp->flags & VSTREAM_FLAG_DOUBLE) 938 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 939 /* FALLTHROUGH */ 940 case 0: /* change position */ 941 bp->flags |= VSTREAM_FLAG_WRITE; 942 if (bp->flags & VSTREAM_FLAG_DOUBLE) 943 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); 944 else 945 VSTREAM_BUF_AT_START(bp); 946 /* FALLTHROUGH */ 947 case VSTREAM_FLAG_WRITE: /* no change */ 948 break; 949 case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ: 950 msg_panic("%s: read/write stream", myname); 951 } 952 953 /* 954 * Remember the direction. If this is the first PUT operation for this 955 * stream or if the buffer is smaller than the requested size, allocate a 956 * new buffer; obviously there is no data to be flushed yet. Otherwise, 957 * flush the buffer. 958 * 959 * XXX Subtle code to set the preferred buffer size as late as possible. 960 */ 961 if (stream->req_bufsize == 0) 962 stream->req_bufsize = VSTREAM_BUFSIZE; 963 if (bp->len < stream->req_bufsize) { 964 vstream_buf_alloc(bp, stream->req_bufsize); 965 } else if (bp->cnt <= 0) { 966 if (VSTREAM_FFLUSH_SOME(stream)) 967 return (VSTREAM_EOF); 968 } 969 return (0); 970 } 971 972 /* vstream_buf_space - reserve space ahead of time */ 973 974 static int vstream_buf_space(VBUF *bp, ssize_t want) 975 { 976 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); 977 ssize_t used; 978 ssize_t incr; 979 ssize_t shortage; 980 const char *myname = "vstream_buf_space"; 981 982 /* 983 * Sanity checks. Reserving space implies writing. It is illegal to write 984 * to a read-only stream. Detect a change of I/O direction or position. 985 * If so, reset the buffer to the beginning. 986 */ 987 if (bp->put_ready == 0) 988 msg_panic("%s: read-only stream", myname); 989 switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) { 990 case VSTREAM_FLAG_READ: /* change direction */ 991 bp->flags &= ~VSTREAM_FLAG_READ; 992 if (bp->flags & VSTREAM_FLAG_DOUBLE) 993 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 994 /* FALLTHROUGH */ 995 case 0: /* change position */ 996 bp->flags |= VSTREAM_FLAG_WRITE; 997 if (bp->flags & VSTREAM_FLAG_DOUBLE) 998 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); 999 else 1000 VSTREAM_BUF_AT_START(bp); 1001 /* FALLTHROUGH */ 1002 case VSTREAM_FLAG_WRITE: /* no change */ 1003 break; 1004 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: 1005 msg_panic("%s: read/write stream", myname); 1006 } 1007 1008 /* 1009 * See if enough space is available. If not, flush a multiple of 1010 * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of 1011 * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt 1012 * to keep file updates block-aligned for better performance. 1013 * 1014 * XXX Subtle code to set the preferred buffer size as late as possible. 1015 */ 1016 #define VSTREAM_TRUNCATE(count, base) (((count) / (base)) * (base)) 1017 #define VSTREAM_ROUNDUP(count, base) VSTREAM_TRUNCATE(count + base - 1, base) 1018 1019 if (stream->req_bufsize == 0) 1020 stream->req_bufsize = VSTREAM_BUFSIZE; 1021 if (want > bp->cnt) { 1022 if ((used = bp->len - bp->cnt) > stream->req_bufsize) 1023 if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize))) 1024 return (VSTREAM_EOF); 1025 if ((shortage = (want - bp->cnt)) > 0) { 1026 if ((bp->flags & VSTREAM_FLAG_FIXED) 1027 || shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) { 1028 bp->flags |= VSTREAM_FLAG_WR_ERR; 1029 } else { 1030 incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize); 1031 vstream_buf_alloc(bp, bp->len + incr); 1032 } 1033 } 1034 } 1035 return (vstream_ferror(stream) ? VSTREAM_EOF : 0); /* mmap() may fail */ 1036 } 1037 1038 /* vstream_fpurge - discard unread or unwritten content */ 1039 1040 int vstream_fpurge(VSTREAM *stream, int direction) 1041 { 1042 const char *myname = "vstream_fpurge"; 1043 VBUF *bp = &stream->buf; 1044 1045 #define VSTREAM_MAYBE_PURGE_WRITE(d, b) if ((d) & VSTREAM_PURGE_WRITE) \ 1046 VSTREAM_BUF_AT_START((b)) 1047 #define VSTREAM_MAYBE_PURGE_READ(d, b) if ((d) & VSTREAM_PURGE_READ) \ 1048 VSTREAM_BUF_AT_END((b)) 1049 1050 /* 1051 * To discard all unread contents, position the read buffer at its end, 1052 * so that we skip over any unread data, and so that the next read 1053 * operation will refill the buffer. 1054 * 1055 * To discard all unwritten content, position the write buffer at its 1056 * beginning, so that the next write operation clobbers any unwritten 1057 * data. 1058 */ 1059 switch (bp->flags & (VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE)) { 1060 case VSTREAM_FLAG_READ_DOUBLE: 1061 VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf); 1062 /* FALLTHROUGH */ 1063 case VSTREAM_FLAG_READ: 1064 VSTREAM_MAYBE_PURGE_READ(direction, bp); 1065 break; 1066 case VSTREAM_FLAG_DOUBLE: 1067 VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf); 1068 VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf); 1069 break; 1070 case VSTREAM_FLAG_WRITE_DOUBLE: 1071 VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf); 1072 /* FALLTHROUGH */ 1073 case VSTREAM_FLAG_WRITE: 1074 VSTREAM_MAYBE_PURGE_WRITE(direction, bp); 1075 break; 1076 case VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE: 1077 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: 1078 msg_panic("%s: read/write stream", myname); 1079 } 1080 1081 /* 1082 * Invalidate the cached file seek position. 1083 */ 1084 bp->flags &= ~VSTREAM_FLAG_SEEK; 1085 stream->offset = 0; 1086 1087 return (0); 1088 } 1089 1090 /* vstream_fseek - change I/O position */ 1091 1092 off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence) 1093 { 1094 const char *myname = "vstream_fseek"; 1095 VBUF *bp = &stream->buf; 1096 1097 /* 1098 * Flush any unwritten output. Discard any unread input. Position the 1099 * buffer at the end, so that the next GET or PUT operation triggers a 1100 * buffer boundary action. 1101 */ 1102 switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) { 1103 case VSTREAM_FLAG_WRITE: 1104 if (bp->ptr > bp->data) { 1105 if (whence == SEEK_CUR) 1106 offset += (bp->ptr - bp->data); /* add unwritten data */ 1107 else if (whence == SEEK_END) 1108 bp->flags &= ~VSTREAM_FLAG_SEEK; 1109 if (VSTREAM_FFLUSH_SOME(stream)) 1110 return (-1); 1111 } 1112 VSTREAM_BUF_AT_END(bp); 1113 break; 1114 case VSTREAM_FLAG_READ: 1115 if (whence == SEEK_CUR) 1116 offset += bp->cnt; /* subtract unread data */ 1117 else if (whence == SEEK_END) 1118 bp->flags &= ~VSTREAM_FLAG_SEEK; 1119 /* FALLTHROUGH */ 1120 case 0: 1121 VSTREAM_BUF_AT_END(bp); 1122 break; 1123 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: 1124 msg_panic("%s: read/write stream", myname); 1125 } 1126 1127 /* 1128 * Clear the read/write flags to inform the buffer boundary action 1129 * routines that we may have changed I/O position. 1130 */ 1131 bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE); 1132 1133 /* 1134 * Shave an unnecessary system call. 1135 */ 1136 if (bp->flags & VSTREAM_FLAG_NSEEK) { 1137 errno = ESPIPE; 1138 return (-1); 1139 } 1140 1141 /* 1142 * Update the cached file seek position. 1143 */ 1144 if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) { 1145 if (errno == ESPIPE) 1146 bp->flags |= VSTREAM_FLAG_NSEEK; 1147 } else { 1148 bp->flags |= VSTREAM_FLAG_SEEK; 1149 } 1150 bp->flags &= ~VSTREAM_FLAG_EOF; 1151 return (stream->offset); 1152 } 1153 1154 /* vstream_ftell - return file offset */ 1155 1156 off_t vstream_ftell(VSTREAM *stream) 1157 { 1158 VBUF *bp = &stream->buf; 1159 1160 /* 1161 * Shave an unnecessary syscall. 1162 */ 1163 if (bp->flags & VSTREAM_FLAG_NSEEK) { 1164 errno = ESPIPE; 1165 return (-1); 1166 } 1167 1168 /* 1169 * Use the cached file offset when available. This is the offset after 1170 * the last read, write or seek operation. 1171 */ 1172 if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) { 1173 if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) { 1174 bp->flags |= VSTREAM_FLAG_NSEEK; 1175 return (-1); 1176 } 1177 bp->flags |= VSTREAM_FLAG_SEEK; 1178 } 1179 1180 /* 1181 * If this is a read buffer, subtract the number of unread bytes from the 1182 * cached offset. Remember that read counts are negative. 1183 */ 1184 if (bp->flags & VSTREAM_FLAG_READ) 1185 return (stream->offset + bp->cnt); 1186 1187 /* 1188 * If this is a write buffer, add the number of unwritten bytes to the 1189 * cached offset. 1190 */ 1191 if (bp->flags & VSTREAM_FLAG_WRITE) 1192 return (stream->offset + (bp->ptr - bp->data)); 1193 1194 /* 1195 * Apparently, this is a new buffer, or a buffer after seek, so there is 1196 * no need to account for unread or unwritten data. 1197 */ 1198 return (stream->offset); 1199 } 1200 1201 /* vstream_fdopen - add buffering to pre-opened stream */ 1202 1203 VSTREAM *vstream_fdopen(int fd, int flags) 1204 { 1205 VSTREAM *stream; 1206 1207 /* 1208 * Sanity check. 1209 */ 1210 if (fd < 0) 1211 msg_panic("vstream_fdopen: bad file %d", fd); 1212 1213 /* 1214 * Initialize buffers etc. but do as little as possible. Late buffer 1215 * allocation etc. gives the application a chance to override default 1216 * policies. Either this, or the vstream*open() routines would have to 1217 * have a really ugly interface with lots of mostly-unused arguments (can 1218 * you say VMS?). 1219 */ 1220 stream = (VSTREAM *) mymalloc(sizeof(*stream)); 1221 stream->fd = fd; 1222 stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_FN) timed_read : 0; 1223 stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_FN) timed_write : 0; 1224 vstream_buf_init(&stream->buf, flags); 1225 stream->offset = 0; 1226 stream->path = 0; 1227 stream->pid = 0; 1228 stream->waitpid_fn = 0; 1229 stream->timeout = 0; 1230 stream->context = 0; 1231 stream->jbuf = 0; 1232 stream->iotime.tv_sec = stream->iotime.tv_usec = 0; 1233 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0; 1234 stream->req_bufsize = 0; 1235 return (stream); 1236 } 1237 1238 /* vstream_fopen - open buffered file stream */ 1239 1240 VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode) 1241 { 1242 VSTREAM *stream; 1243 int fd; 1244 1245 if ((fd = open(path, flags, mode)) < 0) { 1246 return (0); 1247 } else { 1248 stream = vstream_fdopen(fd, flags); 1249 stream->path = mystrdup(path); 1250 return (stream); 1251 } 1252 } 1253 1254 /* vstream_fflush - flush write buffer */ 1255 1256 int vstream_fflush(VSTREAM *stream) 1257 { 1258 if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) 1259 == VSTREAM_FLAG_READ_DOUBLE 1260 && stream->write_buf.len > stream->write_buf.cnt) 1261 vstream_fflush_delayed(stream); 1262 return (VSTREAM_FFLUSH_SOME(stream)); 1263 } 1264 1265 /* vstream_fclose - close buffered stream */ 1266 1267 int vstream_fclose(VSTREAM *stream) 1268 { 1269 int err; 1270 1271 /* 1272 * NOTE: Negative file descriptors are not part of the external 1273 * interface. They are for internal use only, in order to support 1274 * vstream_fdclose() without a lot of code duplication. Applications that 1275 * rely on negative VSTREAM file descriptors will break without warning. 1276 */ 1277 if (stream->pid != 0) 1278 msg_panic("vstream_fclose: stream has process"); 1279 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0 && stream->fd >= 0) 1280 vstream_fflush(stream); 1281 /* Do not remove: vstream_fdclose() depends on this error test. */ 1282 err = vstream_ferror(stream); 1283 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1284 if (stream->read_fd >= 0) 1285 err |= close(stream->read_fd); 1286 if (stream->write_fd != stream->read_fd) 1287 if (stream->write_fd >= 0) 1288 err |= close(stream->write_fd); 1289 vstream_buf_wipe(&stream->read_buf); 1290 vstream_buf_wipe(&stream->write_buf); 1291 stream->buf = stream->read_buf; 1292 } else { 1293 if (stream->fd >= 0) 1294 err |= close(stream->fd); 1295 vstream_buf_wipe(&stream->buf); 1296 } 1297 if (stream->path) 1298 myfree(stream->path); 1299 if (stream->jbuf) 1300 myfree((char *) stream->jbuf); 1301 if (!VSTREAM_STATIC(stream)) 1302 myfree((char *) stream); 1303 return (err ? VSTREAM_EOF : 0); 1304 } 1305 1306 /* vstream_fdclose - close stream, leave file(s) open */ 1307 1308 int vstream_fdclose(VSTREAM *stream) 1309 { 1310 1311 /* 1312 * Flush unwritten output, just like vstream_fclose(). Errors are 1313 * reported by vstream_fclose(). 1314 */ 1315 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0) 1316 (void) vstream_fflush(stream); 1317 1318 /* 1319 * NOTE: Negative file descriptors are not part of the external 1320 * interface. They are for internal use only, in order to support 1321 * vstream_fdclose() without a lot of code duplication. Applications that 1322 * rely on negative VSTREAM file descriptors will break without warning. 1323 */ 1324 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1325 stream->fd = stream->read_fd = stream->write_fd = -1; 1326 } else { 1327 stream->fd = -1; 1328 } 1329 return (vstream_fclose(stream)); 1330 } 1331 1332 /* vstream_printf - formatted print to stdout */ 1333 1334 VSTREAM *vstream_printf(const char *fmt,...) 1335 { 1336 VSTREAM *stream = VSTREAM_OUT; 1337 va_list ap; 1338 1339 va_start(ap, fmt); 1340 vbuf_print(&stream->buf, fmt, ap); 1341 va_end(ap); 1342 return (stream); 1343 } 1344 1345 /* vstream_fprintf - formatted print to buffered stream */ 1346 1347 VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...) 1348 { 1349 va_list ap; 1350 1351 va_start(ap, fmt); 1352 vbuf_print(&stream->buf, fmt, ap); 1353 va_end(ap); 1354 return (stream); 1355 } 1356 1357 /* vstream_fputs - write string to stream */ 1358 1359 int vstream_fputs(const char *str, VSTREAM *stream) 1360 { 1361 int ch; 1362 1363 while ((ch = *str++) != 0) 1364 if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF) 1365 return (VSTREAM_EOF); 1366 return (0); 1367 } 1368 1369 /* vstream_control - fine control */ 1370 1371 void vstream_control(VSTREAM *stream, int name,...) 1372 { 1373 const char *myname = "vstream_control"; 1374 va_list ap; 1375 int floor; 1376 int old_fd; 1377 ssize_t req_bufsize = 0; 1378 VSTREAM *stream2; 1379 1380 #define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0) 1381 1382 for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) { 1383 switch (name) { 1384 case VSTREAM_CTL_READ_FN: 1385 stream->read_fn = va_arg(ap, VSTREAM_FN); 1386 break; 1387 case VSTREAM_CTL_WRITE_FN: 1388 stream->write_fn = va_arg(ap, VSTREAM_FN); 1389 break; 1390 case VSTREAM_CTL_CONTEXT: 1391 stream->context = va_arg(ap, char *); 1392 break; 1393 case VSTREAM_CTL_PATH: 1394 if (stream->path) 1395 myfree(stream->path); 1396 stream->path = mystrdup(va_arg(ap, char *)); 1397 break; 1398 case VSTREAM_CTL_DOUBLE: 1399 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) { 1400 stream->buf.flags |= VSTREAM_FLAG_DOUBLE; 1401 if (stream->buf.flags & VSTREAM_FLAG_READ) { 1402 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 1403 VSTREAM_FORK_STATE(stream, write_buf, write_fd); 1404 } else { 1405 VSTREAM_SAVE_STATE(stream, write_buf, write_fd); 1406 VSTREAM_FORK_STATE(stream, read_buf, read_fd); 1407 } 1408 } 1409 break; 1410 case VSTREAM_CTL_READ_FD: 1411 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) 1412 msg_panic("VSTREAM_CTL_READ_FD requires double buffering"); 1413 stream->read_fd = va_arg(ap, int); 1414 stream->buf.flags |= VSTREAM_FLAG_NSEEK; 1415 break; 1416 case VSTREAM_CTL_WRITE_FD: 1417 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) 1418 msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering"); 1419 stream->write_fd = va_arg(ap, int); 1420 stream->buf.flags |= VSTREAM_FLAG_NSEEK; 1421 break; 1422 case VSTREAM_CTL_SWAP_FD: 1423 stream2 = va_arg(ap, VSTREAM *); 1424 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) 1425 != (stream2->buf.flags & VSTREAM_FLAG_DOUBLE)) 1426 msg_panic("VSTREAM_CTL_SWAP_FD can't swap descriptors between " 1427 "single-buffered and double-buffered streams"); 1428 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1429 SWAP(int, stream->read_fd, stream2->read_fd); 1430 SWAP(int, stream->write_fd, stream2->write_fd); 1431 stream->fd = ((stream->buf.flags & VSTREAM_FLAG_WRITE) ? 1432 stream->write_fd : stream->read_fd); 1433 } else { 1434 SWAP(int, stream->fd, stream2->fd); 1435 } 1436 break; 1437 case VSTREAM_CTL_TIMEOUT: 1438 if (stream->timeout == 0) 1439 GETTIMEOFDAY(&stream->iotime); 1440 stream->timeout = va_arg(ap, int); 1441 if (stream->timeout < 0) 1442 msg_panic("%s: bad timeout %d", myname, stream->timeout); 1443 break; 1444 case VSTREAM_CTL_EXCEPT: 1445 if (stream->jbuf == 0) 1446 stream->jbuf = 1447 (VSTREAM_JMP_BUF *) mymalloc(sizeof(VSTREAM_JMP_BUF)); 1448 break; 1449 1450 #ifdef VSTREAM_CTL_DUPFD 1451 1452 #define VSTREAM_TRY_DUPFD(backup, fd, floor) do { \ 1453 if (((backup) = (fd)) < floor) { \ 1454 if (((fd) = fcntl((backup), F_DUPFD, (floor))) < 0) \ 1455 msg_fatal("fcntl F_DUPFD %d: %m", (floor)); \ 1456 (void) close(backup); \ 1457 } \ 1458 } while (0) 1459 1460 case VSTREAM_CTL_DUPFD: 1461 floor = va_arg(ap, int); 1462 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1463 VSTREAM_TRY_DUPFD(old_fd, stream->read_fd, floor); 1464 if (stream->write_fd == old_fd) 1465 stream->write_fd = stream->read_fd; 1466 else 1467 VSTREAM_TRY_DUPFD(old_fd, stream->write_fd, floor); 1468 stream->fd = (stream->buf.flags & VSTREAM_FLAG_READ) ? 1469 stream->read_fd : stream->write_fd; 1470 } else { 1471 VSTREAM_TRY_DUPFD(old_fd, stream->fd, floor); 1472 } 1473 break; 1474 #endif 1475 1476 /* 1477 * Postpone memory (re)allocation until the space is needed. 1478 */ 1479 case VSTREAM_CTL_BUFSIZE: 1480 req_bufsize = va_arg(ap, ssize_t); 1481 /* Heuristic to detect missing (ssize_t) type cast on LP64 hosts. */ 1482 if (req_bufsize < 0 || req_bufsize > INT_MAX) 1483 msg_panic("unreasonable VSTREAM_CTL_BUFSIZE request: %ld", 1484 (long) req_bufsize); 1485 if ((stream->buf.flags & VSTREAM_FLAG_FIXED) == 0 1486 && req_bufsize > stream->req_bufsize) { 1487 if (msg_verbose) 1488 msg_info("fd=%d: stream buffer size old=%ld new=%ld", 1489 vstream_fileno(stream), 1490 (long) stream->req_bufsize, 1491 (long) req_bufsize); 1492 stream->req_bufsize = req_bufsize; 1493 } 1494 break; 1495 1496 /* 1497 * Make no gettimeofday() etc. system call until we really know 1498 * that we need to do I/O. This avoids a performance hit when 1499 * sending or receiving body content one line at a time. 1500 */ 1501 case VSTREAM_CTL_STOP_DEADLINE: 1502 stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE; 1503 break; 1504 case VSTREAM_CTL_START_DEADLINE: 1505 if (stream->timeout <= 0) 1506 msg_panic("%s: bad timeout %d", myname, stream->timeout); 1507 stream->buf.flags |= VSTREAM_FLAG_DEADLINE; 1508 stream->time_limit.tv_sec = stream->timeout; 1509 stream->time_limit.tv_usec = 0; 1510 break; 1511 default: 1512 msg_panic("%s: bad name %d", myname, name); 1513 } 1514 } 1515 va_end(ap); 1516 } 1517 1518 /* vstream_vprintf - formatted print to stdout */ 1519 1520 VSTREAM *vstream_vprintf(const char *format, va_list ap) 1521 { 1522 VSTREAM *vp = VSTREAM_OUT; 1523 1524 vbuf_print(&vp->buf, format, ap); 1525 return (vp); 1526 } 1527 1528 /* vstream_vfprintf - formatted print engine */ 1529 1530 VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap) 1531 { 1532 vbuf_print(&vp->buf, format, ap); 1533 return (vp); 1534 } 1535 1536 /* vstream_bufstat - get stream buffer status */ 1537 1538 ssize_t vstream_bufstat(VSTREAM *vp, int command) 1539 { 1540 VBUF *bp; 1541 1542 switch (command & VSTREAM_BST_MASK_DIR) { 1543 case VSTREAM_BST_FLAG_IN: 1544 if (vp->buf.flags & VSTREAM_FLAG_READ) { 1545 bp = &vp->buf; 1546 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1547 bp = &vp->read_buf; 1548 } else { 1549 bp = 0; 1550 } 1551 switch (command & ~VSTREAM_BST_MASK_DIR) { 1552 case VSTREAM_BST_FLAG_PEND: 1553 return (bp ? -bp->cnt : 0); 1554 /* Add other requests below. */ 1555 } 1556 break; 1557 case VSTREAM_BST_FLAG_OUT: 1558 if (vp->buf.flags & VSTREAM_FLAG_WRITE) { 1559 bp = &vp->buf; 1560 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1561 bp = &vp->write_buf; 1562 } else { 1563 bp = 0; 1564 } 1565 switch (command & ~VSTREAM_BST_MASK_DIR) { 1566 case VSTREAM_BST_FLAG_PEND: 1567 return (bp ? bp->len - bp->cnt : 0); 1568 /* Add other requests below. */ 1569 } 1570 break; 1571 } 1572 msg_panic("vstream_bufstat: unknown command: %d", command); 1573 } 1574 1575 #undef vstream_peek /* API binary compatibility. */ 1576 1577 /* vstream_peek - peek at a stream */ 1578 1579 ssize_t vstream_peek(VSTREAM *vp) 1580 { 1581 if (vp->buf.flags & VSTREAM_FLAG_READ) { 1582 return (-vp->buf.cnt); 1583 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1584 return (-vp->read_buf.cnt); 1585 } else { 1586 return (0); 1587 } 1588 } 1589 1590 /* vstream_peek_data - peek at unread data */ 1591 1592 const char *vstream_peek_data(VSTREAM *vp) 1593 { 1594 if (vp->buf.flags & VSTREAM_FLAG_READ) { 1595 return ((const char *) vp->buf.ptr); 1596 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1597 return ((const char *) vp->read_buf.ptr); 1598 } else { 1599 return (0); 1600 } 1601 } 1602 1603 #ifdef TEST 1604 1605 static void copy_line(ssize_t bufsize) 1606 { 1607 int c; 1608 1609 vstream_control(VSTREAM_IN, VSTREAM_CTL_BUFSIZE, bufsize, VSTREAM_CTL_END); 1610 vstream_control(VSTREAM_OUT, VSTREAM_CTL_BUFSIZE, bufsize, VSTREAM_CTL_END); 1611 while ((c = VSTREAM_GETC(VSTREAM_IN)) != VSTREAM_EOF) { 1612 VSTREAM_PUTC(c, VSTREAM_OUT); 1613 if (c == '\n') 1614 break; 1615 } 1616 vstream_fflush(VSTREAM_OUT); 1617 } 1618 1619 static void printf_number(void) 1620 { 1621 vstream_printf("%d\n", __MAXINT__(int)); 1622 vstream_fflush(VSTREAM_OUT); 1623 } 1624 1625 /* 1626 * Exercise some of the features. 1627 */ 1628 int main(int argc, char **argv) 1629 { 1630 1631 /* 1632 * Test buffer expansion and shrinking. Formatted print may silently 1633 * expand the write buffer and cause multiple bytes to be written. 1634 */ 1635 copy_line(1); /* one-byte read/write */ 1636 copy_line(2); /* two-byte read/write */ 1637 copy_line(1); /* two-byte read/write */ 1638 printf_number(); /* multi-byte write */ 1639 exit(0); 1640 } 1641 1642 #endif 1643