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