1/* 2 * Copyright (c) 2009 Mark Heily <mark@heily.com> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* To get asprintf(3) */ 18#define _GNU_SOURCE 19 20#include <assert.h> 21#include <errno.h> 22#include <fcntl.h> 23#include <poll.h> 24#include <signal.h> 25#include <stdlib.h> 26#include <stdio.h> 27#include <sys/queue.h> 28#include <sys/socket.h> 29#include <sys/types.h> 30#include <string.h> 31#include <unistd.h> 32 33#include "sys/event.h" 34#include "private.h" 35 36static char * 37kevent_filter_dump(const struct kevent *kev) 38{ 39 static char __thread buf[64]; 40 41 snprintf(&buf[0], sizeof(buf), "%d (%s)", 42 kev->filter, filter_name(kev->filter)); 43 return (&buf[0]); 44} 45 46static char * 47kevent_fflags_dump(const struct kevent *kev) 48{ 49 static char __thread buf[1024]; 50 51#define KEVFFL_DUMP(attrib) \ 52 if (kev->fflags & attrib) \ 53 strncat(buf, #attrib" ", 64); 54 55 snprintf(buf, sizeof(buf), "fflags=0x%04x (", kev->fflags); 56 if (kev->filter == EVFILT_VNODE) { 57 KEVFFL_DUMP(NOTE_DELETE); 58 KEVFFL_DUMP(NOTE_WRITE); 59 KEVFFL_DUMP(NOTE_EXTEND); 60 KEVFFL_DUMP(NOTE_ATTRIB); 61 KEVFFL_DUMP(NOTE_LINK); 62 KEVFFL_DUMP(NOTE_RENAME); 63 } else if (kev->filter == EVFILT_USER) { 64 KEVFFL_DUMP(NOTE_FFNOP); 65 KEVFFL_DUMP(NOTE_FFAND); 66 KEVFFL_DUMP(NOTE_FFOR); 67 KEVFFL_DUMP(NOTE_FFCOPY); 68 KEVFFL_DUMP(NOTE_TRIGGER); 69 } else { 70 strncat(buf, " ", 1); 71 } 72 buf[strlen(buf) - 1] = ')'; 73 74#undef KEVFFL_DUMP 75 76 return (buf); 77} 78 79static char * 80kevent_flags_dump(const struct kevent *kev) 81{ 82 static char __thread buf[1024]; 83 84#define KEVFL_DUMP(attrib) \ 85 if (kev->flags & attrib) \ 86 strncat(buf, #attrib" ", 64); 87 88 snprintf(buf, sizeof(buf), "flags=0x%04x (", kev->flags); 89 KEVFL_DUMP(EV_ADD); 90 KEVFL_DUMP(EV_ENABLE); 91 KEVFL_DUMP(EV_DISABLE); 92 KEVFL_DUMP(EV_DELETE); 93 KEVFL_DUMP(EV_ONESHOT); 94 KEVFL_DUMP(EV_CLEAR); 95 KEVFL_DUMP(EV_EOF); 96 KEVFL_DUMP(EV_ERROR); 97 KEVFL_DUMP(EV_DISPATCH); 98 KEVFL_DUMP(EV_RECEIPT); 99 buf[strlen(buf) - 1] = ')'; 100 101#undef KEVFL_DUMP 102 103 return (buf); 104} 105 106const char * 107kevent_dump(const struct kevent *kev) 108{ 109 static char __thread buf[1024]; 110 111 snprintf(buf, sizeof(buf), 112 "{ ident=%d, filter=%s, %s, %s, data=%d, udata=%p }", 113 (u_int) kev->ident, 114 kevent_filter_dump(kev), 115 kevent_flags_dump(kev), 116 kevent_fflags_dump(kev), 117 (int) kev->data, 118 kev->udata); 119 120 return (buf); 121} 122 123static int 124kevent_copyin_one(struct kqueue *kq, const struct kevent *src) 125{ 126 struct knote *kn = NULL; 127 struct filter *filt; 128 int rv; 129 130 if (src->flags & EV_DISPATCH && src->flags & EV_ONESHOT) { 131 errno = EINVAL; 132 return (-1); 133 } 134 135 if (filter_lookup(&filt, kq, src->filter) < 0) 136 return (-1); 137 138 //dbg_printf("src=%s\n", kevent_dump(src)); 139 140 kn = knote_lookup(filt, src->ident); 141 if (kn == NULL) { 142 if (src->flags & EV_ADD) { 143 if ((kn = knote_new()) == NULL) { 144 errno = ENOENT; 145 return (-1); 146 } 147 memcpy(&kn->kev, src, sizeof(kn->kev)); 148 kn->kev.flags &= ~EV_ENABLE; 149 kn->kev.flags |= EV_ADD;//FIXME why? 150 assert(filt->kn_create); 151 if (filt->kn_create(filt, kn) < 0) { 152 knote_free(filt, kn); 153 errno = EFAULT; 154 return (-1); 155 } 156 knote_insert(filt, kn); 157 dbg_printf("created kevent %s\n", kevent_dump(src)); 158 if (src->flags & EV_DISABLE) { 159 kn->kev.flags |= EV_DISABLE; 160 return (filt->kn_disable(filt, kn)); 161 } 162 return (0); 163 } else { 164 errno = ENOENT; 165 return (-1); 166 } 167 } 168 169 if (src->flags & EV_DELETE) { 170 rv = filt->kn_delete(filt, kn); 171 knote_free(filt, kn); 172 return (rv); 173 } else if (src->flags & EV_DISABLE) { 174 kn->kev.flags |= EV_DISABLE; 175 return (filt->kn_disable(filt, kn)); 176 } else if (src->flags & EV_ENABLE) { 177 kn->kev.flags &= ~EV_DISABLE; 178 return (filt->kn_enable(filt, kn)); 179 } 180 181 /* Implicit EV_ADD */ 182 kn->kev.udata = src->udata; 183 return (filt->kn_modify(filt, kn, src)); 184 185#if DEADWOOD 186 /* Special case for EVFILT_USER: 187 Ignore user-generated events that are not of interest */ 188 if (src->fflags & NOTE_TRIGGER) { 189 filter_unlock(filt); 190 continue; 191 } 192#endif 193} 194 195/** @return number of events added to the eventlist */ 196static int 197kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges, 198 struct kevent *eventlist, int nevents) 199{ 200 int status, nret; 201 202 dbg_printf("nchanges=%d nevents=%d", nchanges, nevents); 203 204 /* TODO: refactor, this has become convoluted to support EV_RECEIPT */ 205 for (nret = 0; nchanges > 0; src++, nchanges--) { 206 207 if (kevent_copyin_one(kq, src) < 0) { 208 dbg_printf("errno=%s",strerror(errno)); 209 status = errno; 210 goto err_path; 211 } else { 212 if (src->flags & EV_RECEIPT) { 213 status = 0; 214 goto err_path; 215 } 216 } 217 218 continue; 219 220err_path: 221 if (nevents > 0) { 222 memcpy(eventlist, src, sizeof(*src)); 223 eventlist->data = status; 224 nevents--; 225 eventlist++; 226 nret++; 227 } else { 228 return (-1); 229 } 230 } 231 232 return (nret); 233} 234 235int __attribute__((visibility("default"))) 236kevent(int kqfd, const struct kevent *changelist, int nchanges, 237 struct kevent *eventlist, int nevents, 238 const struct timespec *timeout) 239{ 240 struct kqueue *kq; 241 int rv, n, nret; 242 243 nret = 0; 244 245 kq = kqueue_get(kqfd); 246 if (kq == NULL) { 247 errno = ENOENT; 248 return (-1); 249 } 250 251 rv = kqueue_validate(kq); 252 if (rv < 0) { 253 return (-1); 254 } else if (rv == 0) { 255 errno = EBADF; 256 return (-1); 257 } 258 259 /* 260 * Process each kevent on the changelist. 261 */ 262 if (nchanges) { 263 kqueue_lock(kq); 264 rv = kevent_copyin(kq, changelist, nchanges, eventlist, nevents); 265 kqueue_unlock(kq); 266 dbg_printf("changelist: rv=%d", rv); 267 if (rv < 0) 268 goto errout; 269 if (rv > 0) { 270 eventlist += rv; 271 nevents -= rv; 272 } 273 } 274 275 /* Determine if we need to wait for events. */ 276 if (nevents > MAX_KEVENT) 277 nevents = MAX_KEVENT; 278 if (nevents == 0) 279 goto out; 280 281 /* Handle spurious wakeups where no events are generated. */ 282 for (nret = 0; nret == 0;) 283 { 284 /* Wait for one or more events. */ 285 n = kevent_wait(kq, timeout); 286 if (n < 0) { 287 dbg_puts("kevent_wait failed"); 288 goto errout; 289 } 290 if (n == 0) 291 goto out; /* Timeout */ 292 293 /* Copy the events to the caller */ 294 kqueue_lock(kq); 295 nret = kevent_copyout(kq, n, eventlist, nevents); 296 kqueue_unlock(kq); 297 } 298 299 if (KQUEUE_DEBUG) { 300 dbg_printf("returning %d events", nret); 301 for (n = 0; n < nret; n++) { 302 dbg_printf("eventlist[%d] = %s", n, kevent_dump(&eventlist[n])); 303 } 304 } 305 306 goto out; 307 308errout: 309 nret = -1; 310 311out: 312 kqueue_put(kq); 313 return (nret); 314} 315