xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/poll_fd.c (revision 16d67a18c4cbb2d3cb426b01120f4938ca6dbbf9)
1 /*	$NetBSD: poll_fd.c,v 1.1.1.1 2014/07/06 19:27:58 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	poll_fd 3
6 /* SUMMARY
7 /*	wait until file descriptor becomes readable or writable
8 /* SYNOPSIS
9 /*	#include <iostuff.h>
10 /*
11 /*	int	readable(fd)
12 /*	int	fd;
13 /*
14 /*	int	writable(fd)
15 /*	int	fd;
16 /*
17 /*	int	read_wait(fd, time_limit)
18 /*	int	fd;
19 /*	int	time_limit;
20 /*
21 /*	int	write_wait(fd, time_limit)
22 /*	int	fd;
23 /*	int	time_limit;
24 /*
25 /*	int	poll_fd(fd, request, time_limit, true_res, false_res)
26 /*	int	fd;
27 /*	int	request;
28 /*	int	time_limit;
29 /*	int	true_res;
30 /*	int	false_res;
31 /* DESCRIPTION
32 /*	The read*() and write*() functions in this module are macros
33 /*	that provide a convenient interface to poll_fd().
34 /*
35 /*	readable() asks the kernel if the specified file descriptor
36 /*	is readable, i.e. a read operation would not block.
37 /*
38 /*	writable() asks the kernel if the specified file descriptor
39 /*	is writable, i.e. a write operation would not block.
40 /*
41 /*	read_wait() waits until the specified file descriptor becomes
42 /*	readable, or until the time limit is reached.
43 /*
44 /*	write_wait() waits until the specified file descriptor
45 /*	becomes writable, or until the time limit is reached.
46 /*
47 /*	poll_fd() waits until the specified file descriptor becomes
48 /*	readable or writable, or until the time limit is reached.
49 /*
50 /*	Arguments:
51 /* .IP fd
52 /*	File descriptor. With implementations based on select(), a
53 /*	best effort is made to handle descriptors >=FD_SETSIZE.
54 /* .IP request
55 /*	POLL_FD_READ (wait until readable) or POLL_FD_WRITE (wait
56 /*	until writable).
57 /* .IP time_limit
58 /*	A positive value specifies a time limit in seconds. A zero
59 /*	value effects a poll (return immediately).  A negative value
60 /*	means wait until the requested POLL_FD_READ or POLL_FD_WRITE
61 /*	condition becomes true.
62 /* .IP true_res
63 /*	Result value when the requested POLL_FD_READ or POLL_FD_WRITE
64 /*	condition is true.
65 /* .IP false_res
66 /*	Result value when the requested POLL_FD_READ or POLL_FD_WRITE
67 /*	condition is false.
68 /* DIAGNOSTICS
69 /*	Panic: interface violation. All system call errors are fatal
70 /*	unless specified otherwise.
71 /*
72 /*	readable() and writable() return 1 when the requested
73 /*	POLL_FD_READ or POLL_FD_WRITE condition is true, zero when
74 /*	it is false. They never return an error indication.
75 /*
76 /*	read_wait() and write_wait() return zero when the requested
77 /*	POLL_FD_READ or POLL_FD_WRITE condition is true, -1 (with
78 /*	errno set to ETIMEDOUT) when it is false.
79 /*
80 /*	poll_fd() returns true_res when the requested POLL_FD_READ
81 /*	or POLL_FD_WRITE condition is true, false_res when it is
82 /*	false.  When poll_fd() returns a false_res value < 0, it
83 /*	also sets errno to ETIMEDOUT.
84 /* LICENSE
85 /* .ad
86 /* .fi
87 /*	The Secure Mailer license must be distributed with this software.
88 /* AUTHOR(S)
89 /*	Wietse Venema
90 /*	IBM T.J. Watson Research
91 /*	P.O. Box 704
92 /*	Yorktown Heights, NY 10598, USA
93 /*--*/
94 
95 /* System library. */
96 
97 #include <sys_defs.h>
98 #include <sys/time.h>
99 #include <signal.h>
100 #include <errno.h>
101 #include <unistd.h>
102 #include <string.h>
103 
104  /*
105   * Use poll() with fall-back to select(). MacOSX needs this for devices.
106   */
107 #if defined(USE_SYSV_POLL_THEN_SELECT)
108 #define poll_fd_sysv	poll_fd
109 #define USE_SYSV_POLL
110 #define USE_BSD_SELECT
111 int     poll_fd_bsd(int, int, int, int, int);
112 
113  /*
114   * Use select() only.
115   */
116 #elif defined(USE_BSD_SELECT)
117 #define poll_fd_bsd	poll_fd
118 #undef USE_SYSV_POLL
119 
120  /*
121   * Use poll() only.
122   */
123 #elif defined(USE_SYSV_POLL)
124 #define poll_fd_sysv	poll_fd
125 
126  /*
127   * Sanity check.
128   */
129 #else
130 #error "specify USE_SYSV_POLL, USE_BSD_SELECT or USE_SYSV_POLL_THEN_SELECT"
131 #endif
132 
133 #ifdef USE_SYSV_POLL
134 #include <poll.h>
135 #endif
136 
137 #ifdef USE_SYS_SELECT_H
138 #include <sys/select.h>
139 #endif
140 
141 /* Utility library. */
142 
143 #include <msg.h>
144 #include <iostuff.h>
145 
146 #ifdef USE_BSD_SELECT
147 
148 /* poll_fd_bsd - block with time_limit until file descriptor is ready */
149 
poll_fd_bsd(int fd,int request,int time_limit,int true_res,int false_res)150 int     poll_fd_bsd(int fd, int request, int time_limit,
151 		            int true_res, int false_res)
152 {
153     fd_set  req_fds;
154     fd_set *read_fds;
155     fd_set *write_fds;
156     fd_set  except_fds;
157     struct timeval tv;
158     struct timeval *tp;
159     int     temp_fd = -1;
160 
161     /*
162      * Sanity checks.
163      */
164     if (FD_SETSIZE <= fd) {
165 	if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
166 	    msg_fatal("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE);
167 	fd = temp_fd;
168     }
169 
170     /*
171      * Use select() so we do not depend on alarm() and on signal() handlers.
172      * Restart select() when interrupted by some signal. Some select()
173      * implementations reduce the time to wait when interrupted, which is
174      * exactly what we want.
175      */
176     FD_ZERO(&req_fds);
177     FD_SET(fd, &req_fds);
178     except_fds = req_fds;
179     if (request == POLL_FD_READ) {
180 	read_fds = &req_fds;
181 	write_fds = 0;
182     } else if (request == POLL_FD_WRITE) {
183 	read_fds = 0;
184 	write_fds = &req_fds;
185     } else {
186 	msg_panic("poll_fd: bad request %d", request);
187     }
188 
189     if (time_limit >= 0) {
190 	tv.tv_usec = 0;
191 	tv.tv_sec = time_limit;
192 	tp = &tv;
193     } else {
194 	tp = 0;
195     }
196 
197     for (;;) {
198 	switch (select(fd + 1, read_fds, write_fds, &except_fds, tp)) {
199 	case -1:
200 	    if (errno != EINTR)
201 		msg_fatal("select: %m");
202 	    continue;
203 	case 0:
204 	    if (temp_fd != -1)
205 		(void) close(temp_fd);
206 	    if (false_res < 0)
207 		errno = ETIMEDOUT;
208 	    return (false_res);
209 	default:
210 	    if (temp_fd != -1)
211 		(void) close(temp_fd);
212 	    return (true_res);
213 	}
214     }
215 }
216 
217 #endif
218 
219 #ifdef USE_SYSV_POLL
220 
221 #ifdef USE_SYSV_POLL_THEN_SELECT
222 #define HANDLE_SYSV_POLL_ERROR(fd, req, time_limit, true_res, false_res) \
223 	return (poll_fd_bsd((fd), (req), (time_limit), (true_res), (false_res)))
224 #else
225 #define HANDLE_SYSV_POLL_ERROR(fd, req, time_limit, true_res, false_res) \
226 	msg_fatal("poll: %m")
227 #endif
228 
229 /* poll_fd_sysv - block with time_limit until file descriptor is ready */
230 
poll_fd_sysv(int fd,int request,int time_limit,int true_res,int false_res)231 int     poll_fd_sysv(int fd, int request, int time_limit,
232 		             int true_res, int false_res)
233 {
234     struct pollfd pollfd;
235 
236     /*
237      * System-V poll() is optimal for polling a few descriptors.
238      */
239 #define WAIT_FOR_EVENT	(-1)
240 
241     pollfd.fd = fd;
242     if (request == POLL_FD_READ) {
243 	pollfd.events = POLLIN;
244     } else if (request == POLL_FD_WRITE) {
245 	pollfd.events = POLLOUT;
246     } else {
247 	msg_panic("poll_fd: bad request %d", request);
248     }
249 
250     for (;;) {
251 	switch (poll(&pollfd, 1, time_limit < 0 ?
252 		     WAIT_FOR_EVENT : time_limit * 1000)) {
253 	case -1:
254 	    if (errno != EINTR)
255 		HANDLE_SYSV_POLL_ERROR(fd, request, time_limit,
256 				       true_res, false_res);
257 	    continue;
258 	case 0:
259 	    if (false_res < 0)
260 		errno = ETIMEDOUT;
261 	    return (false_res);
262 	default:
263 	    if (pollfd.revents & POLLNVAL)
264 		HANDLE_SYSV_POLL_ERROR(fd, request, time_limit,
265 				       true_res, false_res);
266 	    return (true_res);
267 	}
268     }
269 }
270 
271 #endif
272