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