1 /* $NetBSD: vstream.c,v 1.1.1.3 2013/01/02 18:59:14 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 write buffer size, or zero to implement 330 /* a no-op. Requests to shrink an existing buffer size are 331 /* ignored. Requests to change a fixed-size buffer (stdin, 332 /* stdout, stderr) are not allowed. 333 /* 334 /* NOTE: the VSTREAM_CTL_BUFSIZE request specifies intent, not 335 /* reality. Actual buffer sizes are not updated immediately. 336 /* Instead, an existing write buffer will be resized when it 337 /* is full, and an existing read buffer will be resized when 338 /* the buffer is filled. 339 /* 340 /* NOTE: the VSTREAM_CTL_BUFSIZE argument type is ssize_t, not 341 /* int. Use an explicit cast to avoid problems on LP64 342 /* environments and other environments where ssize_t is larger 343 /* than int. 344 /* .IP VSTREAM_CTL_START_DEADLINE 345 /* Change the VSTREAM_CTL_TIMEOUT behavior, to limit the total 346 /* time for all subsequent file descriptor read or write 347 /* operations, and recharge the deadline timer. 348 /* .IP VSTREAM_CTL_STOP_DEADLINE 349 /* Revert VSTREAM_CTL_TIMEOUT behavior to the default, i.e. 350 /* a time limit for individual file descriptor read or write 351 /* operations. 352 /* .PP 353 /* vstream_fileno() gives access to the file handle associated with 354 /* a buffered stream. With streams that have separate read/write 355 /* file descriptors, the result is the current descriptor. 356 /* 357 /* vstream_req_bufsize() returns the requested buffer size for 358 /* the named stream (default: VSTREAM_BUFSIZE). The result 359 /* value reflects intent, not reality: actual buffer sizes are 360 /* not updated immediately when the requested buffer size is 361 /* specified with vstream_control(). Instead, an existing 362 /* write buffer will be resized when it is full, and an existing 363 /* read buffer will be resized when the buffer is filled. 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 VSTREAM_BUFSIZE,}, 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 VSTREAM_BUFSIZE,}, 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 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 if (bp->len < stream->req_bufsize) { 956 vstream_buf_alloc(bp, stream->req_bufsize); 957 } else if (bp->cnt <= 0) { 958 if (VSTREAM_FFLUSH_SOME(stream)) 959 return (VSTREAM_EOF); 960 } 961 return (0); 962 } 963 964 /* vstream_buf_space - reserve space ahead of time */ 965 966 static int vstream_buf_space(VBUF *bp, ssize_t want) 967 { 968 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); 969 ssize_t used; 970 ssize_t incr; 971 ssize_t shortage; 972 const char *myname = "vstream_buf_space"; 973 974 /* 975 * Sanity checks. Reserving space implies writing. It is illegal to write 976 * to a read-only stream. Detect a change of I/O direction or position. 977 * If so, reset the buffer to the beginning. 978 */ 979 if (bp->put_ready == 0) 980 msg_panic("%s: read-only stream", myname); 981 switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) { 982 case VSTREAM_FLAG_READ: /* change direction */ 983 bp->flags &= ~VSTREAM_FLAG_READ; 984 if (bp->flags & VSTREAM_FLAG_DOUBLE) 985 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 986 /* FALLTHROUGH */ 987 case 0: /* change position */ 988 bp->flags |= VSTREAM_FLAG_WRITE; 989 if (bp->flags & VSTREAM_FLAG_DOUBLE) 990 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); 991 else 992 VSTREAM_BUF_AT_START(bp); 993 /* FALLTHROUGH */ 994 case VSTREAM_FLAG_WRITE: /* no change */ 995 break; 996 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: 997 msg_panic("%s: read/write stream", myname); 998 } 999 1000 /* 1001 * See if enough space is available. If not, flush a multiple of 1002 * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of 1003 * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt 1004 * to keep file updates block-aligned for better performance. 1005 */ 1006 #define VSTREAM_TRUNCATE(count, base) (((count) / (base)) * (base)) 1007 #define VSTREAM_ROUNDUP(count, base) VSTREAM_TRUNCATE(count + base - 1, base) 1008 1009 if (want > bp->cnt) { 1010 if ((used = bp->len - bp->cnt) > stream->req_bufsize) 1011 if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize))) 1012 return (VSTREAM_EOF); 1013 if ((shortage = (want - bp->cnt)) > 0) { 1014 if ((bp->flags & VSTREAM_FLAG_FIXED) 1015 || shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) { 1016 bp->flags |= VSTREAM_FLAG_WR_ERR; 1017 } else { 1018 incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize); 1019 vstream_buf_alloc(bp, bp->len + incr); 1020 } 1021 } 1022 } 1023 return (vstream_ferror(stream) ? VSTREAM_EOF : 0); /* mmap() may fail */ 1024 } 1025 1026 /* vstream_fpurge - discard unread or unwritten content */ 1027 1028 int vstream_fpurge(VSTREAM *stream, int direction) 1029 { 1030 const char *myname = "vstream_fpurge"; 1031 VBUF *bp = &stream->buf; 1032 1033 #define VSTREAM_MAYBE_PURGE_WRITE(d, b) if ((d) & VSTREAM_PURGE_WRITE) \ 1034 VSTREAM_BUF_AT_START((b)) 1035 #define VSTREAM_MAYBE_PURGE_READ(d, b) if ((d) & VSTREAM_PURGE_READ) \ 1036 VSTREAM_BUF_AT_END((b)) 1037 1038 /* 1039 * To discard all unread contents, position the read buffer at its end, 1040 * so that we skip over any unread data, and so that the next read 1041 * operation will refill the buffer. 1042 * 1043 * To discard all unwritten content, position the write buffer at its 1044 * beginning, so that the next write operation clobbers any unwritten 1045 * data. 1046 */ 1047 switch (bp->flags & (VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE)) { 1048 case VSTREAM_FLAG_READ_DOUBLE: 1049 VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf); 1050 /* FALLTHROUGH */ 1051 case VSTREAM_FLAG_READ: 1052 VSTREAM_MAYBE_PURGE_READ(direction, bp); 1053 break; 1054 case VSTREAM_FLAG_DOUBLE: 1055 VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf); 1056 VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf); 1057 break; 1058 case VSTREAM_FLAG_WRITE_DOUBLE: 1059 VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf); 1060 /* FALLTHROUGH */ 1061 case VSTREAM_FLAG_WRITE: 1062 VSTREAM_MAYBE_PURGE_WRITE(direction, bp); 1063 break; 1064 case VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE: 1065 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: 1066 msg_panic("%s: read/write stream", myname); 1067 } 1068 1069 /* 1070 * Invalidate the cached file seek position. 1071 */ 1072 bp->flags &= ~VSTREAM_FLAG_SEEK; 1073 stream->offset = 0; 1074 1075 return (0); 1076 } 1077 1078 /* vstream_fseek - change I/O position */ 1079 1080 off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence) 1081 { 1082 const char *myname = "vstream_fseek"; 1083 VBUF *bp = &stream->buf; 1084 1085 /* 1086 * Flush any unwritten output. Discard any unread input. Position the 1087 * buffer at the end, so that the next GET or PUT operation triggers a 1088 * buffer boundary action. 1089 */ 1090 switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) { 1091 case VSTREAM_FLAG_WRITE: 1092 if (bp->ptr > bp->data) { 1093 if (whence == SEEK_CUR) 1094 offset += (bp->ptr - bp->data); /* add unwritten data */ 1095 else if (whence == SEEK_END) 1096 bp->flags &= ~VSTREAM_FLAG_SEEK; 1097 if (VSTREAM_FFLUSH_SOME(stream)) 1098 return (-1); 1099 } 1100 VSTREAM_BUF_AT_END(bp); 1101 break; 1102 case VSTREAM_FLAG_READ: 1103 if (whence == SEEK_CUR) 1104 offset += bp->cnt; /* subtract unread data */ 1105 else if (whence == SEEK_END) 1106 bp->flags &= ~VSTREAM_FLAG_SEEK; 1107 /* FALLTHROUGH */ 1108 case 0: 1109 VSTREAM_BUF_AT_END(bp); 1110 break; 1111 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: 1112 msg_panic("%s: read/write stream", myname); 1113 } 1114 1115 /* 1116 * Clear the read/write flags to inform the buffer boundary action 1117 * routines that we may have changed I/O position. 1118 */ 1119 bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE); 1120 1121 /* 1122 * Shave an unnecessary system call. 1123 */ 1124 if (bp->flags & VSTREAM_FLAG_NSEEK) { 1125 errno = ESPIPE; 1126 return (-1); 1127 } 1128 1129 /* 1130 * Update the cached file seek position. 1131 */ 1132 if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) { 1133 if (errno == ESPIPE) 1134 bp->flags |= VSTREAM_FLAG_NSEEK; 1135 } else { 1136 bp->flags |= VSTREAM_FLAG_SEEK; 1137 } 1138 bp->flags &= ~VSTREAM_FLAG_EOF; 1139 return (stream->offset); 1140 } 1141 1142 /* vstream_ftell - return file offset */ 1143 1144 off_t vstream_ftell(VSTREAM *stream) 1145 { 1146 VBUF *bp = &stream->buf; 1147 1148 /* 1149 * Shave an unnecessary syscall. 1150 */ 1151 if (bp->flags & VSTREAM_FLAG_NSEEK) { 1152 errno = ESPIPE; 1153 return (-1); 1154 } 1155 1156 /* 1157 * Use the cached file offset when available. This is the offset after 1158 * the last read, write or seek operation. 1159 */ 1160 if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) { 1161 if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) { 1162 bp->flags |= VSTREAM_FLAG_NSEEK; 1163 return (-1); 1164 } 1165 bp->flags |= VSTREAM_FLAG_SEEK; 1166 } 1167 1168 /* 1169 * If this is a read buffer, subtract the number of unread bytes from the 1170 * cached offset. Remember that read counts are negative. 1171 */ 1172 if (bp->flags & VSTREAM_FLAG_READ) 1173 return (stream->offset + bp->cnt); 1174 1175 /* 1176 * If this is a write buffer, add the number of unwritten bytes to the 1177 * cached offset. 1178 */ 1179 if (bp->flags & VSTREAM_FLAG_WRITE) 1180 return (stream->offset + (bp->ptr - bp->data)); 1181 1182 /* 1183 * Apparently, this is a new buffer, or a buffer after seek, so there is 1184 * no need to account for unread or unwritten data. 1185 */ 1186 return (stream->offset); 1187 } 1188 1189 /* vstream_fdopen - add buffering to pre-opened stream */ 1190 1191 VSTREAM *vstream_fdopen(int fd, int flags) 1192 { 1193 VSTREAM *stream; 1194 1195 /* 1196 * Sanity check. 1197 */ 1198 if (fd < 0) 1199 msg_panic("vstream_fdopen: bad file %d", fd); 1200 1201 /* 1202 * Initialize buffers etc. but do as little as possible. Late buffer 1203 * allocation etc. gives the application a chance to override default 1204 * policies. Either this, or the vstream*open() routines would have to 1205 * have a really ugly interface with lots of mostly-unused arguments (can 1206 * you say VMS?). 1207 */ 1208 stream = (VSTREAM *) mymalloc(sizeof(*stream)); 1209 stream->fd = fd; 1210 stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_FN) timed_read : 0; 1211 stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_FN) timed_write : 0; 1212 vstream_buf_init(&stream->buf, flags); 1213 stream->offset = 0; 1214 stream->path = 0; 1215 stream->pid = 0; 1216 stream->waitpid_fn = 0; 1217 stream->timeout = 0; 1218 stream->context = 0; 1219 stream->jbuf = 0; 1220 stream->iotime.tv_sec = stream->iotime.tv_usec = 0; 1221 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0; 1222 stream->req_bufsize = VSTREAM_BUFSIZE; 1223 return (stream); 1224 } 1225 1226 /* vstream_fopen - open buffered file stream */ 1227 1228 VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode) 1229 { 1230 VSTREAM *stream; 1231 int fd; 1232 1233 if ((fd = open(path, flags, mode)) < 0) { 1234 return (0); 1235 } else { 1236 stream = vstream_fdopen(fd, flags); 1237 stream->path = mystrdup(path); 1238 return (stream); 1239 } 1240 } 1241 1242 /* vstream_fflush - flush write buffer */ 1243 1244 int vstream_fflush(VSTREAM *stream) 1245 { 1246 if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) 1247 == VSTREAM_FLAG_READ_DOUBLE 1248 && stream->write_buf.len > stream->write_buf.cnt) 1249 vstream_fflush_delayed(stream); 1250 return (VSTREAM_FFLUSH_SOME(stream)); 1251 } 1252 1253 /* vstream_fclose - close buffered stream */ 1254 1255 int vstream_fclose(VSTREAM *stream) 1256 { 1257 int err; 1258 1259 /* 1260 * NOTE: Negative file descriptors are not part of the external 1261 * interface. They are for internal use only, in order to support 1262 * vstream_fdclose() without a lot of code duplication. Applications that 1263 * rely on negative VSTREAM file descriptors will break without warning. 1264 */ 1265 if (stream->pid != 0) 1266 msg_panic("vstream_fclose: stream has process"); 1267 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0 && stream->fd >= 0) 1268 vstream_fflush(stream); 1269 /* Do not remove: vstream_fdclose() depends on this error test. */ 1270 err = vstream_ferror(stream); 1271 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1272 if (stream->read_fd >= 0) 1273 err |= close(stream->read_fd); 1274 if (stream->write_fd != stream->read_fd) 1275 if (stream->write_fd >= 0) 1276 err |= close(stream->write_fd); 1277 vstream_buf_wipe(&stream->read_buf); 1278 vstream_buf_wipe(&stream->write_buf); 1279 stream->buf = stream->read_buf; 1280 } else { 1281 if (stream->fd >= 0) 1282 err |= close(stream->fd); 1283 vstream_buf_wipe(&stream->buf); 1284 } 1285 if (stream->path) 1286 myfree(stream->path); 1287 if (stream->jbuf) 1288 myfree((char *) stream->jbuf); 1289 if (!VSTREAM_STATIC(stream)) 1290 myfree((char *) stream); 1291 return (err ? VSTREAM_EOF : 0); 1292 } 1293 1294 /* vstream_fdclose - close stream, leave file(s) open */ 1295 1296 int vstream_fdclose(VSTREAM *stream) 1297 { 1298 1299 /* 1300 * Flush unwritten output, just like vstream_fclose(). Errors are 1301 * reported by vstream_fclose(). 1302 */ 1303 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0) 1304 (void) vstream_fflush(stream); 1305 1306 /* 1307 * NOTE: Negative file descriptors are not part of the external 1308 * interface. They are for internal use only, in order to support 1309 * vstream_fdclose() without a lot of code duplication. Applications that 1310 * rely on negative VSTREAM file descriptors will break without warning. 1311 */ 1312 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1313 stream->fd = stream->read_fd = stream->write_fd = -1; 1314 } else { 1315 stream->fd = -1; 1316 } 1317 return (vstream_fclose(stream)); 1318 } 1319 1320 /* vstream_printf - formatted print to stdout */ 1321 1322 VSTREAM *vstream_printf(const char *fmt,...) 1323 { 1324 VSTREAM *stream = VSTREAM_OUT; 1325 va_list ap; 1326 1327 va_start(ap, fmt); 1328 vbuf_print(&stream->buf, fmt, ap); 1329 va_end(ap); 1330 return (stream); 1331 } 1332 1333 /* vstream_fprintf - formatted print to buffered stream */ 1334 1335 VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...) 1336 { 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_fputs - write string to stream */ 1346 1347 int vstream_fputs(const char *str, VSTREAM *stream) 1348 { 1349 int ch; 1350 1351 while ((ch = *str++) != 0) 1352 if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF) 1353 return (VSTREAM_EOF); 1354 return (0); 1355 } 1356 1357 /* vstream_control - fine control */ 1358 1359 void vstream_control(VSTREAM *stream, int name,...) 1360 { 1361 const char *myname = "vstream_control"; 1362 va_list ap; 1363 int floor; 1364 int old_fd; 1365 ssize_t req_bufsize = 0; 1366 VSTREAM *stream2; 1367 1368 #define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0) 1369 1370 for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) { 1371 switch (name) { 1372 case VSTREAM_CTL_READ_FN: 1373 stream->read_fn = va_arg(ap, VSTREAM_FN); 1374 break; 1375 case VSTREAM_CTL_WRITE_FN: 1376 stream->write_fn = va_arg(ap, VSTREAM_FN); 1377 break; 1378 case VSTREAM_CTL_CONTEXT: 1379 stream->context = va_arg(ap, char *); 1380 break; 1381 case VSTREAM_CTL_PATH: 1382 if (stream->path) 1383 myfree(stream->path); 1384 stream->path = mystrdup(va_arg(ap, char *)); 1385 break; 1386 case VSTREAM_CTL_DOUBLE: 1387 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) { 1388 stream->buf.flags |= VSTREAM_FLAG_DOUBLE; 1389 if (stream->buf.flags & VSTREAM_FLAG_READ) { 1390 VSTREAM_SAVE_STATE(stream, read_buf, read_fd); 1391 VSTREAM_FORK_STATE(stream, write_buf, write_fd); 1392 } else { 1393 VSTREAM_SAVE_STATE(stream, write_buf, write_fd); 1394 VSTREAM_FORK_STATE(stream, read_buf, read_fd); 1395 } 1396 } 1397 break; 1398 case VSTREAM_CTL_READ_FD: 1399 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) 1400 msg_panic("VSTREAM_CTL_READ_FD requires double buffering"); 1401 stream->read_fd = va_arg(ap, int); 1402 stream->buf.flags |= VSTREAM_FLAG_NSEEK; 1403 break; 1404 case VSTREAM_CTL_WRITE_FD: 1405 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) 1406 msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering"); 1407 stream->write_fd = va_arg(ap, int); 1408 stream->buf.flags |= VSTREAM_FLAG_NSEEK; 1409 break; 1410 case VSTREAM_CTL_SWAP_FD: 1411 stream2 = va_arg(ap, VSTREAM *); 1412 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) 1413 != (stream2->buf.flags & VSTREAM_FLAG_DOUBLE)) 1414 msg_panic("VSTREAM_CTL_SWAP_FD can't swap descriptors between " 1415 "single-buffered and double-buffered streams"); 1416 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1417 SWAP(int, stream->read_fd, stream2->read_fd); 1418 SWAP(int, stream->write_fd, stream2->write_fd); 1419 stream->fd = ((stream->buf.flags & VSTREAM_FLAG_WRITE) ? 1420 stream->write_fd : stream->read_fd); 1421 } else { 1422 SWAP(int, stream->fd, stream2->fd); 1423 } 1424 break; 1425 case VSTREAM_CTL_TIMEOUT: 1426 if (stream->timeout == 0) 1427 GETTIMEOFDAY(&stream->iotime); 1428 stream->timeout = va_arg(ap, int); 1429 if (stream->timeout < 0) 1430 msg_panic("%s: bad timeout %d", myname, stream->timeout); 1431 break; 1432 case VSTREAM_CTL_EXCEPT: 1433 if (stream->jbuf == 0) 1434 stream->jbuf = 1435 (VSTREAM_JMP_BUF *) mymalloc(sizeof(VSTREAM_JMP_BUF)); 1436 break; 1437 1438 #ifdef VSTREAM_CTL_DUPFD 1439 1440 #define VSTREAM_TRY_DUPFD(backup, fd, floor) do { \ 1441 if (((backup) = (fd)) < floor) { \ 1442 if (((fd) = fcntl((backup), F_DUPFD, (floor))) < 0) \ 1443 msg_fatal("fcntl F_DUPFD %d: %m", (floor)); \ 1444 (void) close(backup); \ 1445 } \ 1446 } while (0) 1447 1448 case VSTREAM_CTL_DUPFD: 1449 floor = va_arg(ap, int); 1450 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { 1451 VSTREAM_TRY_DUPFD(old_fd, stream->read_fd, floor); 1452 if (stream->write_fd == old_fd) 1453 stream->write_fd = stream->read_fd; 1454 else 1455 VSTREAM_TRY_DUPFD(old_fd, stream->write_fd, floor); 1456 stream->fd = (stream->buf.flags & VSTREAM_FLAG_READ) ? 1457 stream->read_fd : stream->write_fd; 1458 } else { 1459 VSTREAM_TRY_DUPFD(old_fd, stream->fd, floor); 1460 } 1461 break; 1462 #endif 1463 1464 /* 1465 * Postpone memory (re)allocation until the space is needed. 1466 */ 1467 case VSTREAM_CTL_BUFSIZE: 1468 req_bufsize = va_arg(ap, ssize_t); 1469 if (req_bufsize < 0) 1470 msg_panic("VSTREAM_CTL_BUFSIZE with negative size: %ld", 1471 (long) req_bufsize); 1472 if (stream != VSTREAM_ERR 1473 && req_bufsize > stream->req_bufsize) 1474 stream->req_bufsize = req_bufsize; 1475 break; 1476 1477 /* 1478 * Make no gettimeofday() etc. system call until we really know 1479 * that we need to do I/O. This avoids a performance hit when 1480 * sending or receiving body content one line at a time. 1481 */ 1482 case VSTREAM_CTL_STOP_DEADLINE: 1483 stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE; 1484 break; 1485 case VSTREAM_CTL_START_DEADLINE: 1486 if (stream->timeout <= 0) 1487 msg_panic("%s: bad timeout %d", myname, stream->timeout); 1488 stream->buf.flags |= VSTREAM_FLAG_DEADLINE; 1489 stream->time_limit.tv_sec = stream->timeout; 1490 stream->time_limit.tv_usec = 0; 1491 break; 1492 default: 1493 msg_panic("%s: bad name %d", myname, name); 1494 } 1495 } 1496 va_end(ap); 1497 } 1498 1499 /* vstream_vprintf - formatted print to stdout */ 1500 1501 VSTREAM *vstream_vprintf(const char *format, va_list ap) 1502 { 1503 VSTREAM *vp = VSTREAM_OUT; 1504 1505 vbuf_print(&vp->buf, format, ap); 1506 return (vp); 1507 } 1508 1509 /* vstream_vfprintf - formatted print engine */ 1510 1511 VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap) 1512 { 1513 vbuf_print(&vp->buf, format, ap); 1514 return (vp); 1515 } 1516 1517 /* vstream_bufstat - get stream buffer status */ 1518 1519 ssize_t vstream_bufstat(VSTREAM *vp, int command) 1520 { 1521 VBUF *bp; 1522 1523 switch (command & VSTREAM_BST_MASK_DIR) { 1524 case VSTREAM_BST_FLAG_IN: 1525 if (vp->buf.flags & VSTREAM_FLAG_READ) { 1526 bp = &vp->buf; 1527 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1528 bp = &vp->read_buf; 1529 } else { 1530 bp = 0; 1531 } 1532 switch (command & ~VSTREAM_BST_MASK_DIR) { 1533 case VSTREAM_BST_FLAG_PEND: 1534 return (bp ? -bp->cnt : 0); 1535 /* Add other requests below. */ 1536 } 1537 break; 1538 case VSTREAM_BST_FLAG_OUT: 1539 if (vp->buf.flags & VSTREAM_FLAG_WRITE) { 1540 bp = &vp->buf; 1541 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1542 bp = &vp->write_buf; 1543 } else { 1544 bp = 0; 1545 } 1546 switch (command & ~VSTREAM_BST_MASK_DIR) { 1547 case VSTREAM_BST_FLAG_PEND: 1548 return (bp ? bp->len - bp->cnt : 0); 1549 /* Add other requests below. */ 1550 } 1551 break; 1552 } 1553 msg_panic("vstream_bufstat: unknown command: %d", command); 1554 } 1555 1556 #undef vstream_peek /* API binary compatibility. */ 1557 1558 /* vstream_peek - peek at a stream */ 1559 1560 ssize_t vstream_peek(VSTREAM *vp) 1561 { 1562 if (vp->buf.flags & VSTREAM_FLAG_READ) { 1563 return (-vp->buf.cnt); 1564 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1565 return (-vp->read_buf.cnt); 1566 } else { 1567 return (0); 1568 } 1569 } 1570 1571 /* vstream_peek_data - peek at unread data */ 1572 1573 const char *vstream_peek_data(VSTREAM *vp) 1574 { 1575 if (vp->buf.flags & VSTREAM_FLAG_READ) { 1576 return ((const char *) vp->buf.ptr); 1577 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { 1578 return ((const char *) vp->read_buf.ptr); 1579 } else { 1580 return (0); 1581 } 1582 } 1583