summaryrefslogtreecommitdiffstats
path: root/src/util/poll_fd.c
blob: 80cd0f67daf70d63f46d4bf1766478d5c8c3547c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/*++
/* NAME
/*	poll_fd 3
/* SUMMARY
/*	wait until file descriptor becomes readable or writable
/* SYNOPSIS
/*	#include <iostuff.h>
/*
/*	int	readable(fd)
/*	int	fd;
/*
/*	int	writable(fd)
/*	int	fd;
/*
/*	int	read_wait(fd, time_limit)
/*	int	fd;
/*	int	time_limit;
/*
/*	int	write_wait(fd, time_limit)
/*	int	fd;
/*	int	time_limit;
/*
/*	int	poll_fd(fd, request, time_limit, true_res, false_res)
/*	int	fd;
/*	int	request;
/*	int	time_limit;
/*	int	true_res;
/*	int	false_res;
/* DESCRIPTION
/*	The read*() and write*() functions in this module are macros
/*	that provide a convenient interface to poll_fd().
/*
/*	readable() asks the kernel if the specified file descriptor
/*	is readable, i.e. a read operation would not block.
/*
/*	writable() asks the kernel if the specified file descriptor
/*	is writable, i.e. a write operation would not block.
/*
/*	read_wait() waits until the specified file descriptor becomes
/*	readable, or until the time limit is reached.
/*
/*	write_wait() waits until the specified file descriptor
/*	becomes writable, or until the time limit is reached.
/*
/*	poll_fd() waits until the specified file descriptor becomes
/*	readable or writable, or until the time limit is reached.
/*
/*	Arguments:
/* .IP fd
/*	File descriptor. With implementations based on select(), a
/*	best effort is made to handle descriptors >=FD_SETSIZE.
/* .IP request
/*	POLL_FD_READ (wait until readable) or POLL_FD_WRITE (wait
/*	until writable).
/* .IP time_limit
/*	A positive value specifies a time limit in seconds. A zero
/*	value effects a poll (return immediately).  A negative value
/*	means wait until the requested POLL_FD_READ or POLL_FD_WRITE
/*	condition becomes true.
/* .IP true_res
/*	Result value when the requested POLL_FD_READ or POLL_FD_WRITE
/*	condition is true.
/* .IP false_res
/*	Result value when the requested POLL_FD_READ or POLL_FD_WRITE
/*	condition is false.
/* DIAGNOSTICS
/*	Panic: interface violation. All system call errors are fatal
/*	unless specified otherwise.
/*
/*	readable() and writable() return 1 when the requested
/*	POLL_FD_READ or POLL_FD_WRITE condition is true, zero when
/*	it is false. They never return an error indication.
/*
/*	read_wait() and write_wait() return zero when the requested
/*	POLL_FD_READ or POLL_FD_WRITE condition is true, -1 (with
/*	errno set to ETIMEDOUT) when it is false.
/*
/*	poll_fd() returns true_res when the requested POLL_FD_READ
/*	or POLL_FD_WRITE condition is true, false_res when it is
/*	false.  When poll_fd() returns a false_res value < 0, it
/*	also sets errno to ETIMEDOUT.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

 /*
  * Use poll() with fall-back to select(). MacOSX needs this for devices.
  */
#if defined(USE_SYSV_POLL_THEN_SELECT)
#define poll_fd_sysv	poll_fd
#define USE_SYSV_POLL
#define USE_BSD_SELECT
int     poll_fd_bsd(int, int, int, int, int);

 /*
  * Use select() only.
  */
#elif defined(USE_BSD_SELECT)
#define poll_fd_bsd	poll_fd
#undef USE_SYSV_POLL

 /*
  * Use poll() only.
  */
#elif defined(USE_SYSV_POLL)
#define poll_fd_sysv	poll_fd

 /*
  * Sanity check.
  */
