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