1 /*	$NetBSD: vstream.c,v 1.4 2022/10/08 16:12:50 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 <stdlib.h>			/* 44BSD stdarg.h uses abort() */
528 #include <stdarg.h>
529 #include <stddef.h>
530 #include <unistd.h>
531 #include <fcntl.h>
532 #include <time.h>
533 #include <errno.h>
534 #include <string.h>
535 
536 /* Utility library. */
537 
538 #define VSTRING_INTERNAL
539 
540 #include "mymalloc.h"
541 #include "msg.h"
542 #include "vbuf_print.h"
543 #include "iostuff.h"
544 #include "vstring.h"
545 #include "vstream.h"
546 
547 /* Application-specific. */
548 
549  /*
550   * Forward declarations.
551   */
552 static int vstream_buf_get_ready(VBUF *);
553 static int vstream_buf_put_ready(VBUF *);
554 static int vstream_buf_space(VBUF *, ssize_t);
555 
556  /*
557   * Initialization of the three pre-defined streams. Pre-allocate a static
558   * I/O buffer for the standard error stream, so that the error handler can
559   * produce a diagnostic even when memory allocation fails.
560   */
561 static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE];
562 
563 VSTREAM vstream_fstd[] = {
564     {{
565 	    0,				/* flags */
566 	    0, 0, 0, 0,			/* buffer */
567 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
568     }, STDIN_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
569     0,},
570     {{
571 	    0,				/* flags */
572 	    0, 0, 0, 0,			/* buffer */
573 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
574     }, STDOUT_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
575     0,},
576     {{
577 	    VBUF_FLAG_FIXED | VSTREAM_FLAG_WRITE,
578 	    vstream_fstd_buf, VSTREAM_BUFSIZE, VSTREAM_BUFSIZE, vstream_fstd_buf,
579 	    vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
580     }, STDERR_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
581     VSTREAM_BUFSIZE,},
582 };
583 
584 #define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR)
585 
586  /*
587   * A bunch of macros to make some expressions more readable. XXX We're
588   * assuming that O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2.
589   */
590 #define VSTREAM_ACC_MASK(f)	((f) & (O_APPEND | O_WRONLY | O_RDWR))
591 
592 #define VSTREAM_CAN_READ(f)	(VSTREAM_ACC_MASK(f) == O_RDONLY \
593 				|| VSTREAM_ACC_MASK(f) == O_RDWR)
594 #define VSTREAM_CAN_WRITE(f)	(VSTREAM_ACC_MASK(f) & O_WRONLY \
595 				|| VSTREAM_ACC_MASK(f) & O_RDWR \
596 				|| VSTREAM_ACC_MASK(f) & O_APPEND)
597 
598 #define VSTREAM_BUF_COUNT(bp, n) \
599 	((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n))
600 
601 #define VSTREAM_BUF_AT_START(bp) { \
602 	(bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \
603 	(bp)->ptr = (bp)->data; \
604     }
605 
606 #define VSTREAM_BUF_AT_OFFSET(bp, offset) { \
607 	(bp)->ptr = (bp)->data + (offset); \
608 	(bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \
609     }
610 
611 #define VSTREAM_BUF_AT_END(bp) { \
612 	(bp)->cnt = 0; \
613 	(bp)->ptr = (bp)->data + (bp)->len; \
614     }
615 
616 #define VSTREAM_BUF_ZERO(bp) { \
617 	(bp)->flags = 0; \
618 	(bp)->data = (bp)->ptr = 0; \
619 	(bp)->len = (bp)->cnt = 0; \
620     }
621 
622 #define VSTREAM_BUF_ACTIONS(bp, get_action, put_action, space_action) { \
623 	(bp)->get_ready = (get_action); \
624 	(bp)->put_ready = (put_action); \
625 	(bp)->space = (space_action); \
626     }
627 
628 #define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \
629 	stream->buffer = stream->buf; \
630 	stream->filedes = stream->fd; \
631     }
632 
633 #define VSTREAM_RESTORE_STATE(stream, buffer, filedes) do { \
634 	stream->buffer.flags = stream->buf.flags; \
635 	stream->buf = stream->buffer; \
636 	stream->fd = stream->filedes; \
637     } while(0)
638 
639 #define VSTREAM_FORK_STATE(stream, buffer, filedes) { \
640 	stream->buffer = stream->buf; \
641 	stream->filedes = stream->fd; \
642 	stream->buffer.data = stream->buffer.ptr = 0; \
643 	stream->buffer.len = stream->buffer.cnt = 0; \
644 	stream->buffer.flags &= ~VSTREAM_FLAG_FIXED; \
645     };
646 
647 #define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE)
648 #define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE)
649 
650 #define VSTREAM_FFLUSH_SOME(stream) \
651 	vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt)
652 
653 /* Note: this does not change a negative result into a zero result. */
654 #define VSTREAM_SUB_TIME(x, y, z) \
655     do { \
656 	(x).tv_sec = (y).tv_sec - (z).tv_sec; \
657 	(x).tv_usec = (y).tv_usec - (z).tv_usec; \
658 	while ((x).tv_usec < 0) { \
659 	    (x).tv_usec += 1000000; \
660 	    (x).tv_sec -= 1; \
661 	} \
662 	while ((x).tv_usec >= 1000000) { \
663 	    (x).tv_usec -= 1000000; \
664 	    (x).tv_sec += 1; \
665 	} \
666     } while (0)
667 
668 #define VSTREAM_ADD_TIME(x, y, z) \
669     do { \
670 	(x).tv_sec = (y).tv_sec + (z).tv_sec; \
671 	(x).tv_usec = (y).tv_usec + (z).tv_usec; \
672 	while ((x).tv_usec >= 1000000) { \
673 	    (x).tv_usec -= 1000000; \
674 	    (x).tv_sec += 1; \
675 	} \
676     } while (0)
677 
678 /* vstream_buf_init - initialize buffer */
679 
vstream_buf_init(VBUF * bp,int flags)680 static void vstream_buf_init(VBUF *bp, int flags)
681 {
682 
683     /*
684      * Initialize the buffer such that the first data access triggers a
685      * buffer boundary action.
686      */
687     VSTREAM_BUF_ZERO(bp);
688     VSTREAM_BUF_ACTIONS(bp,
689 			VSTREAM_CAN_READ(flags) ? vstream_buf_get_ready : 0,
690 			VSTREAM_CAN_WRITE(flags) ? vstream_buf_put_ready : 0,
691 			vstream_buf_space);
692 }
693 
694 /* vstream_buf_alloc - allocate buffer memory */
695 
vstream_buf_alloc(VBUF * bp,ssize_t len)696 static void vstream_buf_alloc(VBUF *bp, ssize_t len)
697 {
698     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
699     ssize_t used = bp->ptr - bp->data;
700     const char *myname = "vstream_buf_alloc";
701 
702     if (len < bp->len)
703 	msg_panic("%s: attempt to shrink buffer", myname);
704     if (bp->flags & VSTREAM_FLAG_FIXED)
705 	msg_panic("%s: unable to extend fixed-size buffer", myname);
706 
707     /*
708      * Late buffer allocation allows the user to override the default policy.
709      * If a buffer already exists, allow for the presence of (output) data.
710      */
711     bp->data = (unsigned char *)
712 	(bp->data ? myrealloc((void *) bp->data, len) : mymalloc(len));
713     if (bp->flags & VSTREAM_FLAG_MEMORY)
714 	memset(bp->data + bp->len, 0, len - bp->len);
715     bp->len = len;
716     if (bp->flags & VSTREAM_FLAG_READ) {
717 	bp->ptr = bp->data + used;
718 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
719 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
720     } else {
721 	VSTREAM_BUF_AT_OFFSET(bp, used);
722 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
723 	    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
724     }
725 }
726 
727 /* vstream_buf_wipe - reset buffer to initial state */
728 
vstream_buf_wipe(VBUF * bp)729 static void vstream_buf_wipe(VBUF *bp)
730 {
731     if ((bp->flags & VBUF_FLAG_FIXED) == 0 && bp->data)
732 	myfree((void *) bp->data);
733     VSTREAM_BUF_ZERO(bp);
734     VSTREAM_BUF_ACTIONS(bp, 0, 0, 0);
735 }
736 
737 /* vstream_fflush_some - flush some buffered data */
738 
vstream_fflush_some(VSTREAM * stream,ssize_t to_flush)739 static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
740 {
741     const char *myname = "vstream_fflush_some";
742     VBUF   *bp = &stream->buf;
743     ssize_t used;
744     ssize_t left_over;
745     void   *data;
746     ssize_t len;
747     ssize_t n;
748     int     timeout;
749     struct timeval before;
750     struct timeval elapsed;
751     struct timeval bonus;
752 
753     /*
754      * Sanity checks. It is illegal to flush a read-only stream. Otherwise,
755      * if there is buffered input, discard the input. If there is buffered
756      * output, require that the amount to flush is larger than the amount to
757      * keep, so that we can memcpy() the residue.
758      */
759     if (bp->put_ready == 0)
760 	msg_panic("%s: read-only stream", myname);
761     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
762     case VSTREAM_FLAG_READ:			/* discard input */
763 	VSTREAM_BUF_AT_END(bp);
764 	/* FALLTHROUGH */
765     case 0:					/* flush after seek? */
766 	return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
767     case VSTREAM_FLAG_WRITE:			/* output buffered */
768 	break;
769     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
770 	msg_panic("%s: read/write stream", myname);
771     }
772     used = bp->len - bp->cnt;
773     left_over = used - to_flush;
774 
775     if (msg_verbose > 2 && stream != VSTREAM_ERR)
776 	msg_info("%s: fd %d flush %ld", myname, stream->fd, (long) to_flush);
777     if (to_flush < 0 || left_over < 0)
778 	msg_panic("%s: bad to_flush %ld", myname, (long) to_flush);
779     if (to_flush < left_over)
780 	msg_panic("%s: to_flush < left_over", myname);
781     if (to_flush == 0)
782 	return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
783     if (bp->flags & VSTREAM_FLAG_ERR)
784 	return (VSTREAM_EOF);
785 
786     /*
787      * When flushing a buffer, allow for partial writes. These can happen
788      * while talking to a network. Update the cached file seek position, if
789      * any.
790      *
791      * When deadlines are enabled, we count the elapsed time for each write
792      * operation instead of simply comparing the time-of-day clock with a
793      * per-stream deadline. The latter could result in anomalies when an
794      * application does lengthy processing between write operations. Keep in
795      * mind that a receiver may not be able to keep up when a sender suddenly
796      * floods it with a lot of data as it tries to catch up with a deadline.
797      */
798     for (data = (void *) bp->data, len = to_flush; len > 0; len -= n, data += n) {
799 	if (bp->flags & VSTREAM_FLAG_DEADLINE) {
800 	    timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
801 	    if (timeout <= 0) {
802 		bp->flags |= (VSTREAM_FLAG_WR_ERR | VSTREAM_FLAG_WR_TIMEOUT);
803 		errno = ETIMEDOUT;
804 		return (VSTREAM_EOF);
805 	    }
806 	    if (len == to_flush)
807 		GETTIMEOFDAY(&before);
808 	    else
809 		before = stream->iotime;
810 	} else
811 	    timeout = stream->timeout;
812 	if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) {
813 	    bp->flags |= VSTREAM_FLAG_WR_ERR;
814 	    if (errno == ETIMEDOUT) {
815 		bp->flags |= VSTREAM_FLAG_WR_TIMEOUT;
816 		stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
817 	    }
818 	    return (VSTREAM_EOF);
819 	}
820 	if (timeout) {
821 	    GETTIMEOFDAY(&stream->iotime);
822 	    if (bp->flags & VSTREAM_FLAG_DEADLINE) {
823 		VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
824 		VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
825 		if (stream->min_data_rate > 0) {
826 		    bonus.tv_sec = n / stream->min_data_rate;
827 		    bonus.tv_usec = (n % stream->min_data_rate) * 1000000;
828 		    bonus.tv_usec /= stream->min_data_rate;
829 		    VSTREAM_ADD_TIME(stream->time_limit, stream->time_limit,
830 				     bonus);
831 		    if (stream->time_limit.tv_sec >= stream->timeout) {
832 			stream->time_limit.tv_sec = stream->timeout;
833 			stream->time_limit.tv_usec = 0;
834 		    }
835 		}
836 	    }
837 	}
838 	if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush)
839 	    msg_info("%s: %d flushed %ld/%ld", myname, stream->fd,
840 		     (long) n, (long) to_flush);
841     }
842     if (bp->flags & VSTREAM_FLAG_SEEK)
843 	stream->offset += to_flush;
844 
845     /*
846      * Allow for partial buffer flush requests. We use memcpy() for reasons
847      * of portability to pre-ANSI environments (SunOS 4.x or Ultrix 4.x :-).
848      * This is OK because we have already verified that the to_flush count is
849      * larger than the left_over count.
850      */
851     if (left_over > 0)
852 	memcpy(bp->data, bp->data + to_flush, left_over);
853     bp->cnt += to_flush;
854     bp->ptr -= to_flush;
855     return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
856 }
857 
858 /* vstream_fflush_delayed - delayed stream flush for double-buffered stream */
859 
vstream_fflush_delayed(VSTREAM * stream)860 static int vstream_fflush_delayed(VSTREAM *stream)
861 {
862     int     status;
863 
864     /*
865      * Sanity check.
866      */
867     if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE)
868 	msg_panic("vstream_fflush_delayed: bad flags");
869 
870     /*
871      * Temporarily swap buffers and flush unwritten data. This may seem like
872      * a lot of work, but it's peanuts compared to the write(2) call that we
873      * already have avoided. For example, delayed flush is never used on a
874      * non-pipelined SMTP connection.
875      */
876     stream->buf.flags &= ~VSTREAM_FLAG_READ;
877     VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
878     stream->buf.flags |= VSTREAM_FLAG_WRITE;
879     VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
880 
881     status = VSTREAM_FFLUSH_SOME(stream);
882 
883     stream->buf.flags &= ~VSTREAM_FLAG_WRITE;
884     VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
885     stream->buf.flags |= VSTREAM_FLAG_READ;
886     VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
887 
888     return (status);
889 }
890 
891 /* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */
892 
vstream_buf_get_ready(VBUF * bp)893 static int vstream_buf_get_ready(VBUF *bp)
894 {
895     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
896     const char *myname = "vstream_buf_get_ready";
897     ssize_t n;
898     struct timeval before;
899     struct timeval elapsed;
900     struct timeval bonus;
901     int     timeout;
902 
903     /*
904      * Detect a change of I/O direction or position. If so, flush any
905      * unwritten output immediately when the stream is single-buffered, or
906      * when the stream is double-buffered and the read buffer is empty.
907      */
908     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
909     case VSTREAM_FLAG_WRITE:			/* change direction */
910 	if (bp->ptr > bp->data)
911 	    if ((bp->flags & VSTREAM_FLAG_DOUBLE) == 0
912 		|| stream->read_buf.cnt >= 0)
913 		if (VSTREAM_FFLUSH_SOME(stream))
914 		    return (VSTREAM_EOF);
915 	bp->flags &= ~VSTREAM_FLAG_WRITE;
916 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
917 	    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
918 	/* FALLTHROUGH */
919     case 0:					/* change position */
920 	bp->flags |= VSTREAM_FLAG_READ;
921 	if (bp->flags & VSTREAM_FLAG_DOUBLE) {
922 	    VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
923 	    if (bp->cnt < 0)
924 		return (0);
925 	}
926 	/* FALLTHROUGH */
927     case VSTREAM_FLAG_READ:			/* no change */
928 	break;
929     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
930 	msg_panic("%s: read/write stream", myname);
931     }
932 
933     /*
934      * If this is the first GET operation, allocate a buffer. Late buffer
935      * allocation gives the application a chance to override the default
936      * buffering policy.
937      *
938      * XXX Subtle code to set the preferred buffer size as late as possible.
939      */
940     if (stream->req_bufsize == 0)
941 	stream->req_bufsize = VSTREAM_BUFSIZE;
942     if (bp->len < stream->req_bufsize)
943 	vstream_buf_alloc(bp, stream->req_bufsize);
944 
945     /*
946      * If the stream is double-buffered and the write buffer is not empty,
947      * this is the time to flush the write buffer. Delayed flushes reduce
948      * system call overhead, and on TCP sockets, avoid triggering Nagle's
949      * algorithm.
950      */
951     if ((bp->flags & VSTREAM_FLAG_DOUBLE)
952 	&& stream->write_buf.len > stream->write_buf.cnt)
953 	if (vstream_fflush_delayed(stream))
954 	    return (VSTREAM_EOF);
955 
956     /*
957      * Did we receive an EOF indication?
958      */
959     if (bp->flags & VSTREAM_FLAG_EOF)
960 	return (VSTREAM_EOF);
961 
962     /*
963      * Fill the buffer with as much data as we can handle, or with as much
964      * data as is available right now, whichever is less. Update the cached
965      * file seek position, if any.
966      *
967      * When deadlines are enabled, we count the elapsed time for each read
968      * operation instead of simply comparing the time-of-day clock with a
969      * per-stream deadline. The latter could result in anomalies when an
970      * application does lengthy processing between read operations. Keep in
971      * mind that a sender may get blocked, and may not be able to keep up
972      * when a receiver suddenly wants to read a lot of data as it tries to
973      * catch up with a deadline.
974      */
975     if (bp->flags & VSTREAM_FLAG_DEADLINE) {
976 	timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
977 	if (timeout <= 0) {
978 	    bp->flags |= (VSTREAM_FLAG_RD_ERR | VSTREAM_FLAG_RD_TIMEOUT);
979 	    errno = ETIMEDOUT;
980 	    return (VSTREAM_EOF);
981 	}
982 	GETTIMEOFDAY(&before);
983     } else
984 	timeout = stream->timeout;
985     switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) {
986     case -1:
987 	bp->flags |= VSTREAM_FLAG_RD_ERR;
988 	if (errno == ETIMEDOUT) {
989 	    bp->flags |= VSTREAM_FLAG_RD_TIMEOUT;
990 	    stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
991 	}
992 	return (VSTREAM_EOF);
993     case 0:
994 	bp->flags |= VSTREAM_FLAG_EOF;
995 	return (VSTREAM_EOF);
996     default:
997 	if (timeout) {
998 	    GETTIMEOFDAY(&stream->iotime);
999 	    if (bp->flags & VSTREAM_FLAG_DEADLINE) {
1000 		VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
1001 		VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
1002 		if (stream->min_data_rate > 0) {
1003 		    bonus.tv_sec = n / stream->min_data_rate;
1004 		    bonus.tv_usec = (n % stream->min_data_rate) * 1000000;
1005 		    bonus.tv_usec /= stream->min_data_rate;
1006 		    VSTREAM_ADD_TIME(stream->time_limit, stream->time_limit,
1007 				     bonus);
1008 		    if (stream->time_limit.tv_sec >= stream->timeout) {
1009 			stream->time_limit.tv_sec = stream->timeout;
1010 			stream->time_limit.tv_usec = 0;
1011 		    }
1012 		}
1013 	    }
1014 	}
1015 	if (msg_verbose > 2)
1016 	    msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n);
1017 	bp->cnt = -n;
1018 	bp->ptr = bp->data;
1019 	if (bp->flags & VSTREAM_FLAG_SEEK)
1020 	    stream->offset += n;
1021 	return (0);
1022     }
1023 }
1024 
1025 /* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */
1026 
vstream_buf_put_ready(VBUF * bp)1027 static int vstream_buf_put_ready(VBUF *bp)
1028 {
1029     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
1030     const char *myname = "vstream_buf_put_ready";
1031 
1032     /*
1033      * Sanity checks. Detect a change of I/O direction or position. If so,
1034      * discard unread input, and reset the buffer to the beginning.
1035      */
1036     switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
1037     case VSTREAM_FLAG_READ:			/* change direction */
1038 	bp->flags &= ~VSTREAM_FLAG_READ;
1039 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
1040 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
1041 	/* FALLTHROUGH */
1042     case 0:					/* change position */
1043 	bp->flags |= VSTREAM_FLAG_WRITE;
1044 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
1045 	    VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
1046 	else
1047 	    VSTREAM_BUF_AT_START(bp);
1048 	/* FALLTHROUGH */
1049     case VSTREAM_FLAG_WRITE:			/* no change */
1050 	break;
1051     case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
1052 	msg_panic("%s: read/write stream", myname);
1053     }
1054 
1055     /*
1056      * Remember the direction. If this is the first PUT operation for this
1057      * stream or if the buffer is smaller than the requested size, allocate a
1058      * new buffer; obviously there is no data to be flushed yet. Otherwise,
1059      * flush the buffer.
1060      *
1061      * XXX Subtle code to set the preferred buffer size as late as possible.
1062      */
1063     if (stream->req_bufsize == 0)
1064 	stream->req_bufsize = VSTREAM_BUFSIZE;
1065     if (bp->len < stream->req_bufsize) {
1066 	vstream_buf_alloc(bp, stream->req_bufsize);
1067     } else if (bp->cnt <= 0) {
1068 	if (VSTREAM_FFLUSH_SOME(stream))
1069 	    return (VSTREAM_EOF);
1070     }
1071     return (0);
1072 }
1073 
1074 /* vstream_buf_space - reserve space ahead of time */
1075 
vstream_buf_space(VBUF * bp,ssize_t want)1076 static int vstream_buf_space(VBUF *bp, ssize_t want)
1077 {
1078     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
1079     ssize_t used;
1080     ssize_t incr;
1081     ssize_t shortage;
1082     const char *myname = "vstream_buf_space";
1083 
1084     /*
1085      * Sanity checks. Reserving space implies writing. It is illegal to write
1086      * to a read-only stream. Detect a change of I/O direction or position.
1087      * If so, reset the buffer to the beginning.
1088      */
1089     if (bp->put_ready == 0)
1090 	msg_panic("%s: read-only stream", myname);
1091     if (want < 0)
1092 	msg_panic("%s: bad length %ld", myname, (long) want);
1093     switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
1094     case VSTREAM_FLAG_READ:			/* change direction */
1095 	bp->flags &= ~VSTREAM_FLAG_READ;
1096 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
1097 	    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
1098 	/* FALLTHROUGH */
1099     case 0:					/* change position */
1100 	bp->flags |= VSTREAM_FLAG_WRITE;
1101 	if (bp->flags & VSTREAM_FLAG_DOUBLE)
1102 	    VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
1103 	else
1104 	    VSTREAM_BUF_AT_START(bp);
1105 	/* FALLTHROUGH */
1106     case VSTREAM_FLAG_WRITE:			/* no change */
1107 	break;
1108     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1109 	msg_panic("%s: read/write stream", myname);
1110     }
1111 
1112     /*
1113      * See if enough space is available. If not, flush a multiple of
1114      * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of
1115      * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt
1116      * to keep file updates block-aligned for better performance.
1117      *
1118      * XXX Subtle code to set the preferred buffer size as late as possible.
1119      */
1120 #define VSTREAM_TRUNCATE(count, base)	(((count) / (base)) * (base))
1121 #define VSTREAM_ROUNDUP(count, base)	VSTREAM_TRUNCATE(count + base - 1, base)
1122 
1123     if (stream->req_bufsize == 0)
1124 	stream->req_bufsize = VSTREAM_BUFSIZE;
1125     if (want > bp->cnt) {
1126 	if ((used = bp->len - bp->cnt) > stream->req_bufsize)
1127 	    if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize)))
1128 		return (VSTREAM_EOF);
1129 	if ((shortage = (want - bp->cnt)) > 0) {
1130 	    if ((bp->flags & VSTREAM_FLAG_FIXED)
1131 		|| shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) {
1132 		bp->flags |= VSTREAM_FLAG_WR_ERR;
1133 	    } else {
1134 		incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize);
1135 		vstream_buf_alloc(bp, bp->len + incr);
1136 	    }
1137 	}
1138     }
1139     return (vstream_ferror(stream) ? VSTREAM_EOF : 0);	/* mmap() may fail */
1140 }
1141 
1142 /* vstream_fpurge - discard unread or unwritten content */
1143 
vstream_fpurge(VSTREAM * stream,int direction)1144 int     vstream_fpurge(VSTREAM *stream, int direction)
1145 {
1146     const char *myname = "vstream_fpurge";
1147     VBUF   *bp = &stream->buf;
1148 
1149 #define VSTREAM_MAYBE_PURGE_WRITE(d, b) if ((d) & VSTREAM_PURGE_WRITE) \
1150 	VSTREAM_BUF_AT_START((b))
1151 #define VSTREAM_MAYBE_PURGE_READ(d, b) if ((d) & VSTREAM_PURGE_READ) \
1152 	VSTREAM_BUF_AT_END((b))
1153 
1154     /*
1155      * To discard all unread contents, position the read buffer at its end,
1156      * so that we skip over any unread data, and so that the next read
1157      * operation will refill the buffer.
1158      *
1159      * To discard all unwritten content, position the write buffer at its
1160      * beginning, so that the next write operation clobbers any unwritten
1161      * data.
1162      */
1163     switch (bp->flags & (VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE)) {
1164     case VSTREAM_FLAG_READ_DOUBLE:
1165 	VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
1166 	/* FALLTHROUGH */
1167     case VSTREAM_FLAG_READ:
1168 	VSTREAM_MAYBE_PURGE_READ(direction, bp);
1169 	break;
1170     case VSTREAM_FLAG_DOUBLE:
1171 	VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
1172 	VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
1173 	break;
1174     case VSTREAM_FLAG_WRITE_DOUBLE:
1175 	VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
1176 	/* FALLTHROUGH */
1177     case VSTREAM_FLAG_WRITE:
1178 	VSTREAM_MAYBE_PURGE_WRITE(direction, bp);
1179 	break;
1180     case VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE:
1181     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1182 	msg_panic("%s: read/write stream", myname);
1183     }
1184 
1185     /*
1186      * Invalidate the cached file seek position.
1187      */
1188     bp->flags &= ~VSTREAM_FLAG_SEEK;
1189     stream->offset = 0;
1190 
1191     return (0);
1192 }
1193 
1194 /* vstream_fseek - change I/O position */
1195 
vstream_fseek(VSTREAM * stream,off_t offset,int whence)1196 off_t   vstream_fseek(VSTREAM *stream, off_t offset, int whence)
1197 {
1198     const char *myname = "vstream_fseek";
1199     VBUF   *bp = &stream->buf;
1200 
1201     /*
1202      * TODO: support data length (data length != buffer length). Without data
1203      * length information, Without explicit data length information,
1204      * vstream_memopen(O_RDONLY) has to set the VSTREAM buffer length to the
1205      * vstring payload length to avoid accessing unwritten data after
1206      * vstream_fseek(), because for lseek() compatibility, vstream_fseek()
1207      * must allow seeking past the end of a file.
1208      */
1209     if (stream->buf.flags & VSTREAM_FLAG_MEMORY) {
1210 	if (whence == SEEK_CUR)
1211 	    offset += (bp->ptr - bp->data);
1212 	else if (whence == SEEK_END)
1213 	    offset += bp->len;
1214 	if (offset < 0) {
1215 	    errno = EINVAL;
1216 	    return (-1);
1217 	}
1218 	if (offset > bp->len && (bp->flags & VSTREAM_FLAG_WRITE))
1219 	    vstream_buf_space(bp, offset - bp->len);
1220 	VSTREAM_BUF_AT_OFFSET(bp, offset);
1221 	return (offset);
1222     }
1223 
1224     /*
1225      * Flush any unwritten output. Discard any unread input. Position the
1226      * buffer at the end, so that the next GET or PUT operation triggers a
1227      * buffer boundary action.
1228      */
1229     switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
1230     case VSTREAM_FLAG_WRITE:
1231 	if (bp->ptr > bp->data) {
1232 	    if (whence == SEEK_CUR)
1233 		offset += (bp->ptr - bp->data);	/* add unwritten data */
1234 	    else if (whence == SEEK_END)
1235 		bp->flags &= ~VSTREAM_FLAG_SEEK;
1236 	    if (VSTREAM_FFLUSH_SOME(stream))
1237 		return (-1);
1238 	}
1239 	VSTREAM_BUF_AT_END(bp);
1240 	break;
1241     case VSTREAM_FLAG_READ:
1242 	if (whence == SEEK_CUR)
1243 	    offset += bp->cnt;			/* subtract unread data */
1244 	else if (whence == SEEK_END)
1245 	    bp->flags &= ~VSTREAM_FLAG_SEEK;
1246 	/* FALLTHROUGH */
1247     case 0:
1248 	VSTREAM_BUF_AT_END(bp);
1249 	break;
1250     case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1251 	msg_panic("%s: read/write stream", myname);
1252     }
1253 
1254     /*
1255      * Clear the read/write flags to inform the buffer boundary action
1256      * routines that we may have changed I/O position.
1257      */
1258     bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE);
1259 
1260     /*
1261      * Shave an unnecessary system call.
1262      */
1263     if (bp->flags & VSTREAM_FLAG_NSEEK) {
1264 	errno = ESPIPE;
1265 	return (-1);
1266     }
1267 
1268     /*
1269      * Update the cached file seek position.
1270      */
1271     if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) {
1272 	if (errno == ESPIPE)
1273 	    bp->flags |= VSTREAM_FLAG_NSEEK;
1274     } else {
1275 	bp->flags |= VSTREAM_FLAG_SEEK;
1276     }
1277     bp->flags &= ~VSTREAM_FLAG_EOF;
1278     return (stream->offset);
1279 }
1280 
1281 /* vstream_ftell - return file offset */
1282 
vstream_ftell(VSTREAM * stream)1283 off_t   vstream_ftell(VSTREAM *stream)
1284 {
1285     VBUF   *bp = &stream->buf;
1286 
1287     /*
1288      * Special case for memory buffer.
1289      */
1290     if (stream->buf.flags & VSTREAM_FLAG_MEMORY)
1291 	return (bp->ptr - bp->data);
1292 
1293     /*
1294      * Shave an unnecessary syscall.
1295      */
1296     if (bp->flags & VSTREAM_FLAG_NSEEK) {
1297 	errno = ESPIPE;
1298 	return (-1);
1299     }
1300 
1301     /*
1302      * Use the cached file offset when available. This is the offset after
1303      * the last read, write or seek operation.
1304      */
1305     if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) {
1306 	if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) {
1307 	    bp->flags |= VSTREAM_FLAG_NSEEK;
1308 	    return (-1);
1309 	}
1310 	bp->flags |= VSTREAM_FLAG_SEEK;
1311     }
1312 
1313     /*
1314      * If this is a read buffer, subtract the number of unread bytes from the
1315      * cached offset. Remember that read counts are negative.
1316      */
1317     if (bp->flags & VSTREAM_FLAG_READ)
1318 	return (stream->offset + bp->cnt);
1319 
1320     /*
1321      * If this is a write buffer, add the number of unwritten bytes to the
1322      * cached offset.
1323      */
1324     if (bp->flags & VSTREAM_FLAG_WRITE)
1325 	return (stream->offset + (bp->ptr - bp->data));
1326 
1327     /*
1328      * Apparently, this is a new buffer, or a buffer after seek, so there is
1329      * no need to account for unread or unwritten data.
1330      */
1331     return (stream->offset);
1332 }
1333 
1334 /* vstream_subopen - initialize everything except buffers and I/O handlers */
1335 
vstream_subopen(void)1336 static VSTREAM *vstream_subopen(void)
1337 {
1338     VSTREAM *stream;
1339 
1340     /* Note: memset() is not a portable way to initialize non-integer types. */
1341     stream = (VSTREAM *) mymalloc(sizeof(*stream));
1342     stream->offset = 0;
1343     stream->path = 0;
1344     stream->pid = 0;
1345     stream->waitpid_fn = 0;
1346     stream->timeout = 0;
1347     stream->context = 0;
1348     stream->jbuf = 0;
1349     stream->iotime.tv_sec = stream->iotime.tv_usec = 0;
1350     stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
1351     stream->req_bufsize = 0;
1352     stream->vstring = 0;
1353     stream->min_data_rate = 0;
1354     return (stream);
1355 }
1356 
1357 /* vstream_fdopen - add buffering to pre-opened stream */
1358 
vstream_fdopen(int fd,int flags)1359 VSTREAM *vstream_fdopen(int fd, int flags)
1360 {
1361     VSTREAM *stream;
1362 
1363     /*
1364      * Sanity check.
1365      */
1366     if (fd < 0)
1367 	msg_panic("vstream_fdopen: bad file %d", fd);
1368 
1369     /*
1370      * Initialize buffers etc. but do as little as possible. Late buffer
1371      * allocation etc. gives the application a chance to override default
1372      * policies. Either this, or the vstream*open() routines would have to
1373      * have a really ugly interface with lots of mostly-unused arguments (can
1374      * you say VMS?).
1375      */
1376     stream = vstream_subopen();
1377     stream->fd = fd;
1378     stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_RW_FN) timed_read : 0;
1379     stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_RW_FN) timed_write : 0;
1380     vstream_buf_init(&stream->buf, flags);
1381     return (stream);
1382 }
1383 
1384 /* vstream_fopen - open buffered file stream */
1385 
vstream_fopen(const char * path,int flags,mode_t mode)1386 VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode)
1387 {
1388     VSTREAM *stream;
1389     int     fd;
1390 
1391     if ((fd = open(path, flags, mode)) < 0) {
1392 	return (0);
1393     } else {
1394 	stream = vstream_fdopen(fd, flags);
1395 	stream->path = mystrdup(path);
1396 	return (stream);
1397     }
1398 }
1399 
1400 /* vstream_fflush - flush write buffer */
1401 
vstream_fflush(VSTREAM * stream)1402 int     vstream_fflush(VSTREAM *stream)
1403 {
1404 
1405     /*
1406      * With VSTRING, the write pointer must be positioned behind the end of
1407      * data. But vstream_fseek() changes the write position, and causes the
1408      * data length to be forgotten. Before flushing to vstream, remember the
1409      * current write position, move the write pointer and do what needs to be
1410      * done, then move the write pointer back to the saved location.
1411      */
1412     if (stream->buf.flags & VSTREAM_FLAG_MEMORY) {
1413 	if (stream->buf.flags & VSTREAM_FLAG_WRITE) {
1414 	    VSTRING *string = stream->vstring;
1415 
1416 #ifdef PENDING_VSTREAM_FSEEK_FOR_MEMORY
1417 	    VSTREAM_BUF_AT_OFFSET(&stream->buf, stream->buf.data_len);
1418 #endif
1419 	    memcpy(&string->vbuf, &stream->buf, sizeof(stream->buf));
1420 	    string->vbuf.flags &= VSTRING_FLAG_MASK;
1421 	    VSTRING_TERMINATE(string);
1422 	}
1423 	return (0);
1424     }
1425     if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE)
1426 	== VSTREAM_FLAG_READ_DOUBLE
1427 	&& stream->write_buf.len > stream->write_buf.cnt)
1428 	vstream_fflush_delayed(stream);
1429     return (VSTREAM_FFLUSH_SOME(stream));
1430 }
1431 
1432 /* vstream_fclose - close buffered stream */
1433 
vstream_fclose(VSTREAM * stream)1434 int     vstream_fclose(VSTREAM *stream)
1435 {
1436     int     err;
1437 
1438     /*
1439      * NOTE: Negative file descriptors are not part of the external
1440      * interface. They are for internal use only, in order to support
1441      * vstream_fdclose() without a lot of code duplication. Applications that
1442      * rely on negative VSTREAM file descriptors will break without warning.
1443      */
1444     if (stream->pid != 0)
1445 	msg_panic("vstream_fclose: stream has process");
1446     if ((stream->buf.flags & VSTREAM_FLAG_MEMORY)
1447 	|| ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0
1448 	    && stream->fd >= 0))
1449 	vstream_fflush(stream);
1450     /* Do not remove: vstream_fdclose() depends on this error test. */
1451     err = vstream_ferror(stream);
1452     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1453 	if (stream->read_fd >= 0)
1454 	    err |= close(stream->read_fd);
1455 	if (stream->write_fd != stream->read_fd)
1456 	    if (stream->write_fd >= 0)
1457 		err |= close(stream->write_fd);
1458 	vstream_buf_wipe(&stream->read_buf);
1459 	vstream_buf_wipe(&stream->write_buf);
1460 	stream->buf = stream->read_buf;
1461     } else {
1462 	if (stream->fd >= 0)
1463 	    err |= close(stream->fd);
1464 	if ((stream->buf.flags & VSTREAM_FLAG_MEMORY) == 0)
1465 	    vstream_buf_wipe(&stream->buf);
1466     }
1467     if (stream->path)
1468 	myfree(stream->path);
1469     if (stream->jbuf)
1470 	myfree((void *) stream->jbuf);
1471     if (stream->vstring && (stream->buf.flags & VSTREAM_FLAG_OWN_VSTRING))
1472 	vstring_free(stream->vstring);
1473     if (!VSTREAM_STATIC(stream))
1474 	myfree((void *) stream);
1475     return (err ? VSTREAM_EOF : 0);
1476 }
1477 
1478 /* vstream_fdclose - close stream, leave file(s) open */
1479 
vstream_fdclose(VSTREAM * stream)1480 int     vstream_fdclose(VSTREAM *stream)
1481 {
1482 
1483     /*
1484      * Flush unwritten output, just like vstream_fclose(). Errors are
1485      * reported by vstream_fclose().
1486      */
1487     if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0)
1488 	(void) vstream_fflush(stream);
1489 
1490     /*
1491      * NOTE: Negative file descriptors are not part of the external
1492      * interface. They are for internal use only, in order to support
1493      * vstream_fdclose() without a lot of code duplication. Applications that
1494      * rely on negative VSTREAM file descriptors will break without warning.
1495      */
1496     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1497 	stream->fd = stream->read_fd = stream->write_fd = -1;
1498     } else {
1499 	stream->fd = -1;
1500     }
1501     return (vstream_fclose(stream));
1502 }
1503 
1504 /* vstream_printf - formatted print to stdout */
1505 
vstream_printf(const char * fmt,...)1506 VSTREAM *vstream_printf(const char *fmt,...)
1507 {
1508     VSTREAM *stream = VSTREAM_OUT;
1509     va_list ap;
1510 
1511     va_start(ap, fmt);
1512     vbuf_print(&stream->buf, fmt, ap);
1513     va_end(ap);
1514     return (stream);
1515 }
1516 
1517 /* vstream_fprintf - formatted print to buffered stream */
1518 
vstream_fprintf(VSTREAM * stream,const char * fmt,...)1519 VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...)
1520 {
1521     va_list ap;
1522 
1523     va_start(ap, fmt);
1524     vbuf_print(&stream->buf, fmt, ap);
1525     va_end(ap);
1526     return (stream);
1527 }
1528 
1529 /* vstream_fputs - write string to stream */
1530 
vstream_fputs(const char * str,VSTREAM * stream)1531 int     vstream_fputs(const char *str, VSTREAM *stream)
1532 {
1533     int     ch;
1534 
1535     while ((ch = *str++) != 0)
1536 	if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF)
1537 	    return (VSTREAM_EOF);
1538     return (0);
1539 }
1540 
1541 /* vstream_fread_buf - unformatted read to VSTRING */
1542 
vstream_fread_buf(VSTREAM * fp,VSTRING * vp,ssize_t len)1543 ssize_t vstream_fread_buf(VSTREAM *fp, VSTRING *vp, ssize_t len)
1544 {
1545     ssize_t ret;
1546 
1547     VSTRING_RESET(vp);
1548     VSTRING_SPACE(vp, len);
1549     ret = vstream_fread(fp, vstring_str(vp), len);
1550     if (ret > 0)
1551 	VSTRING_AT_OFFSET(vp, ret);
1552     return (ret);
1553 }
1554 
1555 /* vstream_fread_app - unformatted read to VSTRING */
1556 
vstream_fread_app(VSTREAM * fp,VSTRING * vp,ssize_t len)1557 ssize_t vstream_fread_app(VSTREAM *fp, VSTRING *vp, ssize_t len)
1558 {
1559     ssize_t ret;
1560 
1561     VSTRING_SPACE(vp, len);
1562     ret = vstream_fread(fp, vstring_end(vp), len);
1563     if (ret > 0)
1564 	VSTRING_AT_OFFSET(vp, VSTRING_LEN(vp) + ret);
1565     return (ret);
1566 }
1567 
1568 /* vstream_control - fine control */
1569 
vstream_control(VSTREAM * stream,int name,...)1570 void    vstream_control(VSTREAM *stream, int name,...)
1571 {
1572     const char *myname = "vstream_control";
1573     va_list ap;
1574     int     floor;
1575     int     old_fd;
1576     ssize_t req_bufsize = 0;
1577     VSTREAM *stream2;
1578     int     min_data_rate;
1579 
1580 #define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0)
1581 
1582     /*
1583      * A crude 'allow' filter for memory streams.
1584      */
1585     int     memory_ops =
1586     ((1 << VSTREAM_CTL_END) | (1 << VSTREAM_CTL_CONTEXT)
1587      | (1 << VSTREAM_CTL_PATH) | (1 << VSTREAM_CTL_EXCEPT)
1588      | (1 << VSTREAM_CTL_OWN_VSTRING));
1589 
1590     for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) {
1591 	if ((stream->buf.flags & VSTREAM_FLAG_MEMORY)
1592 	    && (memory_ops & (1 << name)) == 0)
1593 	    msg_panic("%s: memory stream does not support VSTREAM_CTL_%d",
1594 		      VSTREAM_PATH(stream), name);
1595 	switch (name) {
1596 	case VSTREAM_CTL_READ_FN:
1597 	    stream->read_fn = va_arg(ap, VSTREAM_RW_FN);
1598 	    break;
1599 	case VSTREAM_CTL_WRITE_FN:
1600 	    stream->write_fn = va_arg(ap, VSTREAM_RW_FN);
1601 	    break;
1602 	case VSTREAM_CTL_CONTEXT:
1603 	    stream->context = va_arg(ap, void *);
1604 	    break;
1605 	case VSTREAM_CTL_PATH:
1606 	    if (stream->path)
1607 		myfree(stream->path);
1608 	    stream->path = mystrdup(va_arg(ap, char *));
1609 	    break;
1610 	case VSTREAM_CTL_DOUBLE:
1611 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) {
1612 		stream->buf.flags |= VSTREAM_FLAG_DOUBLE;
1613 		if (stream->buf.flags & VSTREAM_FLAG_READ) {
1614 		    VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
1615 		    VSTREAM_FORK_STATE(stream, write_buf, write_fd);
1616 		} else {
1617 		    VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
1618 		    VSTREAM_FORK_STATE(stream, read_buf, read_fd);
1619 		}
1620 	    }
1621 	    break;
1622 	case VSTREAM_CTL_READ_FD:
1623 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
1624 		msg_panic("VSTREAM_CTL_READ_FD requires double buffering");
1625 	    stream->read_fd = va_arg(ap, int);
1626 	    stream->buf.flags |= VSTREAM_FLAG_NSEEK;
1627 	    break;
1628 	case VSTREAM_CTL_WRITE_FD:
1629 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
1630 		msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering");
1631 	    stream->write_fd = va_arg(ap, int);
1632 	    stream->buf.flags |= VSTREAM_FLAG_NSEEK;
1633 	    break;
1634 	case VSTREAM_CTL_SWAP_FD:
1635 	    stream2 = va_arg(ap, VSTREAM *);
1636 	    if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE)
1637 		!= (stream2->buf.flags & VSTREAM_FLAG_DOUBLE))
1638 		msg_panic("VSTREAM_CTL_SWAP_FD can't swap descriptors between "
1639 			  "single-buffered and double-buffered streams");
1640 	    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1641 		SWAP(int, stream->read_fd, stream2->read_fd);
1642 		SWAP(int, stream->write_fd, stream2->write_fd);
1643 		stream->fd = ((stream->buf.flags & VSTREAM_FLAG_WRITE) ?
1644 			      stream->write_fd : stream->read_fd);
1645 	    } else {
1646 		SWAP(int, stream->fd, stream2->fd);
1647 	    }
1648 	    break;
1649 	case VSTREAM_CTL_TIMEOUT:
1650 	    if (stream->timeout == 0)
1651 		GETTIMEOFDAY(&stream->iotime);
1652 	    stream->timeout = va_arg(ap, int);
1653 	    if (stream->timeout < 0)
1654 		msg_panic("%s: bad timeout %d", myname, stream->timeout);
1655 	    break;
1656 	case VSTREAM_CTL_EXCEPT:
1657 	    if (stream->jbuf == 0)
1658 		stream->jbuf =
1659 		    (VSTREAM_JMP_BUF *) mymalloc(sizeof(VSTREAM_JMP_BUF));
1660 	    break;
1661 
1662 #ifdef VSTREAM_CTL_DUPFD
1663 
1664 #define VSTREAM_TRY_DUPFD(backup, fd, floor) do { \
1665 	if (((backup) = (fd)) < floor) { \
1666 	    if (((fd) = fcntl((backup), F_DUPFD, (floor))) < 0) \
1667 		msg_fatal("fcntl F_DUPFD %d: %m", (floor)); \
1668 	    (void) close(backup); \
1669 	} \
1670     } while (0)
1671 
1672 	case VSTREAM_CTL_DUPFD:
1673 	    floor = va_arg(ap, int);
1674 	    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1675 		VSTREAM_TRY_DUPFD(old_fd, stream->read_fd, floor);
1676 		if (stream->write_fd == old_fd)
1677 		    stream->write_fd = stream->read_fd;
1678 		else
1679 		    VSTREAM_TRY_DUPFD(old_fd, stream->write_fd, floor);
1680 		stream->fd = (stream->buf.flags & VSTREAM_FLAG_READ) ?
1681 		    stream->read_fd : stream->write_fd;
1682 	    } else {
1683 		VSTREAM_TRY_DUPFD(old_fd, stream->fd, floor);
1684 	    }
1685 	    break;
1686 #endif
1687 
1688 	    /*
1689 	     * Postpone memory (re)allocation until the space is needed.
1690 	     */
1691 	case VSTREAM_CTL_BUFSIZE:
1692 	    req_bufsize = va_arg(ap, ssize_t);
1693 	    /* Heuristic to detect missing (ssize_t) type cast on LP64 hosts. */
1694 	    if (req_bufsize < 0 || req_bufsize > INT_MAX)
1695 		msg_panic("unreasonable VSTREAM_CTL_BUFSIZE request: %ld",
1696 			  (long) req_bufsize);
1697 	    if ((stream->buf.flags & VSTREAM_FLAG_FIXED) == 0
1698 		&& req_bufsize > stream->req_bufsize) {
1699 		if (msg_verbose)
1700 		    msg_info("fd=%d: stream buffer size old=%ld new=%ld",
1701 			     vstream_fileno(stream),
1702 			     (long) stream->req_bufsize,
1703 			     (long) req_bufsize);
1704 		stream->req_bufsize = req_bufsize;
1705 	    }
1706 	    break;
1707 
1708 	    /*
1709 	     * Make no gettimeofday() etc. system call until we really know
1710 	     * that we need to do I/O. This avoids a performance hit when
1711 	     * sending or receiving body content one line at a time.
1712 	     */
1713 	case VSTREAM_CTL_STOP_DEADLINE:
1714 	    stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE;
1715 	    break;
1716 	case VSTREAM_CTL_START_DEADLINE:
1717 	    if (stream->timeout <= 0)
1718 		msg_panic("%s: bad timeout %d", myname, stream->timeout);
1719 	    stream->buf.flags |= VSTREAM_FLAG_DEADLINE;
1720 	    stream->time_limit.tv_sec = stream->timeout;
1721 	    stream->time_limit.tv_usec = 0;
1722 	    break;
1723 	case VSTREAM_CTL_MIN_DATA_RATE:
1724 	    min_data_rate = va_arg(ap, int);
1725 	    if (min_data_rate < 0)
1726 		msg_panic("%s: bad min_data_rate %d", myname, min_data_rate);
1727 	    stream->min_data_rate = min_data_rate;
1728 	    break;
1729 	case VSTREAM_CTL_OWN_VSTRING:
1730 	    if ((stream->buf.flags |= VSTREAM_FLAG_MEMORY) == 0)
1731 		msg_panic("%s: operation on non-VSTRING stream", myname);
1732 	    stream->buf.flags |= VSTREAM_FLAG_OWN_VSTRING;
1733 	    break;
1734 	default:
1735 	    msg_panic("%s: bad name %d", myname, name);
1736 	}
1737     }
1738     va_end(ap);
1739 }
1740 
1741 /* vstream_vprintf - formatted print to stdout */
1742 
vstream_vprintf(const char * format,va_list ap)1743 VSTREAM *vstream_vprintf(const char *format, va_list ap)
1744 {
1745     VSTREAM *vp = VSTREAM_OUT;
1746 
1747     vbuf_print(&vp->buf, format, ap);
1748     return (vp);
1749 }
1750 
1751 /* vstream_vfprintf - formatted print engine */
1752 
vstream_vfprintf(VSTREAM * vp,const char * format,va_list ap)1753 VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap)
1754 {
1755     vbuf_print(&vp->buf, format, ap);
1756     return (vp);
1757 }
1758 
1759 /* vstream_bufstat - get stream buffer status */
1760 
vstream_bufstat(VSTREAM * vp,int command)1761 ssize_t vstream_bufstat(VSTREAM *vp, int command)
1762 {
1763     VBUF   *bp;
1764 
1765     switch (command & VSTREAM_BST_MASK_DIR) {
1766     case VSTREAM_BST_FLAG_IN:
1767 	if (vp->buf.flags & VSTREAM_FLAG_READ) {
1768 	    bp = &vp->buf;
1769 	} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1770 	    bp = &vp->read_buf;
1771 	} else {
1772 	    bp = 0;
1773 	}
1774 	switch (command & ~VSTREAM_BST_MASK_DIR) {
1775 	case VSTREAM_BST_FLAG_PEND:
1776 	    return (bp ? -bp->cnt : 0);
1777 	    /* Add other requests below. */
1778 	}
1779 	break;
1780     case VSTREAM_BST_FLAG_OUT:
1781 	if (vp->buf.flags & VSTREAM_FLAG_WRITE) {
1782 	    bp = &vp->buf;
1783 	} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1784 	    bp = &vp->write_buf;
1785 	} else {
1786 	    bp = 0;
1787 	}
1788 	switch (command & ~VSTREAM_BST_MASK_DIR) {
1789 	case VSTREAM_BST_FLAG_PEND:
1790 	    return (bp ? bp->len - bp->cnt : 0);
1791 	    /* Add other requests below. */
1792 	}
1793 	break;
1794     }
1795     msg_panic("vstream_bufstat: unknown command: %d", command);
1796 }
1797 
1798 #undef vstream_peek			/* API binary compatibility. */
1799 
1800 /* vstream_peek - peek at a stream */
1801 
vstream_peek(VSTREAM * vp)1802 ssize_t vstream_peek(VSTREAM *vp)
1803 {
1804     if (vp->buf.flags & VSTREAM_FLAG_READ) {
1805 	return (-vp->buf.cnt);
1806     } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1807 	return (-vp->read_buf.cnt);
1808     } else {
1809 	return (0);
1810     }
1811 }
1812 
1813 /* vstream_peek_data - peek at unread data */
1814 
vstream_peek_data(VSTREAM * vp)1815 const char *vstream_peek_data(VSTREAM *vp)
1816 {
1817     if (vp->buf.flags & VSTREAM_FLAG_READ) {
1818 	return ((const char *) vp->buf.ptr);
1819     } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1820 	return ((const char *) vp->read_buf.ptr);
1821     } else {
1822 	return (0);
1823     }
1824 }
1825 
1826 /* vstream_memopen - open a VSTRING */
1827 
vstream_memreopen(VSTREAM * stream,VSTRING * string,int flags)1828 VSTREAM *vstream_memreopen(VSTREAM *stream, VSTRING *string, int flags)
1829 {
1830     if (stream == 0)
1831 	stream = vstream_subopen();
1832     else if ((stream->buf.flags & VSTREAM_FLAG_MEMORY) == 0)
1833 	msg_panic("vstream_memreopen: cannot reopen non-memory stream");
1834     stream->fd = -1;
1835     stream->read_fn = 0;
1836     stream->write_fn = 0;
1837     stream->vstring = string;
1838     memcpy(&stream->buf, &stream->vstring->vbuf, sizeof(stream->buf));
1839     stream->buf.flags |= VSTREAM_FLAG_MEMORY;
1840     switch (VSTREAM_ACC_MASK(flags)) {
1841     case O_RDONLY:
1842 	stream->buf.flags |= VSTREAM_FLAG_READ;
1843 	/* Prevent reading unwritten data after vstream_fseek(). */
1844 	stream->buf.len = stream->buf.ptr - stream->buf.data;
1845 	VSTREAM_BUF_AT_OFFSET(&stream->buf, 0);
1846 	break;
1847     case O_WRONLY:
1848 	stream->buf.flags |= VSTREAM_FLAG_WRITE;
1849 	VSTREAM_BUF_AT_OFFSET(&stream->buf, 0);
1850 	break;
1851     case O_APPEND:
1852 	stream->buf.flags |= VSTREAM_FLAG_WRITE;
1853 	VSTREAM_BUF_AT_OFFSET(&stream->buf,
1854 			      stream->buf.ptr - stream->buf.data);
1855 	break;
1856     default:
1857 	msg_panic("vstream_memopen: flags must be one of "
1858 		  "O_RDONLY, O_WRONLY, or O_APPEND");
1859     }
1860     return (stream);
1861 }
1862 
1863 #ifdef TEST
1864 
copy_line(ssize_t bufsize)1865 static void copy_line(ssize_t bufsize)
1866 {
1867     int     c;
1868 
1869     /*
1870      * Demonstrates that VSTREAM_CTL_BUFSIZE increases the buffer size, but
1871      * does not decrease it. Uses VSTREAM_ERR for non-test output to avoid
1872      * interfering with the test.
1873      */
1874     vstream_fprintf(VSTREAM_ERR, "buffer size test: copy text with %ld buffer size, ignore requests to shrink\n",
1875 		    (long) bufsize);
1876     vstream_fflush(VSTREAM_ERR);
1877     vstream_control(VSTREAM_IN, CA_VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
1878     vstream_control(VSTREAM_OUT, CA_VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
1879     while ((c = VSTREAM_GETC(VSTREAM_IN)) != VSTREAM_EOF) {
1880 	VSTREAM_PUTC(c, VSTREAM_OUT);
1881 	if (c == '\n')
1882 	    break;
1883     }
1884     vstream_fflush(VSTREAM_OUT);
1885     vstream_fprintf(VSTREAM_ERR, "actual read/write buffer sizes: %ld/%ld\n\n",
1886 		    (long) VSTREAM_IN->buf.len, (long) VSTREAM_OUT->buf.len);
1887     vstream_fflush(VSTREAM_ERR);
1888 }
1889 
printf_number(void)1890 static void printf_number(void)
1891 {
1892 
1893     /*
1894      * Demonstrates that vstream_printf() use vbuf_print().
1895      */
1896     vstream_printf("formatting test: print a number\n");
1897     vstream_printf("%d\n\n", 1234567890);
1898     vstream_fflush(VSTREAM_OUT);
1899 }
1900 
do_memory_stream(void)1901 static void do_memory_stream(void)
1902 {
1903     VSTRING *buf = vstring_alloc(1);
1904     VSTREAM *fp;
1905     off_t   offset;
1906     int     ch;
1907 
1908     /*
1909      * Preload the string.
1910      */
1911     vstream_printf("memory stream test prep: prefill the VSTRING\n");
1912     vstring_strcpy(buf, "01234567");
1913     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
1914 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
1915 		   vstring_str(buf));
1916     VSTREAM_PUTCHAR('\n');
1917     vstream_fflush(VSTREAM_OUT);
1918 
1919     /*
1920      * Test: open the memory VSTREAM in write-only mode, and clobber it.
1921      */
1922     vstream_printf("memory stream test: open the VSTRING for writing, overwrite, close\n");
1923     fp = vstream_memopen(buf, O_WRONLY);
1924     vstream_printf("initial memory VSTREAM write offset: %ld/%ld\n",
1925 		   (long) vstream_ftell(fp), (long) fp->buf.len);
1926     vstream_fprintf(fp, "hallo");
1927     vstream_printf("final memory VSTREAM write offset: %ld/%ld\n",
1928 		   (long) vstream_ftell(fp), (long) fp->buf.len);
1929     vstream_fclose(fp);
1930     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
1931 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
1932 		   vstring_str(buf));
1933     VSTREAM_PUTCHAR('\n');
1934     vstream_fflush(VSTREAM_OUT);
1935 
1936     /*
1937      * Test: open the memory VSTREAM for append. vstream_memopen() sets the
1938      * buffer length to the VSTRING buffer length, and positions the write
1939      * pointer at the VSTRING write position. Write some content, then
1940      * overwrite one character.
1941      */
1942     vstream_printf("memory stream test: open the VSTRING for append, write multiple, then overwrite 1\n");
1943     fp = vstream_memopen(buf, O_APPEND);
1944     vstream_printf("initial memory VSTREAM write offset: %ld/%ld\n",
1945 		   (long) vstream_ftell(fp), (long) fp->buf.len);
1946     vstream_fprintf(fp, " world");
1947     vstream_printf("final memory VSTREAM write offset: %ld/%ld\n",
1948 		   (long) vstream_ftell(fp), (long) fp->buf.len);
1949     if (vstream_fflush(fp))
1950 	msg_fatal("vstream_fflush: %m");
1951     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
1952 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
1953 		   vstring_str(buf));
1954     VSTREAM_PUTCHAR('\n');
1955 
1956     /*
1957      * While the stream is still open, replace the second character.
1958      */
1959     vstream_printf("replace second character and close\n");
1960     if ((offset = vstream_fseek(fp, 1, SEEK_SET)) != 1)
1961 	msg_panic("unexpected vstream_fseek return: %ld, expected: %ld",
1962 		  (long) offset, (long) 1);
1963     VSTREAM_PUTC('e', fp);
1964 
1965     /*
1966      * Skip to the end of the content, so that vstream_fflush() will update
1967      * the VSTRING with the right content length.
1968      */
1969     if ((offset = vstream_fseek(fp, VSTRING_LEN(buf), SEEK_SET)) != VSTRING_LEN(buf))
1970 	msg_panic("unexpected vstream_fseek return: %ld, expected: %ld",
1971 		  (long) offset, (long) VSTRING_LEN(buf));
1972     vstream_fclose(fp);
1973 
1974     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
1975 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
1976 		   vstring_str(buf));
1977     VSTREAM_PUTCHAR('\n');
1978     vstream_fflush(VSTREAM_OUT);
1979 
1980     /*
1981      * TODO: test that in write/append mode, seek past the end of data will
1982      * result in zero-filled space.
1983      */
1984 
1985     /*
1986      * Test: Open the VSTRING for reading. This time, vstream_memopen() will
1987      * set the VSTREAM buffer length to the content length of the VSTRING, so
1988      * that it won't attempt to read past the end of the content.
1989      */
1990     vstream_printf("memory stream test: open VSTRING for reading, then read\n");
1991     fp = vstream_memopen(buf, O_RDONLY);
1992     vstream_printf("initial memory VSTREAM read offset: %ld/%ld\n",
1993 		   (long) vstream_ftell(fp), (long) fp->buf.len);
1994     vstream_printf("reading memory VSTREAM: ");
1995     while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF)
1996 	VSTREAM_PUTCHAR(ch);
1997     VSTREAM_PUTCHAR('\n');
1998     vstream_printf("final memory VSTREAM read offset: %ld/%ld\n",
1999 		   (long) vstream_ftell(fp), (long) fp->buf.len);
2000     vstream_printf("seeking to offset %ld should work: ",
2001 		   (long) fp->buf.len + 1);
2002     vstream_fflush(VSTREAM_OUT);
2003     if ((offset = vstream_fseek(fp, fp->buf.len + 1, SEEK_SET)) != fp->buf.len + 1)
2004 	msg_panic("unexpected vstream_fseek return: %ld, expected: %ld",
2005 		  (long) offset, (long) fp->buf.len + 1);
2006     vstream_printf("PASS\n");
2007     vstream_fflush(VSTREAM_OUT);
2008     vstream_printf("VSTREAM_GETC should return VSTREAM_EOF\n");
2009     ch = VSTREAM_GETC(fp);
2010     if (ch != VSTREAM_EOF)
2011 	msg_panic("unexpected vstream_fseek VSTREAM_GETC return: %d, expected: %d",
2012 		  ch, VSTREAM_EOF);
2013     vstream_printf("PASS\n");
2014     vstream_printf("final memory VSTREAM read offset: %ld/%ld\n",
2015 		   (long) vstream_ftell(fp), (long) fp->buf.len);
2016     vstream_printf("VSTRING content length: %ld/%ld, content: %s\n",
2017 		   (long) VSTRING_LEN(buf), (long) buf->vbuf.len,
2018 		   vstring_str(buf));
2019     VSTREAM_PUTCHAR('\n');
2020     vstream_fflush(VSTREAM_OUT);
2021     vstream_fclose(fp);
2022     vstring_free(buf);
2023 }
2024 
2025  /*
2026   * Exercise some of the features.
2027   */
2028 
2029 #include <msg_vstream.h>
2030 
main(int argc,char ** argv)2031 int     main(int argc, char **argv)
2032 {
2033     msg_vstream_init(argv[0], VSTREAM_ERR);
2034 
2035     /*
2036      * Test buffer expansion and shrinking. Formatted print may silently
2037      * expand the write buffer and cause multiple bytes to be written.
2038      */
2039     copy_line(1);				/* one-byte read/write */
2040     copy_line(2);				/* two-byte read/write */
2041     copy_line(1);				/* two-byte read/write */
2042     printf_number();				/* multi-byte write */
2043     do_memory_stream();
2044 
2045     exit(0);
2046 }
2047 
2048 #endif
2049