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