1285307Sed/*- 2285307Sed * Copyright (c) 2015 Nuxi, https://nuxi.nl/ 3285307Sed * 4285307Sed * Redistribution and use in source and binary forms, with or without 5285307Sed * modification, are permitted provided that the following conditions 6285307Sed * are met: 7285307Sed * 1. Redistributions of source code must retain the above copyright 8285307Sed * notice, this list of conditions and the following disclaimer. 9285307Sed * 2. Redistributions in binary form must reproduce the above copyright 10285307Sed * notice, this list of conditions and the following disclaimer in the 11285307Sed * documentation and/or other materials provided with the distribution. 12285307Sed * 13285307Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14285307Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15285307Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16285307Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17285307Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18285307Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19285307Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20285307Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21285307Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22285307Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23285307Sed * SUCH DAMAGE. 24285307Sed */ 25285307Sed 26285307Sed#include <sys/cdefs.h> 27285307Sed__FBSDID("$FreeBSD: releng/11.0/sys/compat/cloudabi64/cloudabi64_poll.c 297247 2016-03-24 21:47:15Z ed $"); 28285307Sed 29286654Sed#include <sys/param.h> 30286654Sed#include <sys/proc.h> 31286654Sed#include <sys/syscallsubr.h> 32286654Sed 33297247Sed#include <contrib/cloudabi/cloudabi64_types.h> 34297247Sed 35286654Sed#include <compat/cloudabi/cloudabi_util.h> 36286654Sed 37285307Sed#include <compat/cloudabi64/cloudabi64_proto.h> 38285307Sed 39286654Sed/* Converts a FreeBSD signal number to a CloudABI signal number. */ 40286654Sedstatic cloudabi_signal_t 41286654Sedconvert_signal(int sig) 42286654Sed{ 43286654Sed static const cloudabi_signal_t signals[] = { 44286654Sed [SIGABRT] = CLOUDABI_SIGABRT, 45286654Sed [SIGALRM] = CLOUDABI_SIGALRM, 46286654Sed [SIGBUS] = CLOUDABI_SIGBUS, 47286654Sed [SIGCHLD] = CLOUDABI_SIGCHLD, 48286654Sed [SIGCONT] = CLOUDABI_SIGCONT, 49286654Sed [SIGFPE] = CLOUDABI_SIGFPE, 50286654Sed [SIGHUP] = CLOUDABI_SIGHUP, 51286654Sed [SIGILL] = CLOUDABI_SIGILL, 52286654Sed [SIGINT] = CLOUDABI_SIGINT, 53286654Sed [SIGKILL] = CLOUDABI_SIGKILL, 54286654Sed [SIGPIPE] = CLOUDABI_SIGPIPE, 55286654Sed [SIGQUIT] = CLOUDABI_SIGQUIT, 56286654Sed [SIGSEGV] = CLOUDABI_SIGSEGV, 57286654Sed [SIGSTOP] = CLOUDABI_SIGSTOP, 58286654Sed [SIGSYS] = CLOUDABI_SIGSYS, 59286654Sed [SIGTERM] = CLOUDABI_SIGTERM, 60286654Sed [SIGTRAP] = CLOUDABI_SIGTRAP, 61286654Sed [SIGTSTP] = CLOUDABI_SIGTSTP, 62286654Sed [SIGTTIN] = CLOUDABI_SIGTTIN, 63286654Sed [SIGTTOU] = CLOUDABI_SIGTTOU, 64286654Sed [SIGURG] = CLOUDABI_SIGURG, 65286654Sed [SIGUSR1] = CLOUDABI_SIGUSR1, 66286654Sed [SIGUSR2] = CLOUDABI_SIGUSR2, 67286654Sed [SIGVTALRM] = CLOUDABI_SIGVTALRM, 68286654Sed [SIGXCPU] = CLOUDABI_SIGXCPU, 69286654Sed [SIGXFSZ] = CLOUDABI_SIGXFSZ, 70286654Sed }; 71286654Sed 72286654Sed /* Convert unknown signals to SIGABRT. */ 73286654Sed if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0) 74286654Sed return (SIGABRT); 75286654Sed return (signals[sig]); 76286654Sed} 77286654Sed 78286654Sedstruct cloudabi64_kevent_args { 79286654Sed const cloudabi64_subscription_t *in; 80286654Sed cloudabi64_event_t *out; 81286654Sed bool once; 82286654Sed}; 83286654Sed 84286654Sed/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */ 85286654Sedstatic int 86286654Sedcloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count) 87286654Sed{ 88286654Sed cloudabi64_subscription_t sub; 89286654Sed struct cloudabi64_kevent_args *args; 90286654Sed cloudabi_timestamp_t ts; 91286654Sed int error; 92286654Sed 93286654Sed args = arg; 94286654Sed while (count-- > 0) { 95286654Sed /* TODO(ed): Copy in multiple entries at once. */ 96286654Sed error = copyin(args->in++, &sub, sizeof(sub)); 97286654Sed if (error != 0) 98286654Sed return (error); 99286654Sed 100286654Sed memset(kevp, 0, sizeof(*kevp)); 101286654Sed kevp->udata = (void *)sub.userdata; 102286654Sed switch (sub.type) { 103286654Sed case CLOUDABI_EVENTTYPE_CLOCK: 104286654Sed kevp->filter = EVFILT_TIMER; 105286654Sed kevp->ident = sub.clock.identifier; 106286654Sed kevp->fflags = NOTE_NSECONDS; 107286654Sed if ((sub.clock.flags & 108286654Sed CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 && 109286654Sed sub.clock.timeout > 0) { 110286654Sed /* Convert absolute timestamp to a relative. */ 111286654Sed error = cloudabi_clock_time_get(curthread, 112286654Sed sub.clock.clock_id, &ts); 113286654Sed if (error != 0) 114286654Sed return (error); 115286654Sed ts = ts > sub.clock.timeout ? 0 : 116286654Sed sub.clock.timeout - ts; 117286654Sed } else { 118286654Sed /* Relative timestamp. */ 119286654Sed ts = sub.clock.timeout; 120286654Sed } 121286654Sed kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts; 122286654Sed break; 123286654Sed case CLOUDABI_EVENTTYPE_FD_READ: 124286654Sed kevp->filter = EVFILT_READ; 125286654Sed kevp->ident = sub.fd_readwrite.fd; 126286654Sed if ((sub.fd_readwrite.flags & 127286654Sed CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0) 128286654Sed kevp->fflags = NOTE_FILE_POLL; 129286654Sed break; 130286654Sed case CLOUDABI_EVENTTYPE_FD_WRITE: 131286654Sed kevp->filter = EVFILT_WRITE; 132286654Sed kevp->ident = sub.fd_readwrite.fd; 133286654Sed break; 134286654Sed case CLOUDABI_EVENTTYPE_PROC_TERMINATE: 135286654Sed kevp->filter = EVFILT_PROCDESC; 136286654Sed kevp->ident = sub.proc_terminate.fd; 137286654Sed kevp->fflags = NOTE_EXIT; 138286654Sed break; 139286654Sed } 140286654Sed if (args->once) { 141286654Sed /* Ignore flags. Simply use oneshot mode. */ 142286654Sed kevp->flags = EV_ADD | EV_ONESHOT; 143286654Sed } else { 144286654Sed /* Translate flags. */ 145286654Sed if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0) 146286654Sed kevp->flags |= EV_ADD; 147286654Sed if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0) 148286654Sed kevp->flags |= EV_CLEAR; 149286654Sed if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0) 150286654Sed kevp->flags |= EV_DELETE; 151286654Sed if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0) 152286654Sed kevp->flags |= EV_DISABLE; 153286654Sed if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0) 154286654Sed kevp->flags |= EV_ENABLE; 155286654Sed if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0) 156286654Sed kevp->flags |= EV_ONESHOT; 157286654Sed } 158286654Sed ++kevp; 159286654Sed } 160286654Sed return (0); 161286654Sed} 162286654Sed 163286654Sed/* Converts FreeBSD's struct kevent to CloudABI's event objects. */ 164286654Sedstatic int 165286654Sedcloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count) 166286654Sed{ 167286654Sed cloudabi64_event_t ev; 168286654Sed struct cloudabi64_kevent_args *args; 169286654Sed int error; 170286654Sed 171286654Sed args = arg; 172286654Sed while (count-- > 0) { 173286654Sed /* Convert fields that should always be present. */ 174286654Sed memset(&ev, 0, sizeof(ev)); 175286654Sed ev.userdata = (uintptr_t)kevp->udata; 176286654Sed switch (kevp->filter) { 177286654Sed case EVFILT_TIMER: 178286654Sed ev.type = CLOUDABI_EVENTTYPE_CLOCK; 179286654Sed ev.clock.identifier = kevp->ident; 180286654Sed break; 181286654Sed case EVFILT_READ: 182286654Sed ev.type = CLOUDABI_EVENTTYPE_FD_READ; 183286654Sed ev.fd_readwrite.fd = kevp->ident; 184286654Sed break; 185286654Sed case EVFILT_WRITE: 186286654Sed ev.type = CLOUDABI_EVENTTYPE_FD_WRITE; 187286654Sed ev.fd_readwrite.fd = kevp->ident; 188286654Sed break; 189286654Sed case EVFILT_PROCDESC: 190286654Sed ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE; 191286654Sed ev.proc_terminate.fd = kevp->ident; 192286654Sed break; 193286654Sed } 194286654Sed 195286654Sed if ((kevp->flags & EV_ERROR) == 0) { 196286654Sed /* Success. */ 197286654Sed switch (kevp->filter) { 198286654Sed case EVFILT_READ: 199286654Sed case EVFILT_WRITE: 200286654Sed ev.fd_readwrite.nbytes = kevp->data; 201286654Sed if ((kevp->flags & EV_EOF) != 0) { 202286654Sed ev.fd_readwrite.flags |= 203286654Sed CLOUDABI_EVENT_FD_READWRITE_HANGUP; 204286654Sed } 205286654Sed break; 206286654Sed case EVFILT_PROCDESC: 207286654Sed if (WIFSIGNALED(kevp->data)) { 208286654Sed /* Process got signalled. */ 209286654Sed ev.proc_terminate.signal = 210286654Sed convert_signal(WTERMSIG(kevp->data)); 211286654Sed ev.proc_terminate.exitcode = 0; 212286654Sed } else { 213286654Sed /* Process exited. */ 214286654Sed ev.proc_terminate.signal = 0; 215286654Sed ev.proc_terminate.exitcode = 216286654Sed WEXITSTATUS(kevp->data); 217286654Sed } 218286654Sed break; 219286654Sed } 220286654Sed } else { 221286654Sed /* Error. */ 222286654Sed ev.error = cloudabi_convert_errno(kevp->data); 223286654Sed } 224286654Sed ++kevp; 225286654Sed 226286654Sed /* TODO(ed): Copy out multiple entries at once. */ 227286654Sed error = copyout(&ev, args->out++, sizeof(ev)); 228286654Sed if (error != 0) 229286654Sed return (error); 230286654Sed } 231286654Sed return (0); 232286654Sed} 233286654Sed 234285307Sedint 235285307Sedcloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) 236285307Sed{ 237286654Sed struct cloudabi64_kevent_args args = { 238286654Sed .in = uap->in, 239286654Sed .out = uap->out, 240286654Sed .once = true, 241286654Sed }; 242286654Sed struct kevent_copyops copyops = { 243286654Sed .k_copyin = cloudabi64_kevent_copyin, 244286654Sed .k_copyout = cloudabi64_kevent_copyout, 245286654Sed .arg = &args, 246286654Sed }; 247285307Sed 248286656Sed /* 249286656Sed * Bandaid to support CloudABI futex constructs that are not 250286656Sed * implemented through FreeBSD's kqueue(). 251286656Sed */ 252297247Sed if (uap->nsubscriptions == 1) { 253286656Sed cloudabi64_subscription_t sub; 254286656Sed cloudabi64_event_t ev = {}; 255286656Sed int error; 256286656Sed 257286656Sed error = copyin(uap->in, &sub, sizeof(sub)); 258286656Sed if (error != 0) 259286656Sed return (error); 260286656Sed ev.userdata = sub.userdata; 261286656Sed ev.type = sub.type; 262286656Sed if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) { 263286656Sed /* Wait on a condition variable. */ 264286656Sed ev.condvar.condvar = sub.condvar.condvar; 265286656Sed ev.error = cloudabi_convert_errno( 266286656Sed cloudabi_futex_condvar_wait( 267286656Sed td, (cloudabi_condvar_t *)sub.condvar.condvar, 268286656Sed sub.condvar.condvar_scope, 269286656Sed (cloudabi_lock_t *)sub.condvar.lock, 270286656Sed sub.condvar.lock_scope, 271286656Sed CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0)); 272286656Sed td->td_retval[0] = 1; 273286656Sed return (copyout(&ev, uap->out, sizeof(ev))); 274286656Sed } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) { 275286656Sed /* Acquire a read lock. */ 276286656Sed ev.lock.lock = sub.lock.lock; 277286656Sed ev.error = cloudabi_convert_errno( 278286656Sed cloudabi_futex_lock_rdlock( 279286656Sed td, (cloudabi_lock_t *)sub.lock.lock, 280286656Sed sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, 281286656Sed UINT64_MAX, 0)); 282286656Sed td->td_retval[0] = 1; 283286656Sed return (copyout(&ev, uap->out, sizeof(ev))); 284286656Sed } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) { 285286656Sed /* Acquire a write lock. */ 286286656Sed ev.lock.lock = sub.lock.lock; 287286656Sed ev.error = cloudabi_convert_errno( 288286656Sed cloudabi_futex_lock_wrlock( 289286656Sed td, (cloudabi_lock_t *)sub.lock.lock, 290286656Sed sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, 291286656Sed UINT64_MAX, 0)); 292286656Sed td->td_retval[0] = 1; 293286656Sed return (copyout(&ev, uap->out, sizeof(ev))); 294286656Sed } 295297247Sed } else if (uap->nsubscriptions == 2) { 296286656Sed cloudabi64_subscription_t sub[2]; 297286656Sed cloudabi64_event_t ev[2] = {}; 298286656Sed int error; 299286656Sed 300286656Sed error = copyin(uap->in, &sub, sizeof(sub)); 301286656Sed if (error != 0) 302286656Sed return (error); 303286656Sed ev[0].userdata = sub[0].userdata; 304286656Sed ev[0].type = sub[0].type; 305286656Sed ev[1].userdata = sub[1].userdata; 306286656Sed ev[1].type = sub[1].type; 307286656Sed if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR && 308286656Sed sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && 309286656Sed sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { 310286656Sed /* Wait for a condition variable with timeout. */ 311286656Sed ev[0].condvar.condvar = sub[0].condvar.condvar; 312286656Sed ev[1].clock.identifier = sub[1].clock.identifier; 313286656Sed error = cloudabi_futex_condvar_wait( 314286656Sed td, (cloudabi_condvar_t *)sub[0].condvar.condvar, 315286656Sed sub[0].condvar.condvar_scope, 316286656Sed (cloudabi_lock_t *)sub[0].condvar.lock, 317286656Sed sub[0].condvar.lock_scope, sub[1].clock.clock_id, 318286656Sed sub[1].clock.timeout, sub[1].clock.precision); 319286656Sed if (error == ETIMEDOUT) { 320286656Sed td->td_retval[0] = 1; 321286656Sed return (copyout(&ev[1], uap->out, 322286656Sed sizeof(ev[1]))); 323286656Sed } 324286656Sed 325286656Sed ev[0].error = cloudabi_convert_errno(error); 326286656Sed td->td_retval[0] = 1; 327286656Sed return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 328286656Sed } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK && 329286656Sed sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && 330286656Sed sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { 331286656Sed /* Acquire a read lock with a timeout. */ 332286656Sed ev[0].lock.lock = sub[0].lock.lock; 333286656Sed ev[1].clock.identifier = sub[1].clock.identifier; 334286656Sed error = cloudabi_futex_lock_rdlock( 335286656Sed td, (cloudabi_lock_t *)sub[0].lock.lock, 336286656Sed sub[0].lock.lock_scope, sub[1].clock.clock_id, 337286656Sed sub[1].clock.timeout, sub[1].clock.precision); 338286656Sed if (error == ETIMEDOUT) { 339286656Sed td->td_retval[0] = 1; 340286656Sed return (copyout(&ev[1], uap->out, 341286656Sed sizeof(ev[1]))); 342286656Sed } 343286656Sed 344286656Sed ev[0].error = cloudabi_convert_errno(error); 345286656Sed td->td_retval[0] = 1; 346286656Sed return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 347286656Sed } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK && 348286656Sed sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && 349286656Sed sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { 350286656Sed /* Acquire a write lock with a timeout. */ 351286656Sed ev[0].lock.lock = sub[0].lock.lock; 352286656Sed ev[1].clock.identifier = sub[1].clock.identifier; 353286656Sed error = cloudabi_futex_lock_wrlock( 354286656Sed td, (cloudabi_lock_t *)sub[0].lock.lock, 355286656Sed sub[0].lock.lock_scope, sub[1].clock.clock_id, 356286656Sed sub[1].clock.timeout, sub[1].clock.precision); 357286656Sed if (error == ETIMEDOUT) { 358286656Sed td->td_retval[0] = 1; 359286656Sed return (copyout(&ev[1], uap->out, 360286656Sed sizeof(ev[1]))); 361286656Sed } 362286656Sed 363286656Sed ev[0].error = cloudabi_convert_errno(error); 364286656Sed td->td_retval[0] = 1; 365286656Sed return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 366286656Sed } 367286656Sed } 368286656Sed 369297247Sed return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops)); 370285307Sed} 371286318Sed 372286318Sedint 373286318Sedcloudabi64_sys_poll_fd(struct thread *td, 374286318Sed struct cloudabi64_sys_poll_fd_args *uap) 375286318Sed{ 376286654Sed struct cloudabi64_kevent_args args = { 377286654Sed .in = uap->in, 378286654Sed .out = uap->out, 379286654Sed .once = false, 380286654Sed }; 381286654Sed struct kevent_copyops copyops = { 382286654Sed .k_copyin = cloudabi64_kevent_copyin, 383286654Sed .k_copyout = cloudabi64_kevent_copyout, 384286654Sed .arg = &args, 385286654Sed }; 386286654Sed cloudabi64_subscription_t subtimo; 387286654Sed struct timespec timeout; 388286654Sed int error; 389286318Sed 390286654Sed if (uap->timeout != NULL) { 391286654Sed /* Poll with a timeout. */ 392286654Sed error = copyin(uap->timeout, &subtimo, sizeof(subtimo)); 393286654Sed if (error != 0) 394286654Sed return (error); 395286654Sed if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK || 396286654Sed subtimo.clock.flags != 0) 397286654Sed return (EINVAL); 398286654Sed timeout.tv_sec = subtimo.clock.timeout / 1000000000; 399286654Sed timeout.tv_nsec = subtimo.clock.timeout % 1000000000; 400286654Sed return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops, 401286654Sed &timeout)); 402286654Sed } else { 403286654Sed /* Poll without a timeout. */ 404286654Sed return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops, 405286654Sed NULL)); 406286654Sed } 407286318Sed} 408