xref: /openbsd-src/lib/libevent/poll.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: poll.c,v 1.2 2003/10/01 09:10:30 markus Exp $	*/
2 
3 /*
4  * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Niels Provos.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_TIME_H
38 #include <sys/time.h>
39 #else
40 #include <sys/_time.h>
41 #endif
42 #include <sys/queue.h>
43 #include <poll.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <errno.h>
50 #include <err.h>
51 
52 #ifdef USE_LOG
53 #include "log.h"
54 #else
55 #define LOG_DBG(x)
56 #define log_error(x)	perror(x)
57 #endif
58 
59 #include "event.h"
60 #include "evsignal.h"
61 
62 extern struct event_list eventqueue;
63 
64 extern volatile sig_atomic_t evsignal_caught;
65 
66 struct pollop {
67 	int event_count;		/* Highest number alloc */
68 	struct pollfd *event_set;
69 	struct event **event_back;
70 	sigset_t evsigmask;
71 } pop;
72 
73 void *poll_init	(void);
74 int poll_add		(void *, struct event *);
75 int poll_del		(void *, struct event *);
76 int poll_recalc	(void *, int);
77 int poll_dispatch	(void *, struct timeval *);
78 
79 struct eventop pollops = {
80 	"poll",
81 	poll_init,
82 	poll_add,
83 	poll_del,
84 	poll_recalc,
85 	poll_dispatch
86 };
87 
88 void *
89 poll_init(void)
90 {
91 	/* Disable kqueue when this environment variable is set */
92 	if (!issetugid() && getenv("EVENT_NOPOLL"))
93 		return (NULL);
94 
95 	memset(&pop, 0, sizeof(pop));
96 
97 	evsignal_init(&pop.evsigmask);
98 
99 	return (&pop);
100 }
101 
102 /*
103  * Called with the highest fd that we know about.  If it is 0, completely
104  * recalculate everything.
105  */
106 
107 int
108 poll_recalc(void *arg, int max)
109 {
110 	struct pollop *pop = arg;
111 
112 	return (evsignal_recalc(&pop->evsigmask));
113 }
114 
115 int
116 poll_dispatch(void *arg, struct timeval *tv)
117 {
118 	int res, i, count, sec, nfds;
119 	struct event *ev;
120 	struct pollop *pop = arg;
121 
122 	count = pop->event_count;
123 	nfds = 0;
124 	TAILQ_FOREACH(ev, &eventqueue, ev_next) {
125 		if (nfds + 1 >= count) {
126 			if (count < 32)
127 				count = 32;
128 			else
129 				count *= 2;
130 
131 			/* We need more file descriptors */
132 			pop->event_set = realloc(pop->event_set,
133 			    count * sizeof(struct pollfd));
134 			if (pop->event_set == NULL) {
135 				log_error("realloc");
136 				return (-1);
137 			}
138 			pop->event_back = realloc(pop->event_back,
139 			    count * sizeof(struct event *));
140 			if (pop->event_back == NULL) {
141 				log_error("realloc");
142 				return (-1);
143 			}
144 			pop->event_count = count;
145 		}
146 		if (ev->ev_events & EV_WRITE) {
147 			struct pollfd *pfd = &pop->event_set[nfds];
148 			pfd->fd = ev->ev_fd;
149 			pfd->events = POLLOUT;
150 			pfd->revents = 0;
151 
152 			pop->event_back[nfds] = ev;
153 
154 			nfds++;
155 		}
156 		if (ev->ev_events & EV_READ) {
157 			struct pollfd *pfd = &pop->event_set[nfds];
158 
159 			pfd->fd = ev->ev_fd;
160 			pfd->events = POLLIN;
161 			pfd->revents = 0;
162 
163 			pop->event_back[nfds] = ev;
164 
165 			nfds++;
166 		}
167 	}
168 
169 	if (evsignal_deliver(&pop->evsigmask) == -1)
170 		return (-1);
171 
172 	sec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
173 	res = poll(pop->event_set, nfds, sec);
174 
175 	if (evsignal_recalc(&pop->evsigmask) == -1)
176 		return (-1);
177 
178 	if (res == -1) {
179 		if (errno != EINTR) {
180 			log_error("poll");
181 			return (-1);
182 		}
183 
184 		evsignal_process();
185 		return (0);
186 	} else if (evsignal_caught)
187 		evsignal_process();
188 
189 	LOG_DBG((LOG_MISC, 80, "%s: poll reports %d", __func__, res));
190 
191 	if (res == 0)
192 		return (0);
193 
194 	for (i = 0; i < nfds; i++) {
195 		res = 0;
196 		/* If the file gets closed notify */
197 		if (pop->event_set[i].revents & POLLHUP)
198 			pop->event_set[i].revents = POLLIN|POLLOUT;
199 		if (pop->event_set[i].revents & POLLIN)
200 			res |= EV_READ;
201 		if (pop->event_set[i].revents & POLLOUT)
202 			res |= EV_WRITE;
203 		if (res == 0)
204 			continue;
205 
206 		ev = pop->event_back[i];
207 		res &= ev->ev_events;
208 
209 		if (res) {
210 			if (!(ev->ev_events & EV_PERSIST))
211 				event_del(ev);
212 			event_active(ev, res, 1);
213 		}
214 	}
215 
216 	return (0);
217 }
218 
219 int
220 poll_add(void *arg, struct event *ev)
221 {
222 	struct pollop *pop = arg;
223 
224 	if (ev->ev_events & EV_SIGNAL)
225 		return (evsignal_add(&pop->evsigmask, ev));
226 
227 	return (0);
228 }
229 
230 /*
231  * Nothing to be done here.
232  */
233 
234 int
235 poll_del(void *arg, struct event *ev)
236 {
237 	struct pollop *pop = arg;
238 
239 	if (!(ev->ev_events & EV_SIGNAL))
240 		return (0);
241 
242 	return (evsignal_del(&pop->evsigmask, ev));
243 }
244