/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" /* Highest signal number supported. POSIX standard signals are < 32 */ #define SIGNAL_MAX 32 static int update_sigmask(const struct filter *filt) { int rv; rv = signalfd(filt->kf_pfd, &filt->kf_sigmask, 0); dbg_printf("signalfd = %d", filt->kf_pfd); if (rv < 0 || rv != filt->kf_pfd) { dbg_printf("signalfd(2): %s", strerror(errno)); return (-1); } return (0); } int evfilt_signal_init(struct filter *filt) { sigemptyset(&filt->kf_sigmask); filt->kf_pfd = signalfd(-1, &filt->kf_sigmask, 0); dbg_printf("signalfd = %d", filt->kf_pfd); if (filt->kf_pfd < 0) return (-1); return (0); } void evfilt_signal_destroy(struct filter *filt) { close (filt->kf_pfd); } int evfilt_signal_copyout(struct filter *filt, struct kevent *dst, int nevents) { struct knote *kn; struct signalfd_siginfo sig[MAX_KEVENT]; int i; ssize_t n; n = read(filt->kf_pfd, &sig, nevents * sizeof(sig[0])); if (n < 0 || n < sizeof(sig[0])) { dbg_puts("invalid read from signalfd"); return (-1); } n /= sizeof(sig[0]); for (i = 0, nevents = 0; i < n; i++) { /* This is not an error because of this race condition: * 1. Signal arrives and is queued * 2. The kevent is deleted via kevent(..., EV_DELETE) * 3. The event is dequeued from the signalfd */ kn = knote_lookup(filt, sig[i].ssi_signo); if (kn == NULL) continue; dbg_printf("got signal %d", sig[i].ssi_signo); memcpy(dst, &kn->kev, sizeof(*dst)); /* TODO: dst->data should be the number of times the signal occurred */ dst->data = 1; if (kn->kev.flags & EV_DISPATCH || kn->kev.flags & EV_ONESHOT) { sigdelset(&filt->kf_sigmask, dst->ident); update_sigmask(filt); /* TODO: error checking */ } if (kn->kev.flags & EV_DISPATCH) KNOTE_DISABLE(kn); if (kn->kev.flags & EV_ONESHOT) knote_free(filt, kn); dst++; nevents++; } return (nevents); } int evfilt_signal_knote_create(struct filter *filt, struct knote *kn) { if (kn->kev.ident >= SIGNAL_MAX) { dbg_printf("bad signal number %u", (u_int) kn->kev.ident); return (-1); } kn->kev.flags |= EV_CLEAR; sigaddset(&filt->kf_sigmask, kn->kev.ident); return (update_sigmask(filt)); } int evfilt_signal_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { if (kev.ident >= SIGNAL_MAX) { dbg_printf("bad signal number %u", (u_int) kev.ident); return (-1); } /* Nothing to do since the sigmask does not change. */ return (0); } int evfilt_signal_knote_delete(struct filter *filt, struct knote *kn) { sigdelset(&filt->kf_sigmask, kn->kev.ident); return (update_sigmask(filt)); } int evfilt_signal_knote_enable(struct filter *filt, struct knote *kn) { sigaddset(&filt->kf_sigmask, kn->kev.ident); return (update_sigmask(filt)); } int evfilt_signal_knote_disable(struct filter *filt, struct knote *kn) { return (evfilt_signal_knote_delete(filt, kn)); } const struct filter evfilt_signal = { EVFILT_SIGNAL, evfilt_signal_init, evfilt_signal_destroy, evfilt_signal_copyout, evfilt_signal_knote_create, evfilt_signal_knote_modify, evfilt_signal_knote_delete, evfilt_signal_knote_enable, evfilt_signal_knote_disable, };