1/*++
2/* NAME
3/*	timed_write 3
4/* SUMMARY
5/*	write operation with pre-write timeout
6/* SYNOPSIS
7/*	#include <iostuff.h>
8/*
9/*	ssize_t	timed_write(fd, buf, len, timeout, context)
10/*	int	fd;
11/*	const void *buf;
12/*	size_t	len;
13/*	int	timeout;
14/*	void	*context;
15/* DESCRIPTION
16/*	timed_write() performs a write() operation when the specified
17/*	descriptor becomes writable within a user-specified deadline.
18/*
19/*	Arguments:
20/* .IP fd
21/*	File descriptor in the range 0..FD_SETSIZE.
22/* .IP buf
23/*	Write buffer pointer.
24/* .IP len
25/*	Write buffer size.
26/* .IP timeout
27/*	The deadline in seconds. If this is <= 0, the deadline feature
28/*	is disabled.
29/* .IP context
30/*	Application context. This parameter is unused. It exists only
31/*	for the sake of VSTREAM compatibility.
32/* DIAGNOSTICS
33/*	When the operation does not complete within the deadline, the
34/*	result value is -1, and errno is set to ETIMEDOUT.
35/*	All other returns are identical to those of a write(2) operation.
36/* LICENSE
37/* .ad
38/* .fi
39/*	The Secure Mailer license must be distributed with this software.
40/* AUTHOR(S)
41/*	Wietse Venema
42/*	IBM T.J. Watson Research
43/*	P.O. Box 704
44/*	Yorktown Heights, NY 10598, USA
45/*--*/
46
47/* System library. */
48
49#include <sys_defs.h>
50#include <unistd.h>
51#include <errno.h>
52
53/* Utility library. */
54
55#include <msg.h>
56#include <iostuff.h>
57
58/* timed_write - write with deadline */
59
60ssize_t timed_write(int fd, void *buf, size_t len,
61		            int timeout, void *unused_context)
62{
63    ssize_t ret;
64
65    /*
66     * Wait for a limited amount of time for something to happen. If nothing
67     * happens, report an ETIMEDOUT error.
68     *
69     * XXX Solaris 8 read() fails with EAGAIN after read-select() returns
70     * success. The code below exists just in case their write implementation
71     * is equally broken.
72     *
73     * This condition may also be found on systems where select() returns
74     * success on pipes with less than PIPE_BUF bytes of space, and with
75     * badly designed software where multiple writers are fighting for access
76     * to the same resource.
77     */
78    for (;;) {
79	if (timeout > 0 && write_wait(fd, timeout) < 0)
80	    return (-1);
81	if ((ret = write(fd, buf, len)) < 0 && timeout > 0 && errno == EAGAIN) {
82	    msg_warn("write() returns EAGAIN on a writable file descriptor!");
83	    msg_warn("pausing to avoid going into a tight select/write loop!");
84	    sleep(1);
85	    continue;
86	} else if (ret < 0 && errno == EINTR) {
87	    continue;
88	} else {
89	    return (ret);
90	}
91    }
92}
93