2 * Copyright (c) 2015 Nuxi, https://nuxi.nl/
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/param.h>
31 #include <sys/syscallsubr.h>
33 #include <contrib/cloudabi/cloudabi64_types.h>
35 #include <compat/cloudabi/cloudabi_util.h>
37 #include <compat/cloudabi64/cloudabi64_proto.h>
39 /* Converts a FreeBSD signal number to a CloudABI signal number. */
40 static cloudabi_signal_t
41 convert_signal(int sig)
43 static const cloudabi_signal_t signals[] = {
44 [SIGABRT] = CLOUDABI_SIGABRT,
45 [SIGALRM] = CLOUDABI_SIGALRM,
46 [SIGBUS] = CLOUDABI_SIGBUS,
47 [SIGCHLD] = CLOUDABI_SIGCHLD,
48 [SIGCONT] = CLOUDABI_SIGCONT,
49 [SIGFPE] = CLOUDABI_SIGFPE,
50 [SIGHUP] = CLOUDABI_SIGHUP,
51 [SIGILL] = CLOUDABI_SIGILL,
52 [SIGINT] = CLOUDABI_SIGINT,
53 [SIGKILL] = CLOUDABI_SIGKILL,
54 [SIGPIPE] = CLOUDABI_SIGPIPE,
55 [SIGQUIT] = CLOUDABI_SIGQUIT,
56 [SIGSEGV] = CLOUDABI_SIGSEGV,
57 [SIGSTOP] = CLOUDABI_SIGSTOP,
58 [SIGSYS] = CLOUDABI_SIGSYS,
59 [SIGTERM] = CLOUDABI_SIGTERM,
60 [SIGTRAP] = CLOUDABI_SIGTRAP,
61 [SIGTSTP] = CLOUDABI_SIGTSTP,
62 [SIGTTIN] = CLOUDABI_SIGTTIN,
63 [SIGTTOU] = CLOUDABI_SIGTTOU,
64 [SIGURG] = CLOUDABI_SIGURG,
65 [SIGUSR1] = CLOUDABI_SIGUSR1,
66 [SIGUSR2] = CLOUDABI_SIGUSR2,
67 [SIGVTALRM] = CLOUDABI_SIGVTALRM,
68 [SIGXCPU] = CLOUDABI_SIGXCPU,
69 [SIGXFSZ] = CLOUDABI_SIGXFSZ,
72 /* Convert unknown signals to SIGABRT. */
73 if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
75 return (signals[sig]);
78 struct cloudabi64_kevent_args {
79 const cloudabi64_subscription_t *in;
80 cloudabi64_event_t *out;
84 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
86 cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
88 cloudabi64_subscription_t sub;
89 struct cloudabi64_kevent_args *args;
90 cloudabi_timestamp_t ts;
95 /* TODO(ed): Copy in multiple entries at once. */
96 error = copyin(args->in++, &sub, sizeof(sub));
100 memset(kevp, 0, sizeof(*kevp));
101 kevp->udata = (void *)sub.userdata;
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);
115 ts = ts > sub.clock.timeout ? 0 :
116 sub.clock.timeout - ts;
118 /* Relative timestamp. */
119 ts = sub.clock.timeout;
121 kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
123 case CLOUDABI_EVENTTYPE_FD_READ:
124 kevp->filter = EVFILT_READ;
125 kevp->ident = sub.fd_readwrite.fd;
126 if ((sub.fd_readwrite.flags &
127 CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0)
128 kevp->fflags = NOTE_FILE_POLL;
130 case CLOUDABI_EVENTTYPE_FD_WRITE:
131 kevp->filter = EVFILT_WRITE;
132 kevp->ident = sub.fd_readwrite.fd;
134 case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
135 kevp->filter = EVFILT_PROCDESC;
136 kevp->ident = sub.proc_terminate.fd;
137 kevp->fflags = NOTE_EXIT;
141 /* Ignore flags. Simply use oneshot mode. */
142 kevp->flags = EV_ADD | EV_ONESHOT;
144 /* Translate flags. */
145 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0)
146 kevp->flags |= EV_ADD;
147 if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0)
148 kevp->flags |= EV_CLEAR;
149 if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0)
150 kevp->flags |= EV_DELETE;
151 if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0)
152 kevp->flags |= EV_DISABLE;
153 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0)
154 kevp->flags |= EV_ENABLE;
155 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0)
156 kevp->flags |= EV_ONESHOT;
163 /* Converts FreeBSD's struct kevent to CloudABI's event objects. */
165 cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
167 cloudabi64_event_t ev;
168 struct cloudabi64_kevent_args *args;
172 while (count-- > 0) {
173 /* Convert fields that should always be present. */
174 memset(&ev, 0, sizeof(ev));
175 ev.userdata = (uintptr_t)kevp->udata;
176 switch (kevp->filter) {
178 ev.type = CLOUDABI_EVENTTYPE_CLOCK;
179 ev.clock.identifier = kevp->ident;
182 ev.type = CLOUDABI_EVENTTYPE_FD_READ;
183 ev.fd_readwrite.fd = kevp->ident;
186 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
187 ev.fd_readwrite.fd = kevp->ident;
189 case EVFILT_PROCDESC:
190 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
191 ev.proc_terminate.fd = kevp->ident;
195 if ((kevp->flags & EV_ERROR) == 0) {
197 switch (kevp->filter) {
200 ev.fd_readwrite.nbytes = kevp->data;
201 if ((kevp->flags & EV_EOF) != 0) {
202 ev.fd_readwrite.flags |=
203 CLOUDABI_EVENT_FD_READWRITE_HANGUP;
206 case EVFILT_PROCDESC:
207 if (WIFSIGNALED(kevp->data)) {
208 /* Process got signalled. */
209 ev.proc_terminate.signal =
210 convert_signal(WTERMSIG(kevp->data));
211 ev.proc_terminate.exitcode = 0;
213 /* Process exited. */
214 ev.proc_terminate.signal = 0;
215 ev.proc_terminate.exitcode =
216 WEXITSTATUS(kevp->data);
222 ev.error = cloudabi_convert_errno(kevp->data);
226 /* TODO(ed): Copy out multiple entries at once. */
227 error = copyout(&ev, args->out++, sizeof(ev));
235 cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
237 struct cloudabi64_kevent_args args = {
242 struct kevent_copyops copyops = {
243 .k_copyin = cloudabi64_kevent_copyin,
244 .k_copyout = cloudabi64_kevent_copyout,
249 * Bandaid to support CloudABI futex constructs that are not
250 * implemented through FreeBSD's kqueue().
252 if (uap->nsubscriptions == 1) {
253 cloudabi64_subscription_t sub;
254 cloudabi64_event_t ev = {};
257 error = copyin(uap->in, &sub, sizeof(sub));
260 ev.userdata = sub.userdata;
262 if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) {
263 /* Wait on a condition variable. */
264 ev.condvar.condvar = sub.condvar.condvar;
265 ev.error = cloudabi_convert_errno(
266 cloudabi_futex_condvar_wait(
267 td, (cloudabi_condvar_t *)sub.condvar.condvar,
268 sub.condvar.condvar_scope,
269 (cloudabi_lock_t *)sub.condvar.lock,
270 sub.condvar.lock_scope,
271 CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0));
272 td->td_retval[0] = 1;
273 return (copyout(&ev, uap->out, sizeof(ev)));
274 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) {
275 /* Acquire a read lock. */
276 ev.lock.lock = sub.lock.lock;
277 ev.error = cloudabi_convert_errno(
278 cloudabi_futex_lock_rdlock(
279 td, (cloudabi_lock_t *)sub.lock.lock,
280 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
282 td->td_retval[0] = 1;
283 return (copyout(&ev, uap->out, sizeof(ev)));
284 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) {
285 /* Acquire a write lock. */
286 ev.lock.lock = sub.lock.lock;
287 ev.error = cloudabi_convert_errno(
288 cloudabi_futex_lock_wrlock(
289 td, (cloudabi_lock_t *)sub.lock.lock,
290 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
292 td->td_retval[0] = 1;
293 return (copyout(&ev, uap->out, sizeof(ev)));
295 } else if (uap->nsubscriptions == 2) {
296 cloudabi64_subscription_t sub[2];
297 cloudabi64_event_t ev[2] = {};
300 error = copyin(uap->in, &sub, sizeof(sub));
303 ev[0].userdata = sub[0].userdata;
304 ev[0].type = sub[0].type;
305 ev[1].userdata = sub[1].userdata;
306 ev[1].type = sub[1].type;
307 if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR &&
308 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
309 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
310 /* Wait for a condition variable with timeout. */
311 ev[0].condvar.condvar = sub[0].condvar.condvar;
312 ev[1].clock.identifier = sub[1].clock.identifier;
313 error = cloudabi_futex_condvar_wait(
314 td, (cloudabi_condvar_t *)sub[0].condvar.condvar,
315 sub[0].condvar.condvar_scope,
316 (cloudabi_lock_t *)sub[0].condvar.lock,
317 sub[0].condvar.lock_scope, sub[1].clock.clock_id,
318 sub[1].clock.timeout, sub[1].clock.precision);
319 if (error == ETIMEDOUT) {
320 td->td_retval[0] = 1;
321 return (copyout(&ev[1], uap->out,
325 ev[0].error = cloudabi_convert_errno(error);
326 td->td_retval[0] = 1;
327 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
328 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK &&
329 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
330 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
331 /* Acquire a read lock with a timeout. */
332 ev[0].lock.lock = sub[0].lock.lock;
333 ev[1].clock.identifier = sub[1].clock.identifier;
334 error = cloudabi_futex_lock_rdlock(
335 td, (cloudabi_lock_t *)sub[0].lock.lock,
336 sub[0].lock.lock_scope, sub[1].clock.clock_id,
337 sub[1].clock.timeout, sub[1].clock.precision);
338 if (error == ETIMEDOUT) {
339 td->td_retval[0] = 1;
340 return (copyout(&ev[1], uap->out,
344 ev[0].error = cloudabi_convert_errno(error);
345 td->td_retval[0] = 1;
346 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
347 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK &&
348 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
349 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
350 /* Acquire a write lock with a timeout. */
351 ev[0].lock.lock = sub[0].lock.lock;
352 ev[1].clock.identifier = sub[1].clock.identifier;
353 error = cloudabi_futex_lock_wrlock(
354 td, (cloudabi_lock_t *)sub[0].lock.lock,
355 sub[0].lock.lock_scope, sub[1].clock.clock_id,
356 sub[1].clock.timeout, sub[1].clock.precision);
357 if (error == ETIMEDOUT) {
358 td->td_retval[0] = 1;
359 return (copyout(&ev[1], uap->out,
363 ev[0].error = cloudabi_convert_errno(error);
364 td->td_retval[0] = 1;
365 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
369 return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops));
373 cloudabi64_sys_poll_fd(struct thread *td,
374 struct cloudabi64_sys_poll_fd_args *uap)
376 struct cloudabi64_kevent_args args = {
381 struct kevent_copyops copyops = {
382 .k_copyin = cloudabi64_kevent_copyin,
383 .k_copyout = cloudabi64_kevent_copyout,
386 cloudabi64_subscription_t subtimo;
387 struct timespec timeout;
390 if (uap->timeout != NULL) {
391 /* Poll with a timeout. */
392 error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
395 if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
396 subtimo.clock.flags != 0)
398 timeout.tv_sec = subtimo.clock.timeout / 1000000000;
399 timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
400 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops,
403 /* Poll without a timeout. */
404 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops,