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