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