1 /* $OpenBSD: select.c,v 1.14 2008/05/02 06:09:11 brad 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. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 #ifdef HAVE_CONFIG_H 30 #include "config.h" 31 #endif 32 33 #include <sys/types.h> 34 #ifdef HAVE_SYS_TIME_H 35 #include <sys/time.h> 36 #else 37 #include <sys/_time.h> 38 #endif 39 #ifdef HAVE_SYS_SELECT_H 40 #include <sys/select.h> 41 #endif 42 #include <sys/queue.h> 43 #include <sys/tree.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 #ifdef CHECK_INVARIANTS 51 #include <assert.h> 52 #endif 53 54 #include "event.h" 55 #include "event-internal.h" 56 #include "evsignal.h" 57 #include "log.h" 58 59 #ifndef howmany 60 #define howmany(x, y) (((x)+((y)-1))/(y)) 61 #endif 62 63 struct selectop { 64 int event_fds; /* Highest fd in fd set */ 65 int event_fdsz; 66 fd_set *event_readset_in; 67 fd_set *event_writeset_in; 68 fd_set *event_readset_out; 69 fd_set *event_writeset_out; 70 struct event **event_r_by_fd; 71 struct event **event_w_by_fd; 72 }; 73 74 void *select_init (struct event_base *); 75 int select_add (void *, struct event *); 76 int select_del (void *, struct event *); 77 int select_recalc (struct event_base *, void *, int); 78 int select_dispatch (struct event_base *, void *, struct timeval *); 79 void select_dealloc (struct event_base *, void *); 80 81 const struct eventop selectops = { 82 "select", 83 select_init, 84 select_add, 85 select_del, 86 select_recalc, 87 select_dispatch, 88 select_dealloc 89 }; 90 91 static int select_resize(struct selectop *sop, int fdsz); 92 93 void * 94 select_init(struct event_base *base) 95 { 96 struct selectop *sop; 97 98 /* Disable select when this environment variable is set */ 99 if (!issetugid() && getenv("EVENT_NOSELECT")) 100 return (NULL); 101 102 if (!(sop = calloc(1, sizeof(struct selectop)))) 103 return (NULL); 104 105 select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask)); 106 107 evsignal_init(base); 108 109 return (sop); 110 } 111 112 #ifdef CHECK_INVARIANTS 113 static void 114 check_selectop(struct selectop *sop) 115 { 116 int i; 117 for (i = 0; i <= sop->event_fds; ++i) { 118 if (FD_ISSET(i, sop->event_readset_in)) { 119 assert(sop->event_r_by_fd[i]); 120 assert(sop->event_r_by_fd[i]->ev_events & EV_READ); 121 assert(sop->event_r_by_fd[i]->ev_fd == i); 122 } else { 123 assert(! sop->event_r_by_fd[i]); 124 } 125 if (FD_ISSET(i, sop->event_writeset_in)) { 126 assert(sop->event_w_by_fd[i]); 127 assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE); 128 assert(sop->event_w_by_fd[i]->ev_fd == i); 129 } else { 130 assert(! sop->event_w_by_fd[i]); 131 } 132 } 133 134 } 135 #else 136 #define check_selectop(sop) do { (void) sop; } while (0) 137 #endif 138 139 /* 140 * Called with the highest fd that we know about. If it is 0, completely 141 * recalculate everything. 142 */ 143 144 int 145 select_recalc(struct event_base *base, void *arg, int max) 146 { 147 struct selectop *sop = arg; 148 149 check_selectop(sop); 150 151 return (0); 152 } 153 154 int 155 select_dispatch(struct event_base *base, void *arg, struct timeval *tv) 156 { 157 int res, i; 158 struct selectop *sop = arg; 159 160 check_selectop(sop); 161 162 memcpy(sop->event_readset_out, sop->event_readset_in, 163 sop->event_fdsz); 164 memcpy(sop->event_writeset_out, sop->event_writeset_in, 165 sop->event_fdsz); 166 167 res = select(sop->event_fds + 1, sop->event_readset_out, 168 sop->event_writeset_out, NULL, tv); 169 170 check_selectop(sop); 171 172 if (res == -1) { 173 if (errno != EINTR) { 174 event_warn("select"); 175 return (-1); 176 } 177 178 evsignal_process(base); 179 return (0); 180 } else if (base->sig.evsignal_caught) { 181 evsignal_process(base); 182 } 183 184 event_debug(("%s: select reports %d", __func__, res)); 185 186 check_selectop(sop); 187 for (i = 0; i <= sop->event_fds; ++i) { 188 struct event *r_ev = NULL, *w_ev = NULL; 189 res = 0; 190 if (FD_ISSET(i, sop->event_readset_out)) { 191 r_ev = sop->event_r_by_fd[i]; 192 res |= EV_READ; 193 } 194 if (FD_ISSET(i, sop->event_writeset_out)) { 195 w_ev = sop->event_w_by_fd[i]; 196 res |= EV_WRITE; 197 } 198 if (r_ev && (res & r_ev->ev_events)) { 199 if (!(r_ev->ev_events & EV_PERSIST)) 200 event_del(r_ev); 201 event_active(r_ev, res & r_ev->ev_events, 1); 202 } 203 if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { 204 if (!(w_ev->ev_events & EV_PERSIST)) 205 event_del(w_ev); 206 event_active(w_ev, res & w_ev->ev_events, 1); 207 } 208 } 209 check_selectop(sop); 210 211 return (0); 212 } 213 214 215 static int 216 select_resize(struct selectop *sop, int fdsz) 217 { 218 int n_events, n_events_old; 219 220 fd_set *readset_in = NULL; 221 fd_set *writeset_in = NULL; 222 fd_set *readset_out = NULL; 223 fd_set *writeset_out = NULL; 224 struct event **r_by_fd = NULL; 225 struct event **w_by_fd = NULL; 226 227 n_events = (fdsz/sizeof(fd_mask)) * NFDBITS; 228 n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS; 229 230 if (sop->event_readset_in) 231 check_selectop(sop); 232 233 if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL) 234 goto error; 235 sop->event_readset_in = readset_in; 236 if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL) 237 goto error; 238 sop->event_readset_out = readset_out; 239 if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL) 240 goto error; 241 sop->event_writeset_in = writeset_in; 242 if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL) 243 goto error; 244 sop->event_writeset_out = writeset_out; 245 if ((r_by_fd = realloc(sop->event_r_by_fd, 246 n_events*sizeof(struct event*))) == NULL) 247 goto error; 248 sop->event_r_by_fd = r_by_fd; 249 if ((w_by_fd = realloc(sop->event_w_by_fd, 250 n_events * sizeof(struct event*))) == NULL) 251 goto error; 252 sop->event_w_by_fd = w_by_fd; 253 254 memset((char *)sop->event_readset_in + sop->event_fdsz, 0, 255 fdsz - sop->event_fdsz); 256 memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, 257 fdsz - sop->event_fdsz); 258 memset(sop->event_r_by_fd + n_events_old, 0, 259 (n_events-n_events_old) * sizeof(struct event*)); 260 memset(sop->event_w_by_fd + n_events_old, 0, 261 (n_events-n_events_old) * sizeof(struct event*)); 262 263 sop->event_fdsz = fdsz; 264 check_selectop(sop); 265 266 return (0); 267 268 error: 269 event_warn("malloc"); 270 return (-1); 271 } 272 273 274 int 275 select_add(void *arg, struct event *ev) 276 { 277 struct selectop *sop = arg; 278 279 if (ev->ev_events & EV_SIGNAL) 280 return (evsignal_add(ev)); 281 282 check_selectop(sop); 283 /* 284 * Keep track of the highest fd, so that we can calculate the size 285 * of the fd_sets for select(2) 286 */ 287 if (sop->event_fds < ev->ev_fd) { 288 int fdsz = sop->event_fdsz; 289 290 if (fdsz < sizeof(fd_mask)) 291 fdsz = sizeof(fd_mask); 292 293 while (fdsz < 294 (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask))) 295 fdsz *= 2; 296 297 if (fdsz != sop->event_fdsz) { 298 if (select_resize(sop, fdsz)) { 299 check_selectop(sop); 300 return (-1); 301 } 302 } 303 304 sop->event_fds = ev->ev_fd; 305 } 306 307 if (ev->ev_events & EV_READ) { 308 FD_SET(ev->ev_fd, sop->event_readset_in); 309 sop->event_r_by_fd[ev->ev_fd] = ev; 310 } 311 if (ev->ev_events & EV_WRITE) { 312 FD_SET(ev->ev_fd, sop->event_writeset_in); 313 sop->event_w_by_fd[ev->ev_fd] = ev; 314 } 315 check_selectop(sop); 316 317 return (0); 318 } 319 320 /* 321 * Nothing to be done here. 322 */ 323 324 int 325 select_del(void *arg, struct event *ev) 326 { 327 struct selectop *sop = arg; 328 329 check_selectop(sop); 330 if (ev->ev_events & EV_SIGNAL) 331 return (evsignal_del(ev)); 332 333 if (sop->event_fds < ev->ev_fd) { 334 check_selectop(sop); 335 return (0); 336 } 337 338 if (ev->ev_events & EV_READ) { 339 FD_CLR(ev->ev_fd, sop->event_readset_in); 340 sop->event_r_by_fd[ev->ev_fd] = NULL; 341 } 342 343 if (ev->ev_events & EV_WRITE) { 344 FD_CLR(ev->ev_fd, sop->event_writeset_in); 345 sop->event_w_by_fd[ev->ev_fd] = NULL; 346 } 347 348 check_selectop(sop); 349 return (0); 350 } 351 352 void 353 select_dealloc(struct event_base *base, void *arg) 354 { 355 struct selectop *sop = arg; 356 357 evsignal_dealloc(base); 358 if (sop->event_readset_in) 359 free(sop->event_readset_in); 360 if (sop->event_writeset_in) 361 free(sop->event_writeset_in); 362 if (sop->event_readset_out) 363 free(sop->event_readset_out); 364 if (sop->event_writeset_out) 365 free(sop->event_writeset_out); 366 if (sop->event_r_by_fd) 367 free(sop->event_r_by_fd); 368 if (sop->event_w_by_fd) 369 free(sop->event_w_by_fd); 370 371 memset(sop, 0, sizeof(struct selectop)); 372 free(sop); 373 } 374