1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	write_wait 3
6/* SUMMARY
7/*	wait until descriptor becomes writable
8/* SYNOPSIS
9/*	#include <iostuff.h>
10/*
11/*	int	write_wait(fd, timeout)
12/*	int	fd;
13/*	int	timeout;
14/* DESCRIPTION
15/*	write_wait() blocks the current process until the specified file
16/*	descriptor becomes writable, or until the deadline is exceeded.
17/*
18/*	Arguments:
19/* .IP fd
20/*	File descriptor in the range 0..FD_SETSIZE.
21/* .IP timeout
22/*	If positive, deadline in seconds. A zero value effects a poll.
23/*	A negative value means wait until something happens.
24/* DIAGNOSTICS
25/*	Panic: interface violation. All system call errors are fatal.
26/*
27/*	A zero result means success.  When the specified deadline is
28/*	exceeded, write_wait() returns -1 and sets errno to ETIMEDOUT.
29/* LICENSE
30/* .ad
31/* .fi
32/*	The Secure Mailer license must be distributed with this software.
33/* AUTHOR(S)
34/*	Wietse Venema
35/*	IBM T.J. Watson Research
36/*	P.O. Box 704
37/*	Yorktown Heights, NY 10598, USA
38/*--*/
39
40/* System library. */
41
42#include <sys_defs.h>
43#include <sys/time.h>
44#include <signal.h>
45#include <errno.h>
46#include <unistd.h>
47#include <string.h>
48
49#ifdef USE_SYSV_POLL
50#include <poll.h>
51#endif
52
53#ifdef USE_SYS_SELECT_H
54#include <sys/select.h>
55#endif
56
57/* Utility library. */
58
59#include <msg.h>
60#include <iostuff.h>
61
62/* write_wait - block with timeout until file descriptor is writable */
63
64int     write_wait(int fd, int timeout)
65{
66#ifndef USE_SYSV_POLL
67    fd_set  write_fds;
68    fd_set  except_fds;
69    struct timeval tv;
70    struct timeval *tp;
71
72    /*
73     * Sanity checks.
74     */
75    if (FD_SETSIZE <= fd)
76	msg_panic("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE);
77
78    /*
79     * Guard the write() with select() so we do not depend on alarm() and on
80     * signal() handlers. Restart the select when interrupted by some signal.
81     * Some select() implementations may reduce the time to wait when
82     * interrupted, which is exactly what we want.
83     */
84    FD_ZERO(&write_fds);
85    FD_SET(fd, &write_fds);
86    FD_ZERO(&except_fds);
87    FD_SET(fd, &except_fds);
88    if (timeout >= 0) {
89	tv.tv_usec = 0;
90	tv.tv_sec = timeout;
91	tp = &tv;
92    } else {
93	tp = 0;
94    }
95
96    for (;;) {
97	switch (select(fd + 1, (fd_set *) 0, &write_fds, &except_fds, tp)) {
98	case -1:
99	    if (errno != EINTR)
100		msg_fatal("select: %m");
101	    continue;
102	case 0:
103	    errno = ETIMEDOUT;
104	    return (-1);
105	default:
106	    return (0);
107	}
108    }
109#else
110
111    /*
112     * System-V poll() is optimal for polling a few descriptors.
113     */
114    struct pollfd pollfd;
115
116#define WAIT_FOR_EVENT	(-1)
117
118    pollfd.fd = fd;
119    pollfd.events = POLLOUT;
120    for (;;) {
121	switch (poll(&pollfd, 1, timeout < 0 ?
122		     WAIT_FOR_EVENT : timeout * 1000)) {
123	case -1:
124	    if (errno != EINTR)
125		msg_fatal("poll: %m");
126	    continue;
127	case 0:
128	    errno = ETIMEDOUT;
129	    return (-1);
130	default:
131	    if (pollfd.revents & POLLNVAL)
132		msg_fatal("poll: %m");
133	    return (0);
134	}
135    }
136#endif
137}
138