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