#else
#error "specify USE_SYSV_POLL, USE_BSD_SELECT or USE_SYSV_POLL_THEN_SELECT"
#endif

#ifdef USE_SYSV_POLL
#include <poll.h>
#endif

#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif

/* Utility library. */

#include <msg.h>
#include <iostuff.h>

#ifdef USE_BSD_SELECT

/* poll_fd_bsd - block with time_limit until file descriptor is ready */

int     poll_fd_bsd(int fd, int request, int time_limit,
		            int true_res, int false_res)
{
    fd_set  req_fds;
    fd_set *read_fds;
    fd_set *write_fds;
    fd_set  except_fds;
    struct timeval tv;
    struct timeval *tp;
    int     temp_fd = -1;

    /*
     * Sanity checks.
     */
    if (FD_SETSIZE <= fd) {
	if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
	    msg_fatal("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE);
	fd = temp_fd;
    }

    /*
     * Use select() so we do not depend on alarm() and on signal() handlers.
     * Restart select() when interrupted by some signal. Some select()
     * implementations reduce the time to wait when interrupted, which is
     * exactly what we want.
     */
    FD_ZERO(&req_fds);
    FD_SET(fd, &req_fds);
    except_fds = req_fds;
    if (request == POLL_FD_READ) {
	read_fds = &req_fds;
	write_fds = 0;
    } else if (request == POLL_FD_WRITE) {
	read_fds = 0;
	write_fds = &req_fds;
    } else {
	msg_panic("poll_fd: bad request %d", request);
    }

    if (time_limit >= 0) {
	tv.tv_usec = 0;
	tv.tv_sec = time_limit;
	tp = &tv;
    } else {
	tp = 0;
    }

    for (;;) {
	switch (select(fd + 1, read_fds, write_fds, &except_fds, tp)) {
	case -1:
	    if (errno != EINTR)
		msg_fatal("select: %m");
	    continue;
	case 0:
	    if (temp_fd != -1)
		(void) close(temp_fd);
	    if (false_res < 0)
		errno = ETIMEDOUT;
	    return (false_res);
	default:
	    if (temp_fd != -1)
		(void) close(temp_fd);
	    return (true_res);
	}
    }
}

#endif

#ifdef USE_SYSV_POLL

#ifdef USE_SYSV_POLL_THEN_SELECT
#define HANDLE_SYSV_POLL_ERROR(fd, req, time_limit, true_res, false_res) \
	return (poll_fd_bsd((fd), (req), (time_limit), (true_res), (false_res)))
#else
#define HANDLE_SYSV_POLL_ERROR(fd, req, time_limit, true_res, false_res) \
	msg_fatal("poll: %m")
#endif

/* poll_fd_sysv - block with time_limit until file descriptor is ready */

int     poll_fd_sysv(int fd, int request, int time_limit,
		             int true_res, int false_res)
{
    struct pollfd pollfd;

    /*
     * System-V poll() is optimal for polling a few descriptors.
     */
#define WAIT_FOR_EVENT	(-1)

    pollfd.fd = fd;
    if (request == POLL_FD_READ) {
	pollfd.events = POLLIN;
    } else if (request == POLL_FD_WRITE) {
	pollfd.events = POLLOUT;
    } else {
	msg_panic("poll_fd: bad request %d", request);
    }

    for (;;) {
	switch (poll(&pollfd, 1, time_limit < 0 ?
		     WAIT_FOR_EVENT : time_limit * 1000)) {
	case -1:
	    if (errno != EINTR)
		HANDLE_SYSV_POLL_ERROR(fd, request, time_limit,
				       true_res, false_res);
	    continue;
	case 0:
	    if (false_res < 0)
		errno = ETIMEDOUT;
	    return (false_res);
	default:
	    if (pollfd.revents & POLLNVAL)
		HANDLE_SYSV_POLL_ERROR(fd, request, time_limit,
				       true_res, false_res);
	    return (true_res);
	}
    }
}

#endif