cloudabi64_poll.c revision 316574
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 316574 2017-04-06 15:10:36Z 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 cloudabi64_event_t *out; 82 bool once; 83}; 84 85/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */ 86static int 87cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count) 88{ 89 cloudabi64_subscription_t sub; 90 struct cloudabi64_kevent_args *args; 91 cloudabi_timestamp_t ts; 92 int error; 93 94 args = arg; 95 while (count-- > 0) { 96 /* TODO(ed): Copy in multiple entries at once. */ 97 error = copyin(args->in++, &sub, sizeof(sub)); 98 if (error != 0) 99 return (error); 100 101 memset(kevp, 0, sizeof(*kevp)); 102 kevp->udata = TO_PTR(sub.userdata); 103 switch (sub.type) { 104 case CLOUDABI_EVENTTYPE_CLOCK: 105 kevp->filter = EVFILT_TIMER; 106 kevp->ident = sub.clock.identifier; 107 kevp->fflags = NOTE_NSECONDS; 108 if ((sub.clock.flags & 109 CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 && 110 sub.clock.timeout > 0) { 111 /* Convert absolute timestamp to a relative. */ 112 error = cloudabi_clock_time_get(curthread, 113 sub.clock.clock_id, &ts); 114 if (error != 0) 115 return (error); 116 ts = ts > sub.clock.timeout ? 0 : 117 sub.clock.timeout - ts; 118 } else { 119 /* Relative timestamp. */ 120 ts = sub.clock.timeout; 121 } 122 kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts; 123 break; 124 case CLOUDABI_EVENTTYPE_FD_READ: 125 kevp->filter = EVFILT_READ; 126 kevp->ident = sub.fd_readwrite.fd; 127 if ((sub.fd_readwrite.flags & 128 CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0) 129 kevp->fflags = NOTE_FILE_POLL; 130 break; 131 case CLOUDABI_EVENTTYPE_FD_WRITE: 132 kevp->filter = EVFILT_WRITE; 133 kevp->ident = sub.fd_readwrite.fd; 134 break; 135 case CLOUDABI_EVENTTYPE_PROC_TERMINATE: 136 kevp->filter = EVFILT_PROCDESC; 137 kevp->ident = sub.proc_terminate.fd; 138 kevp->fflags = NOTE_EXIT; 139 break; 140 } 141 if (args->once) { 142 /* Ignore flags. Simply use oneshot mode. */ 143 kevp->flags = EV_ADD | EV_ONESHOT; 144 } else { 145 /* Translate flags. */ 146 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0) 147 kevp->flags |= EV_ADD; 148 if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0) 149 kevp->flags |= EV_CLEAR; 150 if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0) 151 kevp->flags |= EV_DELETE; 152 if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0) 153 kevp->flags |= EV_DISABLE; 154 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0) 155 kevp->flags |= EV_ENABLE; 156 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0) 157 kevp->flags |= EV_ONESHOT; 158 } 159 ++kevp; 160 } 161 return (0); 162} 163 164/* Converts FreeBSD's struct kevent to CloudABI's event objects. */ 165static int 166cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count) 167{ 168 cloudabi64_event_t ev; 169 struct cloudabi64_kevent_args *args; 170 int error; 171 172 args = arg; 173 while (count-- > 0) { 174 /* Convert fields that should always be present. */ 175 memset(&ev, 0, sizeof(ev)); 176 ev.userdata = (uintptr_t)kevp->udata; 177 switch (kevp->filter) { 178 case EVFILT_TIMER: 179 ev.type = CLOUDABI_EVENTTYPE_CLOCK; 180 ev.clock.identifier = kevp->ident; 181 break; 182 case EVFILT_READ: 183 ev.type = CLOUDABI_EVENTTYPE_FD_READ; 184 ev.fd_readwrite.fd = kevp->ident; 185 break; 186 case EVFILT_WRITE: 187 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE; 188 ev.fd_readwrite.fd = kevp->ident; 189 break; 190 case EVFILT_PROCDESC: 191 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE; 192 ev.proc_terminate.fd = kevp->ident; 193 break; 194 } 195 196 if ((kevp->flags & EV_ERROR) == 0) { 197 /* Success. */ 198 switch (kevp->filter) { 199 case EVFILT_READ: 200 case EVFILT_WRITE: 201 ev.fd_readwrite.nbytes = kevp->data; 202 if ((kevp->flags & EV_EOF) != 0) { 203 ev.fd_readwrite.flags |= 204 CLOUDABI_EVENT_FD_READWRITE_HANGUP; 205 } 206 break; 207 case EVFILT_PROCDESC: 208 if (WIFSIGNALED(kevp->data)) { 209 /* Process got signalled. */ 210 ev.proc_terminate.signal = 211 convert_signal(WTERMSIG(kevp->data)); 212 ev.proc_terminate.exitcode = 0; 213 } else { 214 /* Process exited. */ 215 ev.proc_terminate.signal = 0; 216 ev.proc_terminate.exitcode = 217 WEXITSTATUS(kevp->data); 218 } 219 break; 220 } 221 } else { 222 /* Error. */ 223 ev.error = cloudabi_convert_errno(kevp->data); 224 } 225 ++kevp; 226 227 /* TODO(ed): Copy out multiple entries at once. */ 228 error = copyout(&ev, args->out++, sizeof(ev)); 229 if (error != 0) 230 return (error); 231 } 232 return (0); 233} 234 235int 236cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) 237{ 238 struct cloudabi64_kevent_args args = { 239 .in = uap->in, 240 .out = uap->out, 241 .once = true, 242 }; 243 struct kevent_copyops copyops = { 244 .k_copyin = cloudabi64_kevent_copyin, 245 .k_copyout = cloudabi64_kevent_copyout, 246 .arg = &args, 247 }; 248 249 /* 250 * Bandaid to support CloudABI futex constructs that are not 251 * implemented through FreeBSD's kqueue(). 252 */ 253 if (uap->nsubscriptions == 1) { 254 cloudabi64_subscription_t sub; 255 cloudabi64_event_t ev = {}; 256 int error; 257 258 error = copyin(uap->in, &sub, sizeof(sub)); 259 if (error != 0) 260 return (error); 261 ev.userdata = sub.userdata; 262 ev.type = sub.type; 263 if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) { 264 /* Wait on a condition variable. */ 265 ev.condvar.condvar = sub.condvar.condvar; 266 ev.error = cloudabi_convert_errno( 267 cloudabi_futex_condvar_wait( 268 td, TO_PTR(sub.condvar.condvar), 269 sub.condvar.condvar_scope, 270 TO_PTR(sub.condvar.lock), 271 sub.condvar.lock_scope, 272 CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0)); 273 td->td_retval[0] = 1; 274 return (copyout(&ev, uap->out, sizeof(ev))); 275 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) { 276 /* Acquire a read lock. */ 277 ev.lock.lock = sub.lock.lock; 278 ev.error = cloudabi_convert_errno( 279 cloudabi_futex_lock_rdlock( 280 td, TO_PTR(sub.lock.lock), 281 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, 282 UINT64_MAX, 0)); 283 td->td_retval[0] = 1; 284 return (copyout(&ev, uap->out, sizeof(ev))); 285 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) { 286 /* Acquire a write lock. */ 287 ev.lock.lock = sub.lock.lock; 288 ev.error = cloudabi_convert_errno( 289 cloudabi_futex_lock_wrlock( 290 td, TO_PTR(sub.lock.lock), 291 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, 292 UINT64_MAX, 0)); 293 td->td_retval[0] = 1; 294 return (copyout(&ev, uap->out, sizeof(ev))); 295 } 296 } else if (uap->nsubscriptions == 2) { 297 cloudabi64_subscription_t sub[2]; 298 cloudabi64_event_t ev[2] = {}; 299 int error; 300 301 error = copyin(uap->in, &sub, sizeof(sub)); 302 if (error != 0) 303 return (error); 304 ev[0].userdata = sub[0].userdata; 305 ev[0].type = sub[0].type; 306 ev[1].userdata = sub[1].userdata; 307 ev[1].type = sub[1].type; 308 if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR && 309 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && 310 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { 311 /* Wait for a condition variable with timeout. */ 312 ev[0].condvar.condvar = sub[0].condvar.condvar; 313 ev[1].clock.identifier = sub[1].clock.identifier; 314 error = cloudabi_futex_condvar_wait( 315 td, TO_PTR(sub[0].condvar.condvar), 316 sub[0].condvar.condvar_scope, 317 TO_PTR(sub[0].condvar.lock), 318 sub[0].condvar.lock_scope, sub[1].clock.clock_id, 319 sub[1].clock.timeout, sub[1].clock.precision); 320 if (error == ETIMEDOUT) { 321 td->td_retval[0] = 1; 322 return (copyout(&ev[1], uap->out, 323 sizeof(ev[1]))); 324 } 325 326 ev[0].error = cloudabi_convert_errno(error); 327 td->td_retval[0] = 1; 328 return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 329 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK && 330 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && 331 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { 332 /* Acquire a read lock with a timeout. */ 333 ev[0].lock.lock = sub[0].lock.lock; 334 ev[1].clock.identifier = sub[1].clock.identifier; 335 error = cloudabi_futex_lock_rdlock( 336 td, TO_PTR(sub[0].lock.lock), 337 sub[0].lock.lock_scope, sub[1].clock.clock_id, 338 sub[1].clock.timeout, sub[1].clock.precision); 339 if (error == ETIMEDOUT) { 340 td->td_retval[0] = 1; 341 return (copyout(&ev[1], uap->out, 342 sizeof(ev[1]))); 343 } 344 345 ev[0].error = cloudabi_convert_errno(error); 346 td->td_retval[0] = 1; 347 return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 348 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK && 349 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && 350 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { 351 /* Acquire a write lock with a timeout. */ 352 ev[0].lock.lock = sub[0].lock.lock; 353 ev[1].clock.identifier = sub[1].clock.identifier; 354 error = cloudabi_futex_lock_wrlock( 355 td, TO_PTR(sub[0].lock.lock), 356 sub[0].lock.lock_scope, sub[1].clock.clock_id, 357 sub[1].clock.timeout, sub[1].clock.precision); 358 if (error == ETIMEDOUT) { 359 td->td_retval[0] = 1; 360 return (copyout(&ev[1], uap->out, 361 sizeof(ev[1]))); 362 } 363 364 ev[0].error = cloudabi_convert_errno(error); 365 td->td_retval[0] = 1; 366 return (copyout(&ev[0], uap->out, sizeof(ev[0]))); 367 } 368 } 369 370 return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops)); 371} 372 373int 374cloudabi64_sys_poll_fd(struct thread *td, 375 struct cloudabi64_sys_poll_fd_args *uap) 376{ 377 struct cloudabi64_kevent_args args = { 378 .in = uap->in, 379 .out = uap->out, 380 .once = false, 381 }; 382 struct kevent_copyops copyops = { 383 .k_copyin = cloudabi64_kevent_copyin, 384 .k_copyout = cloudabi64_kevent_copyout, 385 .arg = &args, 386 }; 387 cloudabi64_subscription_t subtimo; 388 struct timespec timeout; 389 int error; 390 391 if (uap->timeout != NULL) { 392 /* Poll with a timeout. */ 393 error = copyin(uap->timeout, &subtimo, sizeof(subtimo)); 394 if (error != 0) 395 return (error); 396 if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK || 397 subtimo.clock.flags != 0) 398 return (EINVAL); 399 timeout.tv_sec = subtimo.clock.timeout / 1000000000; 400 timeout.tv_nsec = subtimo.clock.timeout % 1000000000; 401 return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len, 402 ©ops, &timeout)); 403 } else { 404 /* Poll without a timeout. */ 405 return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len, 406 ©ops, NULL)); 407 } 408} 409