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