1 /*        $NetBSD: select.c,v 1.5 2021/04/10 19:18:45 rillig Exp $    */
2 /*        $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $   */
3 
4 /*
5  * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
6  * Copyright 2007-2012 Niels Provos and Nick Mathewson
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #include "event2/event-config.h"
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: select.c,v 1.5 2021/04/10 19:18:45 rillig Exp $");
33 #include "evconfig-private.h"
34 
35 #ifdef EVENT__HAVE_SELECT
36 
37 #ifdef __APPLE__
38 /* Apple wants us to define this if we might ever pass more than
39  * FD_SETSIZE bits to select(). */
40 #define _DARWIN_UNLIMITED_SELECT
41 #endif
42 
43 #include <sys/types.h>
44 #ifdef EVENT__HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47 #ifdef EVENT__HAVE_SYS_SELECT_H
48 #include <sys/select.h>
49 #endif
50 #include <sys/queue.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <errno.h>
57 
58 #include "event-internal.h"
59 #include "evsignal-internal.h"
60 #include "event2/thread.h"
61 #include "evthread-internal.h"
62 #include "log-internal.h"
63 #include "evmap-internal.h"
64 
65 #ifndef EVENT__HAVE_FD_MASK
66 /* This type is mandatory, but Android doesn't define it. */
67 typedef unsigned long fd_mask;
68 #endif
69 
70 #ifndef NFDBITS
71 #define NFDBITS (sizeof(fd_mask)*8)
72 #endif
73 
74 /* Divide positive x by y, rounding up. */
75 #define DIV_ROUNDUP(x, y)   (((x)+((y)-1))/(y))
76 
77 /* How many bytes to allocate for N fds? */
78 #define SELECT_ALLOC_SIZE(n) \
79           (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
80 
81 struct selectop {
82           int event_fds;                /* Highest fd in fd set */
83           int event_fdsz;
84           int resize_out_sets;
85           fd_set *event_readset_in;
86           fd_set *event_writeset_in;
87           fd_set *event_readset_out;
88           fd_set *event_writeset_out;
89 };
90 
91 static void *select_init(struct event_base *);
92 static int select_add(struct event_base *, int, short old, short events, void*);
93 static int select_del(struct event_base *, int, short old, short events, void*);
94 static int select_dispatch(struct event_base *, struct timeval *);
95 static void select_dealloc(struct event_base *);
96 
97 const struct eventop selectops = {
98           "select",
99           select_init,
100           select_add,
101           select_del,
102           select_dispatch,
103           select_dealloc,
104           1, /* need_reinit. */
105           EV_FEATURE_FDS,
106           0,
107 };
108 
109 static int select_resize(struct selectop *sop, int fdsz);
110 static void select_free_selectop(struct selectop *sop);
111 
112 static void *
select_init(struct event_base * base)113 select_init(struct event_base *base)
114 {
115           struct selectop *sop;
116 
117           if (!(sop = mm_calloc(1, sizeof(struct selectop))))
118                     return (NULL);
119 
120           if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
121                     select_free_selectop(sop);
122                     return (NULL);
123           }
124 
125           evsig_init_(base);
126 
127           evutil_weakrand_seed_(&base->weakrand_seed, 0);
128 
129           return (sop);
130 }
131 
132 #ifdef CHECK_INVARIANTS
133 static void
check_selectop(struct selectop * sop)134 check_selectop(struct selectop *sop)
135 {
136           /* nothing to be done here */
137 }
138 #else
139 #define check_selectop(sop) do { (void) sop; } while (0)
140 #endif
141 
142 static int
select_dispatch(struct event_base * base,struct timeval * tv)143 select_dispatch(struct event_base *base, struct timeval *tv)
144 {
145           int res=0, i, j, nfds;
146           struct selectop *sop = base->evbase;
147 
148           check_selectop(sop);
149           if (sop->resize_out_sets) {
150                     fd_set *readset_out=NULL, *writeset_out=NULL;
151                     size_t sz = sop->event_fdsz;
152                     if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
153                               return (-1);
154                     sop->event_readset_out = readset_out;
155                     if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
156                               /* We don't free readset_out here, since it was
157                                * already successfully reallocated. The next time
158                                * we call select_dispatch, the realloc will be a
159                                * no-op. */
160                               return (-1);
161                     }
162                     sop->event_writeset_out = writeset_out;
163                     sop->resize_out_sets = 0;
164           }
165 
166           memcpy(sop->event_readset_out, sop->event_readset_in,
167                  sop->event_fdsz);
168           memcpy(sop->event_writeset_out, sop->event_writeset_in,
169                  sop->event_fdsz);
170 
171           nfds = sop->event_fds+1;
172 
173           EVBASE_RELEASE_LOCK(base, th_base_lock);
174 
175           res = select(nfds, sop->event_readset_out,
176               sop->event_writeset_out, NULL, tv);
177 
178           EVBASE_ACQUIRE_LOCK(base, th_base_lock);
179 
180           check_selectop(sop);
181 
182           if (res == -1) {
183                     if (errno != EINTR) {
184                               event_warn("select");
185                               return (-1);
186                     }
187 
188                     return (0);
189           }
190 
191           event_debug(("%s: select reports %d", __func__, res));
192 
193           check_selectop(sop);
194           i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
195           for (j = 0; j < nfds; ++j) {
196                     if (++i >= nfds)
197                               i = 0;
198                     res = 0;
199                     if (FD_ISSET(i, sop->event_readset_out))
200                               res |= EV_READ;
201                     if (FD_ISSET(i, sop->event_writeset_out))
202                               res |= EV_WRITE;
203 
204                     if (res == 0)
205                               continue;
206 
207                     evmap_io_active_(base, i, res);
208           }
209           check_selectop(sop);
210 
211           return (0);
212 }
213 
214 static int
select_resize(struct selectop * sop,int fdsz)215 select_resize(struct selectop *sop, int fdsz)
216 {
217           fd_set *readset_in = NULL;
218           fd_set *writeset_in = NULL;
219 
220           if (sop->event_readset_in)
221                     check_selectop(sop);
222 
223           if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
224                     goto error;
225           sop->event_readset_in = readset_in;
226           if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
227                     /* Note that this will leave event_readset_in expanded.
228                      * That's okay; we wouldn't want to free it, since that would
229                      * change the semantics of select_resize from "expand the
230                      * readset_in and writeset_in, or return -1" to "expand the
231                      * *set_in members, or trash them and return -1."
232                      */
233                     goto error;
234           }
235           sop->event_writeset_in = writeset_in;
236           sop->resize_out_sets = 1;
237 
238           memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
239               fdsz - sop->event_fdsz);
240           memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
241               fdsz - sop->event_fdsz);
242 
243           sop->event_fdsz = fdsz;
244           check_selectop(sop);
245 
246           return (0);
247 
248  error:
249           event_warn("malloc");
250           return (-1);
251 }
252 
253 
254 static int
select_add(struct event_base * base,int fd,short old,short events,void * p)255 select_add(struct event_base *base, int fd, short old, short events, void *p)
256 {
257           struct selectop *sop = base->evbase;
258           (void) p;
259 
260           EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
261           check_selectop(sop);
262           /*
263            * Keep track of the highest fd, so that we can calculate the size
264            * of the fd_sets for select(2)
265            */
266           if (sop->event_fds < fd) {
267                     int fdsz = sop->event_fdsz;
268 
269                     if (fdsz < (int)sizeof(fd_mask))
270                               fdsz = (int)sizeof(fd_mask);
271 
272                     /* In theory we should worry about overflow here.  In
273                      * reality, though, the highest fd on a unixy system will
274                      * not overflow here. XXXX */
275                     while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
276                               fdsz *= 2;
277 
278                     if (fdsz != sop->event_fdsz) {
279                               if (select_resize(sop, fdsz)) {
280                                         check_selectop(sop);
281                                         return (-1);
282                               }
283                     }
284 
285                     sop->event_fds = fd;
286           }
287 
288           if (events & EV_READ)
289                     FD_SET(fd, sop->event_readset_in);
290           if (events & EV_WRITE)
291                     FD_SET(fd, sop->event_writeset_in);
292           check_selectop(sop);
293 
294           return (0);
295 }
296 
297 /*
298  * Nothing to be done here.
299  */
300 
301 static int
select_del(struct event_base * base,int fd,short old,short events,void * p)302 select_del(struct event_base *base, int fd, short old, short events, void *p)
303 {
304           struct selectop *sop = base->evbase;
305           (void)p;
306 
307           EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
308           check_selectop(sop);
309 
310           if (sop->event_fds < fd) {
311                     check_selectop(sop);
312                     return (0);
313           }
314 
315           if (events & EV_READ)
316                     FD_CLR(fd, sop->event_readset_in);
317 
318           if (events & EV_WRITE)
319                     FD_CLR(fd, sop->event_writeset_in);
320 
321           check_selectop(sop);
322           return (0);
323 }
324 
325 static void
select_free_selectop(struct selectop * sop)326 select_free_selectop(struct selectop *sop)
327 {
328           if (sop->event_readset_in)
329                     mm_free(sop->event_readset_in);
330           if (sop->event_writeset_in)
331                     mm_free(sop->event_writeset_in);
332           if (sop->event_readset_out)
333                     mm_free(sop->event_readset_out);
334           if (sop->event_writeset_out)
335                     mm_free(sop->event_writeset_out);
336 
337           memset(sop, 0, sizeof(struct selectop));
338           mm_free(sop);
339 }
340 
341 static void
select_dealloc(struct event_base * base)342 select_dealloc(struct event_base *base)
343 {
344           evsig_dealloc_(base);
345 
346           select_free_selectop(base->evbase);
347 }
348 
349 #endif /* EVENT__HAVE_SELECT */
350