Index: head/sys/compat/cloudabi64/cloudabi64_poll.c =================================================================== --- head/sys/compat/cloudabi64/cloudabi64_poll.c (revision 286653) +++ head/sys/compat/cloudabi64/cloudabi64_poll.c (revision 286654) @@ -1,47 +1,285 @@ /*- * Copyright (c) 2015 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); +#include +#include +#include + +#include + #include #include +/* Converts a FreeBSD signal number to a CloudABI signal number. */ +static cloudabi_signal_t +convert_signal(int sig) +{ + static const cloudabi_signal_t signals[] = { + [SIGABRT] = CLOUDABI_SIGABRT, + [SIGALRM] = CLOUDABI_SIGALRM, + [SIGBUS] = CLOUDABI_SIGBUS, + [SIGCHLD] = CLOUDABI_SIGCHLD, + [SIGCONT] = CLOUDABI_SIGCONT, + [SIGFPE] = CLOUDABI_SIGFPE, + [SIGHUP] = CLOUDABI_SIGHUP, + [SIGILL] = CLOUDABI_SIGILL, + [SIGINT] = CLOUDABI_SIGINT, + [SIGKILL] = CLOUDABI_SIGKILL, + [SIGPIPE] = CLOUDABI_SIGPIPE, + [SIGQUIT] = CLOUDABI_SIGQUIT, + [SIGSEGV] = CLOUDABI_SIGSEGV, + [SIGSTOP] = CLOUDABI_SIGSTOP, + [SIGSYS] = CLOUDABI_SIGSYS, + [SIGTERM] = CLOUDABI_SIGTERM, + [SIGTRAP] = CLOUDABI_SIGTRAP, + [SIGTSTP] = CLOUDABI_SIGTSTP, + [SIGTTIN] = CLOUDABI_SIGTTIN, + [SIGTTOU] = CLOUDABI_SIGTTOU, + [SIGURG] = CLOUDABI_SIGURG, + [SIGUSR1] = CLOUDABI_SIGUSR1, + [SIGUSR2] = CLOUDABI_SIGUSR2, + [SIGVTALRM] = CLOUDABI_SIGVTALRM, + [SIGXCPU] = CLOUDABI_SIGXCPU, + [SIGXFSZ] = CLOUDABI_SIGXFSZ, + }; + + /* Convert unknown signals to SIGABRT. */ + if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0) + return (SIGABRT); + return (signals[sig]); +} + +struct cloudabi64_kevent_args { + const cloudabi64_subscription_t *in; + cloudabi64_event_t *out; + bool once; +}; + +/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */ +static int +cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count) +{ + cloudabi64_subscription_t sub; + struct cloudabi64_kevent_args *args; + cloudabi_timestamp_t ts; + int error; + + args = arg; + while (count-- > 0) { + /* TODO(ed): Copy in multiple entries at once. */ + error = copyin(args->in++, &sub, sizeof(sub)); + if (error != 0) + return (error); + + memset(kevp, 0, sizeof(*kevp)); + kevp->udata = (void *)sub.userdata; + switch (sub.type) { + case CLOUDABI_EVENTTYPE_CLOCK: + kevp->filter = EVFILT_TIMER; + kevp->ident = sub.clock.identifier; + kevp->fflags = NOTE_NSECONDS; + if ((sub.clock.flags & + CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 && + sub.clock.timeout > 0) { + /* Convert absolute timestamp to a relative. */ + error = cloudabi_clock_time_get(curthread, + sub.clock.clock_id, &ts); + if (error != 0) + return (error); + ts = ts > sub.clock.timeout ? 0 : + sub.clock.timeout - ts; + } else { + /* Relative timestamp. */ + ts = sub.clock.timeout; + } + kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts; + break; + case CLOUDABI_EVENTTYPE_FD_READ: + kevp->filter = EVFILT_READ; + kevp->ident = sub.fd_readwrite.fd; + if ((sub.fd_readwrite.flags & + CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0) + kevp->fflags = NOTE_FILE_POLL; + break; + case CLOUDABI_EVENTTYPE_FD_WRITE: + kevp->filter = EVFILT_WRITE; + kevp->ident = sub.fd_readwrite.fd; + break; + case CLOUDABI_EVENTTYPE_PROC_TERMINATE: + kevp->filter = EVFILT_PROCDESC; + kevp->ident = sub.proc_terminate.fd; + kevp->fflags = NOTE_EXIT; + break; + } + if (args->once) { + /* Ignore flags. Simply use oneshot mode. */ + kevp->flags = EV_ADD | EV_ONESHOT; + } else { + /* Translate flags. */ + if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0) + kevp->flags |= EV_ADD; + if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0) + kevp->flags |= EV_CLEAR; + if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0) + kevp->flags |= EV_DELETE; + if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0) + kevp->flags |= EV_DISABLE; + if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0) + kevp->flags |= EV_ENABLE; + if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0) + kevp->flags |= EV_ONESHOT; + } + ++kevp; + } + return (0); +} + +/* Converts FreeBSD's struct kevent to CloudABI's event objects. */ +static int +cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count) +{ + cloudabi64_event_t ev; + struct cloudabi64_kevent_args *args; + int error; + + args = arg; + while (count-- > 0) { + /* Convert fields that should always be present. */ + memset(&ev, 0, sizeof(ev)); + ev.userdata = (uintptr_t)kevp->udata; + switch (kevp->filter) { + case EVFILT_TIMER: + ev.type = CLOUDABI_EVENTTYPE_CLOCK; + ev.clock.identifier = kevp->ident; + break; + case EVFILT_READ: + ev.type = CLOUDABI_EVENTTYPE_FD_READ; + ev.fd_readwrite.fd = kevp->ident; + break; + case EVFILT_WRITE: + ev.type = CLOUDABI_EVENTTYPE_FD_WRITE; + ev.fd_readwrite.fd = kevp->ident; + break; + case EVFILT_PROCDESC: + ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE; + ev.proc_terminate.fd = kevp->ident; + break; + } + + if ((kevp->flags & EV_ERROR) == 0) { + /* Success. */ + switch (kevp->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + ev.fd_readwrite.nbytes = kevp->data; + if ((kevp->flags & EV_EOF) != 0) { + ev.fd_readwrite.flags |= + CLOUDABI_EVENT_FD_READWRITE_HANGUP; + } + break; + case EVFILT_PROCDESC: + if (WIFSIGNALED(kevp->data)) { + /* Process got signalled. */ + ev.proc_terminate.signal = + convert_signal(WTERMSIG(kevp->data)); + ev.proc_terminate.exitcode = 0; + } else { + /* Process exited. */ + ev.proc_terminate.signal = 0; + ev.proc_terminate.exitcode = + WEXITSTATUS(kevp->data); + } + break; + } + } else { + /* Error. */ + ev.error = cloudabi_convert_errno(kevp->data); + } + ++kevp; + + /* TODO(ed): Copy out multiple entries at once. */ + error = copyout(&ev, args->out++, sizeof(ev)); + if (error != 0) + return (error); + } + return (0); +} + int cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) { + struct cloudabi64_kevent_args args = { + .in = uap->in, + .out = uap->out, + .once = true, + }; + struct kevent_copyops copyops = { + .k_copyin = cloudabi64_kevent_copyin, + .k_copyout = cloudabi64_kevent_copyout, + .arg = &args, + }; - /* Not implemented. */ - return (ENOSYS); + return (kern_kevent_anonymous(td, uap->nevents, ©ops)); } int cloudabi64_sys_poll_fd(struct thread *td, struct cloudabi64_sys_poll_fd_args *uap) { + struct cloudabi64_kevent_args args = { + .in = uap->in, + .out = uap->out, + .once = false, + }; + struct kevent_copyops copyops = { + .k_copyin = cloudabi64_kevent_copyin, + .k_copyout = cloudabi64_kevent_copyout, + .arg = &args, + }; + cloudabi64_subscription_t subtimo; + struct timespec timeout; + int error; - /* Not implemented. */ - return (ENOSYS); + if (uap->timeout != NULL) { + /* Poll with a timeout. */ + error = copyin(uap->timeout, &subtimo, sizeof(subtimo)); + if (error != 0) + return (error); + if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK || + subtimo.clock.flags != 0) + return (EINVAL); + timeout.tv_sec = subtimo.clock.timeout / 1000000000; + timeout.tv_nsec = subtimo.clock.timeout % 1000000000; + return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops, + &timeout)); + } else { + /* Poll without a timeout. */ + return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops, + NULL)); + } }