1 /* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art 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 #include <sys/queue.h> 40 #include <sys/event.h> 41 #include <signal.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <errno.h> 47 #ifdef HAVE_INTTYPES_H 48 #include <inttypes.h> 49 #endif 50 51 #if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) 52 #define INTPTR(x) (intptr_t)x 53 #else 54 #define INTPTR(x) x 55 #endif 56 57 #include "event.h" 58 #include "log.h" 59 60 #define EVLIST_X_KQINKERNEL 0x1000 61 62 #define NEVENT 64 63 64 struct kqop { 65 struct kevent *changes; 66 int nchanges; 67 struct kevent *events; 68 int nevents; 69 int kq; 70 }; 71 72 void *kq_init (void); 73 int kq_add (void *, struct event *); 74 int kq_del (void *, struct event *); 75 int kq_recalc (struct event_base *, void *, int); 76 int kq_dispatch (struct event_base *, void *, struct timeval *); 77 int kq_insert (struct kqop *, struct kevent *); 78 void kq_dealloc (void *); 79 80 const struct eventop kqops = { 81 "kqueue", 82 kq_init, 83 kq_add, 84 kq_del, 85 kq_recalc, 86 kq_dispatch, 87 kq_dealloc 88 }; 89 90 void * 91 kq_init(void) 92 { 93 int kq; 94 struct kqop *kqueueop; 95 96 /* Disable kqueue when this environment variable is set */ 97 if (getenv("EVENT_NOKQUEUE")) 98 return (NULL); 99 100 if (!(kqueueop = calloc(1, sizeof(struct kqop)))) 101 return (NULL); 102 103 /* Initalize the kernel queue */ 104 105 if ((kq = kqueue()) == -1) { 106 event_warn("kqueue"); 107 free (kqueueop); 108 return (NULL); 109 } 110 111 kqueueop->kq = kq; 112 113 /* Initalize fields */ 114 kqueueop->changes = malloc(NEVENT * sizeof(struct kevent)); 115 if (kqueueop->changes == NULL) { 116 free (kqueueop); 117 return (NULL); 118 } 119 kqueueop->events = malloc(NEVENT * sizeof(struct kevent)); 120 if (kqueueop->events == NULL) { 121 free (kqueueop->changes); 122 free (kqueueop); 123 return (NULL); 124 } 125 kqueueop->nevents = NEVENT; 126 127 /* Check for Mac OS X kqueue bug. */ 128 kqueueop->changes[0].ident = -1; 129 kqueueop->changes[0].filter = EVFILT_READ; 130 kqueueop->changes[0].flags = EV_ADD; 131 /* 132 * If kqueue works, then kevent will succeed, and it will 133 * stick an error in events[0]. If kqueue is broken, then 134 * kevent will fail. 135 */ 136 if (kevent(kq, 137 kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 || 138 kqueueop->events[0].ident != -1 || 139 kqueueop->events[0].flags != EV_ERROR) { 140 event_warn("%s: detected broken kqueue; not using.", __func__); 141 free(kqueueop->changes); 142 free(kqueueop->events); 143 free(kqueueop); 144 close(kq); 145 return (NULL); 146 } 147 148 return (kqueueop); 149 } 150 151 int 152 kq_recalc(struct event_base *base, void *arg, int max) 153 { 154 return (0); 155 } 156 157 int 158 kq_insert(struct kqop *kqop, struct kevent *kev) 159 { 160 int nevents = kqop->nevents; 161 162 if (kqop->nchanges == nevents) { 163 struct kevent *newchange; 164 struct kevent *newresult; 165 166 nevents *= 2; 167 168 newchange = realloc(kqop->changes, 169 nevents * sizeof(struct kevent)); 170 if (newchange == NULL) { 171 event_warn("%s: malloc", __func__); 172 return (-1); 173 } 174 kqop->changes = newchange; 175 176 newresult = realloc(kqop->events, 177 nevents * sizeof(struct kevent)); 178 179 /* 180 * If we fail, we don't have to worry about freeing, 181 * the next realloc will pick it up. 182 */ 183 if (newresult == NULL) { 184 event_warn("%s: malloc", __func__); 185 return (-1); 186 } 187 kqop->events = newresult; 188 189 kqop->nevents = nevents; 190 } 191 192 memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent)); 193 194 event_debug(("%s: fd %d %s%s", 195 __func__, kev->ident, 196 kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE", 197 kev->flags == EV_DELETE ? " (del)" : "")); 198 199 return (0); 200 } 201 202 static void 203 kq_sighandler(int sig) 204 { 205 /* Do nothing here */ 206 } 207 208 int 209 kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) 210 { 211 struct kqop *kqop = arg; 212 struct kevent *changes = kqop->changes; 213 struct kevent *events = kqop->events; 214 struct event *ev; 215 struct timespec ts; 216 int i, res; 217 218 TIMEVAL_TO_TIMESPEC(tv, &ts); 219 220 res = kevent(kqop->kq, changes, kqop->nchanges, 221 events, kqop->nevents, &ts); 222 kqop->nchanges = 0; 223 if (res == -1) { 224 if (errno != EINTR) { 225 event_warn("kevent"); 226 return (-1); 227 } 228 229 return (0); 230 } 231 232 event_debug(("%s: kevent reports %d", __func__, res)); 233 234 for (i = 0; i < res; i++) { 235 int which = 0; 236 237 if (events[i].flags & EV_ERROR) { 238 /* 239 * Error messages that can happen, when a delete fails. 240 * EBADF happens when the file discriptor has been 241 * closed, 242 * ENOENT when the file discriptor was closed and 243 * then reopened. 244 * EINVAL for some reasons not understood; EINVAL 245 * should not be returned ever; but FreeBSD does :-\ 246 * An error is also indicated when a callback deletes 247 * an event we are still processing. In that case 248 * the data field is set to ENOENT. 249 */ 250 if (events[i].data == EBADF || 251 events[i].data == EINVAL || 252 events[i].data == ENOENT) 253 continue; 254 errno = events[i].data; 255 return (-1); 256 } 257 258 ev = (struct event *)events[i].udata; 259 260 if (events[i].filter == EVFILT_READ) { 261 which |= EV_READ; 262 } else if (events[i].filter == EVFILT_WRITE) { 263 which |= EV_WRITE; 264 } else if (events[i].filter == EVFILT_SIGNAL) { 265 which |= EV_SIGNAL; 266 } 267 268 if (!which) 269 continue; 270 271 if (!(ev->ev_events & EV_PERSIST)) 272 event_del(ev); 273 274 event_active(ev, which, 275 ev->ev_events & EV_SIGNAL ? events[i].data : 1); 276 } 277 278 return (0); 279 } 280 281 282 int 283 kq_add(void *arg, struct event *ev) 284 { 285 struct kqop *kqop = arg; 286 struct kevent kev; 287 288 if (ev->ev_events & EV_SIGNAL) { 289 int nsignal = EVENT_SIGNAL(ev); 290 291 memset(&kev, 0, sizeof(kev)); 292 kev.ident = nsignal; 293 kev.filter = EVFILT_SIGNAL; 294 kev.flags = EV_ADD; 295 if (!(ev->ev_events & EV_PERSIST)) 296 kev.flags |= EV_ONESHOT; 297 kev.udata = INTPTR(ev); 298 299 if (kq_insert(kqop, &kev) == -1) 300 return (-1); 301 302 if (signal(nsignal, kq_sighandler) == SIG_ERR) 303 return (-1); 304 305 ev->ev_flags |= EVLIST_X_KQINKERNEL; 306 return (0); 307 } 308 309 if (ev->ev_events & EV_READ) { 310 memset(&kev, 0, sizeof(kev)); 311 kev.ident = ev->ev_fd; 312 kev.filter = EVFILT_READ; 313 #ifdef NOTE_EOF 314 /* Make it behave like select() and poll() */ 315 kev.fflags = NOTE_EOF; 316 #endif 317 kev.flags = EV_ADD; 318 if (!(ev->ev_events & EV_PERSIST)) 319 kev.flags |= EV_ONESHOT; 320 kev.udata = INTPTR(ev); 321 322 if (kq_insert(kqop, &kev) == -1) 323 return (-1); 324 325 ev->ev_flags |= EVLIST_X_KQINKERNEL; 326 } 327 328 if (ev->ev_events & EV_WRITE) { 329 memset(&kev, 0, sizeof(kev)); 330 kev.ident = ev->ev_fd; 331 kev.filter = EVFILT_WRITE; 332 kev.flags = EV_ADD; 333 if (!(ev->ev_events & EV_PERSIST)) 334 kev.flags |= EV_ONESHOT; 335 kev.udata = INTPTR(ev); 336 337 if (kq_insert(kqop, &kev) == -1) 338 return (-1); 339 340 ev->ev_flags |= EVLIST_X_KQINKERNEL; 341 } 342 343 return (0); 344 } 345 346 int 347 kq_del(void *arg, struct event *ev) 348 { 349 struct kqop *kqop = arg; 350 struct kevent kev; 351 352 if (!(ev->ev_flags & EVLIST_X_KQINKERNEL)) 353 return (0); 354 355 if (ev->ev_events & EV_SIGNAL) { 356 int nsignal = EVENT_SIGNAL(ev); 357 358 memset(&kev, 0, sizeof(kev)); 359 kev.ident = nsignal; 360 kev.filter = EVFILT_SIGNAL; 361 kev.flags = EV_DELETE; 362 363 if (kq_insert(kqop, &kev) == -1) 364 return (-1); 365 366 if (signal(nsignal, SIG_DFL) == SIG_ERR) 367 return (-1); 368 369 ev->ev_flags &= ~EVLIST_X_KQINKERNEL; 370 return (0); 371 } 372 373 if (ev->ev_events & EV_READ) { 374 memset(&kev, 0, sizeof(kev)); 375 kev.ident = ev->ev_fd; 376 kev.filter = EVFILT_READ; 377 kev.flags = EV_DELETE; 378 379 if (kq_insert(kqop, &kev) == -1) 380 return (-1); 381 382 ev->ev_flags &= ~EVLIST_X_KQINKERNEL; 383 } 384 385 if (ev->ev_events & EV_WRITE) { 386 memset(&kev, 0, sizeof(kev)); 387 kev.ident = ev->ev_fd; 388 kev.filter = EVFILT_WRITE; 389 kev.flags = EV_DELETE; 390 391 if (kq_insert(kqop, &kev) == -1) 392 return (-1); 393 394 ev->ev_flags &= ~EVLIST_X_KQINKERNEL; 395 } 396 397 return (0); 398 } 399 400 void 401 kq_dealloc(void *arg) 402 { 403 struct kqop *kqop = arg; 404 405 if (kqop->changes) 406 free(kqop->changes); 407 if (kqop->events) 408 free(kqop->events); 409 if (kqop->kq) 410 close(kqop->kq); 411 memset(kqop, 0, sizeof(struct kqop)); 412 free(kqop); 413 } 414