1/*- 2 * Copyright (c) 2015 Nuxi, https://nuxi.nl/ 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: stable/11/sys/compat/cloudabi64/cloudabi64_poll.c 328127 2018-01-18 13:43:09Z ed $"); 28 29#include <sys/param.h> 30#include <sys/proc.h> 31#include <sys/syscallsubr.h> 32 33#include <contrib/cloudabi/cloudabi64_types.h> 34 35#include <compat/cloudabi/cloudabi_util.h> 36 37#include <compat/cloudabi64/cloudabi64_proto.h> 38#include <compat/cloudabi64/cloudabi64_util.h> 39 40/* Converts a FreeBSD signal number to a CloudABI signal number. */ 41static cloudabi_signal_t 42convert_signal(int sig) 43{ 44 static const cloudabi_signal_t signals[] = { 45 [SIGABRT] = CLOUDABI_SIGABRT, 46 [SIGALRM] = CLOUDABI_SIGALRM, 47 [SIGBUS] = CLOUDABI_SIGBUS, 48 [SIGCHLD] = CLOUDABI_SIGCHLD, 49 [SIGCONT] = CLOUDABI_SIGCONT, 50 [SIGFPE] = CLOUDABI_SIGFPE, 51 [SIGHUP] = CLOUDABI_SIGHUP, 52 [SIGILL] = CLOUDABI_SIGILL, 53 [SIGINT] = CLOUDABI_SIGINT, 54 [SIGKILL] = CLOUDABI_SIGKILL, 55 [SIGPIPE] = CLOUDABI_SIGPIPE, 56 [SIGQUIT] = CLOUDABI_SIGQUIT, 57 [SIGSEGV] = CLOUDABI_SIGSEGV, 58 [SIGSTOP] = CLOUDABI_SIGSTOP, 59 [SIGSYS] = CLOUDABI_SIGSYS, 60 [SIGTERM] = CLOUDABI_SIGTERM, 61 [SIGTRAP] = CLOUDABI_SIGTRAP, 62 [SIGTSTP] = CLOUDABI_SIGTSTP, 63 [SIGTTIN] = CLOUDABI_SIGTTIN, 64 [SIGTTOU] = CLOUDABI_SIGTTOU, 65 [SIGURG] = CLOUDABI_SIGURG, 66 [SIGUSR1] = CLOUDABI_SIGUSR1, 67 [SIGUSR2] = CLOUDABI_SIGUSR2, 68 [SIGVTALRM] = CLOUDABI_SIGVTALRM, 69 [SIGXCPU] = CLOUDABI_SIGXCPU, 70 [SIGXFSZ] = CLOUDABI_SIGXFSZ, 71 }; 72 73 /* Convert unknown signals to SIGABRT. */ 74 if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0) 75 return (SIGABRT); 76 return (signals[sig]); 77} 78 79struct cloudabi64_kevent_args { 80 const cloudabi64_subscription_t *in; 81 cloudabi_event_t *out; 82}; 83 84/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */ 85static int 86cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count) 87{ 88 cloudabi64_subscription_t sub; 89 struct cloudabi64_kevent_args *args; 90 cloudabi_timestamp_t ts; 91 int error; 92 93 args = arg; 94 while (count-- > 0) { 95 /* TODO(ed): Copy in multiple entries at once. */ 96 error = copyin(args->in++, &sub, sizeof(sub)); 97 if (error != 0) 98 return (error); 99 100 memset(kevp, 0, sizeof(*kevp)); 101 kevp->udata = TO_PTR(sub.userdata); 102 switch (sub.type) { 103 case CLOUDABI_EVENTTYPE_CLOCK: 104 kevp->filter = EVFILT_TIMER; 105 kevp->ident = sub.clock.identifier; 106 kevp->fflags = NOTE_NSECONDS; 107 if ((sub.clock.flags & 108 CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 && 109 sub.clock.timeout > 0) { 110 /* Convert absolute timestamp to a relative. */ 111 error = cloudabi_clock_time_get(curthread, 112 sub.clock.clock_id, &ts); 113 if (error != 0) 114 return (error); 115 ts = ts > sub.clock.timeout ? 0 : 116 sub.clock.timeout - ts; 117 } else { 118 /* Relative timestamp. */ 119 ts = sub.clock.timeout; 120 } 121 kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts; 122 break; 123 case CLOUDABI_EVENTTYPE_FD_READ: 124 kevp->filter = EVFILT_READ; 125 kevp->ident = sub.fd_readwrite.fd; 126 kevp->fflags = NOTE_FILE_POLL; 127 break; 128 case CLOUDABI_EVENTTYPE_FD_WRITE: 129 kevp->filter = EVFILT_WRITE; 130 kevp->ident = sub.fd_readwrite.fd; 131 break; 132 case CLOUDABI_EVENTTYPE_PROC_TERMINATE: 133 kevp->filter = EVFILT_PROCDESC; 134 kevp->ident = sub.proc_terminate.fd; 135 kevp->fflags = NOTE_EXIT; 136 break; 137 } 138 kevp->flags = EV_ADD | EV_ONESHOT; 139 ++kevp; 140 } 141 return (0); 142} 143 144/* Converts FreeBSD's struct kevent to CloudABI's event objects. */ 145static int 146cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count) 147{ 148 cloudabi_event_t ev; 149 struct cloudabi64_kevent_args *args; 150 int error; 151 152 args = arg; 153 while (count-- > 0) { 154 /* Convert fields that should always be present. */ 155 memset(&ev, 0, sizeof(ev)); 156 ev.userdata = (uintptr_t)kevp->udata; 157 switch (kevp->filter) { 158 case EVFILT_TIMER: 159 ev.type = CLOUDABI_EVENTTYPE_CLOCK; 160 break; 161 case EVFILT_READ: 162 ev.type = CLOUDABI_EVENTTYPE_FD_READ; 163 break; 164 case EVFILT_WRITE: 165 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE; 166 break; 167 case EVFILT_PROCDESC: 168 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE; 169 break; 170 } 171 172 if ((kevp->flags & EV_ERROR) == 0) { 173 /* Success. */ 174 switch (kevp->filter) { 175 case EVFILT_READ: 176 case EVFILT_WRITE: 177 ev.fd_readwrite.nbytes = kevp->data; 178 if ((kevp->flags & EV_EOF) != 0) { 179 ev.fd_readwrite.flags |= 180 CLOUDABI_EVENT_FD_READWRITE_HANGUP; 181 } 182 break; 183 case EVFILT_PROCDESC: 184 if (WIFSIGNALED(kevp->data)) { 185 /* Process got signalled. */ 186 ev.proc_terminate.signal = 187 convert_signal(WTERMSIG(kevp->data)); 188 ev.proc_terminate.exitcode = 0; 189 } else { 190 /* Process exited. */ 191 ev.proc_terminate.signal = 0; 192 ev.proc_terminate.exitcode = 193 WEXITSTATUS(kevp->data); 194 } 195 break; 196 } 197 } else { 198 /* Error. */ 199 ev.error = cloudabi_convert_errno(kevp->data); 200 } 201 ++kevp; 202 203 /* TODO(ed): Copy out multiple entries at once. */ 204 error = copyout(&ev, args->out++, sizeof(ev)); 205 if (error != 0) 206 return (error); 207 } 208 return (0); 209} 210 211int 212cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) 213{ 214 struct cloudabi64_kevent_args args = { 215 .in = uap->in, 216 .out = uap->out, 217 }; 218 struct kevent_copyops copyops = { 219 .k_copyin = cloudabi64_kevent_copyin, 220 .k_copyout = cloudabi64_kevent_copyout, 221 .arg = &args, 222 }; 223 224 /* 225 * Bandaid to support CloudABI futex constructs that are not 226 * implemented through FreeBSD's kqueue(). 227 */ 228 if (uap->nsubscriptions == 1) { 229 cloudabi64_subscription_t sub; 230 cloudabi_event_t ev = {}; 231 int error; 232 233 error = copyin(uap->in, &sub, sizeof(sub)); 234 if (error != 0) 235 return (error); 236 ev.userdata = sub.userdata; 237 ev.type = sub.type; 238 if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) { 239 /* Wait on a condition variable. */ 240 ev.error = cloudabi_convert_errno( 241 cloudabi_futex_condvar_wait( 242 td, TO_PTR(sub.condvar.condvar), 243 sub.condvar.condvar_scope, 244 TO_PTR(sub.condvar.lock), 245 sub.condvar.lock_scope, 246 CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0, true)); 247 td->td_retval[0] = 1; 248 return (copyout(&ev, uap->out, sizeof(ev))); 249 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) { 250 /* Acquire a read lock. */ 251 ev.error = cloudabi_convert_errno( 252 cloudabi_futex_lock_rdlock( 253 td, TO_PTR(sub.lock.lock), 254 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, 255 UINT64_MAX, 0, true)); 256 td->td_retval[0] = 1; 257 return (copyout(&ev, uap->out, sizeof(ev))); 258 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) { 259 /* Acquire a write lock. */ 260 ev.error = cloudabi_convert_errno( 261 cloudabi_futex_lock_wrlock( 262 td, TO_PTR(sub.lock.lock), 263 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, 264 UINT64_MAX, 0, true)); 265 td->td_retval[0] = 1; 266 return (copyout(&ev, uap->out, sizeof(ev))); 267 } 268 } else if (uap->nsubscriptions == 2) { 269 cloudabi64_subscription_t sub[2]; 270 cloudabi_event_t ev[2] = {}; 271 int error; 272 273 error = copyin(uap->in, &sub, sizeof(sub)); 274 if (error != 0) 275 return (error); 276 ev[0].userdata = sub[0].userdata; 277 ev[0].type = sub[0].type; 278 ev[1].userdata = sub[1].userdata; 279 ev[1].type = sub[1].type; 280 if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR && 281 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK) { 282 /* Wait for a condition variable with timeout. */ 283 error = cloudabi_futex_condvar_wait( 284 td, TO_PTR(sub[0].condvar.condvar), 285 sub[0].condvar.condvar_scope, 286 TO_PTR(sub[0].condvar.lock), 287 sub[0].condvar.lock_scope, sub[1].clock.clock_id, 288 sub[1].clock.timeout, sub[1].clock.precision, 289 (sub[1].clock.flags & 290 CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0); 291 if (error == ETIMEDOUT) { 292 td->td_retval[0] = 1; 293 return (copyout(&ev[1], uap->out, 294 sizeof(ev[1]))); 295 } 296 297 ev[0].error = cloudabi_convert_errno(error); 298 td->td_retval[0] = 1; 299 return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 300 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK && 301 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK) { 302 /* Acquire a read lock with a timeout. */ 303 error = cloudabi_futex_lock_rdlock( 304 td, TO_PTR(sub[0].lock.lock), 305 sub[0].lock.lock_scope, sub[1].clock.clock_id, 306 sub[1].clock.timeout, sub[1].clock.precision, 307 (sub[1].clock.flags & 308 CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0); 309 if (error == ETIMEDOUT) { 310 td->td_retval[0] = 1; 311 return (copyout(&ev[1], uap->out, 312 sizeof(ev[1]))); 313 } 314 315 ev[0].error = cloudabi_convert_errno(error); 316 td->td_retval[0] = 1; 317 return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 318 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK && 319 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK) { 320 /* Acquire a write lock with a timeout. */ 321 error = cloudabi_futex_lock_wrlock( 322 td, TO_PTR(sub[0].lock.lock), 323 sub[0].lock.lock_scope, sub[1].clock.clock_id, 324 sub[1].clock.timeout, sub[1].clock.precision, 325 (sub[1].clock.flags & 326 CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0); 327 if (error == ETIMEDOUT) { 328 td->td_retval[0] = 1; 329 return (copyout(&ev[1], uap->out, 330 sizeof(ev[1]))); 331 } 332 333 ev[0].error = cloudabi_convert_errno(error); 334 td->td_retval[0] = 1; 335 return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 336 } 337 } 338 339 return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops)); 340} 341