xref: /openbsd-src/lib/libevent/select.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: select.c,v 1.5 2003/07/09 10:54:38 markus Exp $	*/
2 
3 /*
4  * Copyright 2000-2002 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 <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <err.h>
50 
51 #ifdef USE_LOG
52 #include "log.h"
53 #else
54 #define LOG_DBG(x)
55 #define log_error(x)	perror(x)
56 #endif
57 
58 #include "event.h"
59 #include "evsignal.h"
60 
61 extern struct event_list eventqueue;
62 
63 #ifndef howmany
64 #define        howmany(x, y)   (((x)+((y)-1))/(y))
65 #endif
66 
67 extern volatile sig_atomic_t evsignal_caught;
68 
69 struct selectop {
70 	int event_fds;		/* Highest fd in fd set */
71 	int event_fdsz;
72 	fd_set *event_readset;
73 	fd_set *event_writeset;
74 	sigset_t evsigmask;
75 } sop;
76 
77 void *select_init	(void);
78 int select_add		(void *, struct event *);
79 int select_del		(void *, struct event *);
80 int select_recalc	(void *, int);
81 int select_dispatch	(void *, struct timeval *);
82 
83 const struct eventop selectops = {
84 	"select",
85 	select_init,
86 	select_add,
87 	select_del,
88 	select_recalc,
89 	select_dispatch
90 };
91 
92 void *
93 select_init(void)
94 {
95 	/* Disable kqueue when this environment variable is set */
96 	if (!issetugid() && getenv("EVENT_NOSELECT"))
97 		return (NULL);
98 
99 	memset(&sop, 0, sizeof(sop));
100 
101 	evsignal_init(&sop.evsigmask);
102 
103 	return (&sop);
104 }
105 
106 /*
107  * Called with the highest fd that we know about.  If it is 0, completely
108  * recalculate everything.
109  */
110 
111 int
112 select_recalc(void *arg, int max)
113 {
114 	struct selectop *sop = arg;
115 	fd_set *readset, *writeset;
116 	struct event *ev;
117 	int fdsz;
118 
119 	if (sop->event_fds < max)
120 		sop->event_fds = max;
121 
122 	if (!sop->event_fds) {
123 		TAILQ_FOREACH(ev, &eventqueue, ev_next)
124 			if (ev->ev_fd > sop->event_fds)
125 				sop->event_fds = ev->ev_fd;
126 	}
127 
128 	fdsz = howmany(sop->event_fds + 1, NFDBITS) * sizeof(fd_mask);
129 	if (fdsz > sop->event_fdsz) {
130 		if ((readset = realloc(sop->event_readset, fdsz)) == NULL) {
131 			log_error("malloc");
132 			return (-1);
133 		}
134 
135 		if ((writeset = realloc(sop->event_writeset, fdsz)) == NULL) {
136 			log_error("malloc");
137 			free(readset);
138 			return (-1);
139 		}
140 
141 		memset((char *)readset + sop->event_fdsz, 0,
142 		    fdsz - sop->event_fdsz);
143 		memset((char *)writeset + sop->event_fdsz, 0,
144 		    fdsz - sop->event_fdsz);
145 
146 		sop->event_readset = readset;
147 		sop->event_writeset = writeset;
148 		sop->event_fdsz = fdsz;
149 	}
150 
151 	return (evsignal_recalc(&sop->evsigmask));
152 }
153 
154 int
155 select_dispatch(void *arg, struct timeval *tv)
156 {
157 	int maxfd, res;
158 	struct event *ev, *next;
159 	struct selectop *sop = arg;
160 
161 	memset(sop->event_readset, 0, sop->event_fdsz);
162 	memset(sop->event_writeset, 0, sop->event_fdsz);
163 
164 	TAILQ_FOREACH(ev, &eventqueue, ev_next) {
165 		if (ev->ev_events & EV_WRITE)
166 			FD_SET(ev->ev_fd, sop->event_writeset);
167 		if (ev->ev_events & EV_READ)
168 			FD_SET(ev->ev_fd, sop->event_readset);
169 	}
170 
171 	if (evsignal_deliver(&sop->evsigmask) == -1)
172 		return (-1);
173 
174 	res = select(sop->event_fds + 1, sop->event_readset,
175 	    sop->event_writeset, NULL, tv);
176 
177 	if (evsignal_recalc(&sop->evsigmask) == -1)
178 		return (-1);
179 
180 	if (res == -1) {
181 		if (errno != EINTR) {
182 			log_error("select");
183 			return (-1);
184 		}
185 
186 		evsignal_process();
187 		return (0);
188 	} else if (evsignal_caught)
189 		evsignal_process();
190 
191 	LOG_DBG((LOG_MISC, 80, "%s: select reports %d", __func__, res));
192 
193 	maxfd = 0;
194 	for (ev = TAILQ_FIRST(&eventqueue); ev != NULL; ev = next) {
195 		next = TAILQ_NEXT(ev, ev_next);
196 
197 		res = 0;
198 		if (FD_ISSET(ev->ev_fd, sop->event_readset))
199 			res |= EV_READ;
200 		if (FD_ISSET(ev->ev_fd, sop->event_writeset))
201 			res |= EV_WRITE;
202 		res &= ev->ev_events;
203 
204 		if (res) {
205 			if (!(ev->ev_events & EV_PERSIST))
206 				event_del(ev);
207 			event_active(ev, res, 1);
208 		} else if (ev->ev_fd > maxfd)
209 			maxfd = ev->ev_fd;
210 	}
211 
212 	sop->event_fds = maxfd;
213 
214 	return (0);
215 }
216 
217 int
218 select_add(void *arg, struct event *ev)
219 {
220 	struct selectop *sop = arg;
221 
222 	if (ev->ev_events & EV_SIGNAL)
223 		return (evsignal_add(&sop->evsigmask, ev));
224 
225 	/*
226 	 * Keep track of the highest fd, so that we can calculate the size
227 	 * of the fd_sets for select(2)
228 	 */
229 	if (sop->event_fds < ev->ev_fd)
230 		sop->event_fds = ev->ev_fd;
231 
232 	return (0);
233 }
234 
235 /*
236  * Nothing to be done here.
237  */
238 
239 int
240 select_del(void *arg, struct event *ev)
241 {
242 	struct selectop *sop = arg;
243 
244 	if (!(ev->ev_events & EV_SIGNAL))
245 		return (0);
246 
247 	return (evsignal_del(&sop->evsigmask, ev));
248 }
249