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/cloudabi32_types.h>
35 #include <compat/cloudabi/cloudabi_util.h>
37 #include <compat/cloudabi32/cloudabi32_proto.h>
38 #include <compat/cloudabi32/cloudabi32_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 cloudabi32_kevent_args {
80 const cloudabi32_subscription_t *in;
81 cloudabi32_event_t *out;
85 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
87 cloudabi32_kevent_copyin(void *arg, struct kevent *kevp, int count)
89 cloudabi32_subscription_t sub;
90 struct cloudabi32_kevent_args *args;
91 cloudabi_timestamp_t ts;
96 /* TODO(ed): Copy in multiple entries at once. */
97 error = copyin(args->in++, &sub, sizeof(sub));
101 memset(kevp, 0, sizeof(*kevp));
102 kevp->udata = TO_PTR(sub.userdata);
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);
116 ts = ts > sub.clock.timeout ? 0 :
117 sub.clock.timeout - ts;
119 /* Relative timestamp. */
120 ts = sub.clock.timeout;
122 kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
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;
131 case CLOUDABI_EVENTTYPE_FD_WRITE:
132 kevp->filter = EVFILT_WRITE;
133 kevp->ident = sub.fd_readwrite.fd;
135 case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
136 kevp->filter = EVFILT_PROCDESC;
137 kevp->ident = sub.proc_terminate.fd;
138 kevp->fflags = NOTE_EXIT;
142 /* Ignore flags. Simply use oneshot mode. */
143 kevp->flags = EV_ADD | EV_ONESHOT;
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;
164 /* Converts FreeBSD's struct kevent to CloudABI's event objects. */
166 cloudabi32_kevent_copyout(void *arg, struct kevent *kevp, int count)
168 cloudabi32_event_t ev;
169 struct cloudabi32_kevent_args *args;
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) {
179 ev.type = CLOUDABI_EVENTTYPE_CLOCK;
180 ev.clock.identifier = kevp->ident;
183 ev.type = CLOUDABI_EVENTTYPE_FD_READ;
184 ev.fd_readwrite.fd = kevp->ident;
187 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
188 ev.fd_readwrite.fd = kevp->ident;
190 case EVFILT_PROCDESC:
191 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
192 ev.proc_terminate.fd = kevp->ident;
196 if ((kevp->flags & EV_ERROR) == 0) {
198 switch (kevp->filter) {
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;
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;
214 /* Process exited. */
215 ev.proc_terminate.signal = 0;
216 ev.proc_terminate.exitcode =
217 WEXITSTATUS(kevp->data);
223 ev.error = cloudabi_convert_errno(kevp->data);
227 /* TODO(ed): Copy out multiple entries at once. */
228 error = copyout(&ev, args->out++, sizeof(ev));
236 cloudabi32_sys_poll(struct thread *td, struct cloudabi32_sys_poll_args *uap)
238 struct cloudabi32_kevent_args args = {
243 struct kevent_copyops copyops = {
244 .k_copyin = cloudabi32_kevent_copyin,
245 .k_copyout = cloudabi32_kevent_copyout,
250 * Bandaid to support CloudABI futex constructs that are not
251 * implemented through FreeBSD's kqueue().
253 if (uap->nsubscriptions == 1) {
254 cloudabi32_subscription_t sub;
255 cloudabi32_event_t ev = {};
258 error = copyin(uap->in, &sub, sizeof(sub));
261 ev.userdata = sub.userdata;
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,
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,
293 td->td_retval[0] = 1;
294 return (copyout(&ev, uap->out, sizeof(ev)));
296 } else if (uap->nsubscriptions == 2) {
297 cloudabi32_subscription_t sub[2];
298 cloudabi32_event_t ev[2] = {};
301 error = copyin(uap->in, &sub, sizeof(sub));
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,
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,
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,
364 ev[0].error = cloudabi_convert_errno(error);
365 td->td_retval[0] = 1;
366 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
370 return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops));
374 cloudabi32_sys_poll_fd(struct thread *td,
375 struct cloudabi32_sys_poll_fd_args *uap)
377 struct cloudabi32_kevent_args args = {
382 struct kevent_copyops copyops = {
383 .k_copyin = cloudabi32_kevent_copyin,
384 .k_copyout = cloudabi32_kevent_copyout,
387 cloudabi32_subscription_t subtimo;
388 struct timespec timeout;
391 if (uap->timeout != NULL) {
392 /* Poll with a timeout. */
393 error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
396 if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
397 subtimo.clock.flags != 0)
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));
404 /* Poll without a timeout. */
405 return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len,