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>
38 #include <compat/cloudabi64/cloudabi64_util.h>
40 /* Converts a FreeBSD signal number to a CloudABI signal number. */
41 static cloudabi_signal_t
42 convert_signal(int sig)
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,
73 /* Convert unknown signals to SIGABRT. */
74 if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
76 return (signals[sig]);
79 struct cloudabi64_kevent_args {
80 const cloudabi64_subscription_t *in;
81 cloudabi_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 = TO_PTR(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 kevp->fflags = NOTE_FILE_POLL;
128 case CLOUDABI_EVENTTYPE_FD_WRITE:
129 kevp->filter = EVFILT_WRITE;
130 kevp->ident = sub.fd_readwrite.fd;
132 case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
133 kevp->filter = EVFILT_PROCDESC;
134 kevp->ident = sub.proc_terminate.fd;
135 kevp->fflags = NOTE_EXIT;
138 kevp->flags = EV_ADD | EV_ONESHOT;
144 /* Converts FreeBSD's struct kevent to CloudABI's event objects. */
146 cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
149 struct cloudabi64_kevent_args *args;
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) {
159 ev.type = CLOUDABI_EVENTTYPE_CLOCK;
162 ev.type = CLOUDABI_EVENTTYPE_FD_READ;
165 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
167 case EVFILT_PROCDESC:
168 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
172 if ((kevp->flags & EV_ERROR) == 0) {
174 switch (kevp->filter) {
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;
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;
190 /* Process exited. */
191 ev.proc_terminate.signal = 0;
192 ev.proc_terminate.exitcode =
193 WEXITSTATUS(kevp->data);
199 ev.error = cloudabi_convert_errno(kevp->data);
203 /* TODO(ed): Copy out multiple entries at once. */
204 error = copyout(&ev, args->out++, sizeof(ev));
212 cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
214 struct cloudabi64_kevent_args args = {
218 struct kevent_copyops copyops = {
219 .k_copyin = cloudabi64_kevent_copyin,
220 .k_copyout = cloudabi64_kevent_copyout,
225 * Bandaid to support CloudABI futex constructs that are not
226 * implemented through FreeBSD's kqueue().
228 if (uap->nsubscriptions == 1) {
229 cloudabi64_subscription_t sub;
230 cloudabi_event_t ev = {};
233 error = copyin(uap->in, &sub, sizeof(sub));
236 ev.userdata = sub.userdata;
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)));
268 } else if (uap->nsubscriptions == 2) {
269 cloudabi64_subscription_t sub[2];
270 cloudabi_event_t ev[2] = {};
273 error = copyin(uap->in, &sub, sizeof(sub));
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,
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,
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,
333 ev[0].error = cloudabi_convert_errno(error);
334 td->td_retval[0] = 1;
335 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
339 return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops));