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