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 <compat/cloudabi/cloudabi_util.h>
35 #include <compat/cloudabi64/cloudabi64_syscalldefs.h>
36 #include <compat/cloudabi64/cloudabi64_proto.h>
38 /* Converts a FreeBSD signal number to a CloudABI signal number. */
39 static cloudabi_signal_t
40 convert_signal(int sig)
42 static const cloudabi_signal_t signals[] = {
43 [SIGABRT] = CLOUDABI_SIGABRT,
44 [SIGALRM] = CLOUDABI_SIGALRM,
45 [SIGBUS] = CLOUDABI_SIGBUS,
46 [SIGCHLD] = CLOUDABI_SIGCHLD,
47 [SIGCONT] = CLOUDABI_SIGCONT,
48 [SIGFPE] = CLOUDABI_SIGFPE,
49 [SIGHUP] = CLOUDABI_SIGHUP,
50 [SIGILL] = CLOUDABI_SIGILL,
51 [SIGINT] = CLOUDABI_SIGINT,
52 [SIGKILL] = CLOUDABI_SIGKILL,
53 [SIGPIPE] = CLOUDABI_SIGPIPE,
54 [SIGQUIT] = CLOUDABI_SIGQUIT,
55 [SIGSEGV] = CLOUDABI_SIGSEGV,
56 [SIGSTOP] = CLOUDABI_SIGSTOP,
57 [SIGSYS] = CLOUDABI_SIGSYS,
58 [SIGTERM] = CLOUDABI_SIGTERM,
59 [SIGTRAP] = CLOUDABI_SIGTRAP,
60 [SIGTSTP] = CLOUDABI_SIGTSTP,
61 [SIGTTIN] = CLOUDABI_SIGTTIN,
62 [SIGTTOU] = CLOUDABI_SIGTTOU,
63 [SIGURG] = CLOUDABI_SIGURG,
64 [SIGUSR1] = CLOUDABI_SIGUSR1,
65 [SIGUSR2] = CLOUDABI_SIGUSR2,
66 [SIGVTALRM] = CLOUDABI_SIGVTALRM,
67 [SIGXCPU] = CLOUDABI_SIGXCPU,
68 [SIGXFSZ] = CLOUDABI_SIGXFSZ,
71 /* Convert unknown signals to SIGABRT. */
72 if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
74 return (signals[sig]);
77 struct cloudabi64_kevent_args {
78 const cloudabi64_subscription_t *in;
79 cloudabi64_event_t *out;
83 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
85 cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
87 cloudabi64_subscription_t sub;
88 struct cloudabi64_kevent_args *args;
89 cloudabi_timestamp_t ts;
94 /* TODO(ed): Copy in multiple entries at once. */
95 error = copyin(args->in++, &sub, sizeof(sub));
99 memset(kevp, 0, sizeof(*kevp));
100 kevp->udata = (void *)sub.userdata;
102 case CLOUDABI_EVENTTYPE_CLOCK:
103 kevp->filter = EVFILT_TIMER;
104 kevp->ident = sub.clock.identifier;
105 kevp->fflags = NOTE_NSECONDS;
106 if ((sub.clock.flags &
107 CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 &&
108 sub.clock.timeout > 0) {
109 /* Convert absolute timestamp to a relative. */
110 error = cloudabi_clock_time_get(curthread,
111 sub.clock.clock_id, &ts);
114 ts = ts > sub.clock.timeout ? 0 :
115 sub.clock.timeout - ts;
117 /* Relative timestamp. */
118 ts = sub.clock.timeout;
120 kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
122 case CLOUDABI_EVENTTYPE_FD_READ:
123 kevp->filter = EVFILT_READ;
124 kevp->ident = sub.fd_readwrite.fd;
125 if ((sub.fd_readwrite.flags &
126 CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0)
127 kevp->fflags = NOTE_FILE_POLL;
129 case CLOUDABI_EVENTTYPE_FD_WRITE:
130 kevp->filter = EVFILT_WRITE;
131 kevp->ident = sub.fd_readwrite.fd;
133 case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
134 kevp->filter = EVFILT_PROCDESC;
135 kevp->ident = sub.proc_terminate.fd;
136 kevp->fflags = NOTE_EXIT;
140 /* Ignore flags. Simply use oneshot mode. */
141 kevp->flags = EV_ADD | EV_ONESHOT;
143 /* Translate flags. */
144 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0)
145 kevp->flags |= EV_ADD;
146 if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0)
147 kevp->flags |= EV_CLEAR;
148 if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0)
149 kevp->flags |= EV_DELETE;
150 if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0)
151 kevp->flags |= EV_DISABLE;
152 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0)
153 kevp->flags |= EV_ENABLE;
154 if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0)
155 kevp->flags |= EV_ONESHOT;
162 /* Converts FreeBSD's struct kevent to CloudABI's event objects. */
164 cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
166 cloudabi64_event_t ev;
167 struct cloudabi64_kevent_args *args;
171 while (count-- > 0) {
172 /* Convert fields that should always be present. */
173 memset(&ev, 0, sizeof(ev));
174 ev.userdata = (uintptr_t)kevp->udata;
175 switch (kevp->filter) {
177 ev.type = CLOUDABI_EVENTTYPE_CLOCK;
178 ev.clock.identifier = kevp->ident;
181 ev.type = CLOUDABI_EVENTTYPE_FD_READ;
182 ev.fd_readwrite.fd = kevp->ident;
185 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
186 ev.fd_readwrite.fd = kevp->ident;
188 case EVFILT_PROCDESC:
189 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
190 ev.proc_terminate.fd = kevp->ident;
194 if ((kevp->flags & EV_ERROR) == 0) {
196 switch (kevp->filter) {
199 ev.fd_readwrite.nbytes = kevp->data;
200 if ((kevp->flags & EV_EOF) != 0) {
201 ev.fd_readwrite.flags |=
202 CLOUDABI_EVENT_FD_READWRITE_HANGUP;
205 case EVFILT_PROCDESC:
206 if (WIFSIGNALED(kevp->data)) {
207 /* Process got signalled. */
208 ev.proc_terminate.signal =
209 convert_signal(WTERMSIG(kevp->data));
210 ev.proc_terminate.exitcode = 0;
212 /* Process exited. */
213 ev.proc_terminate.signal = 0;
214 ev.proc_terminate.exitcode =
215 WEXITSTATUS(kevp->data);
221 ev.error = cloudabi_convert_errno(kevp->data);
225 /* TODO(ed): Copy out multiple entries at once. */
226 error = copyout(&ev, args->out++, sizeof(ev));
234 cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
236 struct cloudabi64_kevent_args args = {
241 struct kevent_copyops copyops = {
242 .k_copyin = cloudabi64_kevent_copyin,
243 .k_copyout = cloudabi64_kevent_copyout,
248 * Bandaid to support CloudABI futex constructs that are not
249 * implemented through FreeBSD's kqueue().
251 if (uap->nevents == 1) {
252 cloudabi64_subscription_t sub;
253 cloudabi64_event_t ev = {};
256 error = copyin(uap->in, &sub, sizeof(sub));
259 ev.userdata = sub.userdata;
261 if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) {
262 /* Wait on a condition variable. */
263 ev.condvar.condvar = sub.condvar.condvar;
264 ev.error = cloudabi_convert_errno(
265 cloudabi_futex_condvar_wait(
266 td, (cloudabi_condvar_t *)sub.condvar.condvar,
267 sub.condvar.condvar_scope,
268 (cloudabi_lock_t *)sub.condvar.lock,
269 sub.condvar.lock_scope,
270 CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0));
271 td->td_retval[0] = 1;
272 return (copyout(&ev, uap->out, sizeof(ev)));
273 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) {
274 /* Acquire a read lock. */
275 ev.lock.lock = sub.lock.lock;
276 ev.error = cloudabi_convert_errno(
277 cloudabi_futex_lock_rdlock(
278 td, (cloudabi_lock_t *)sub.lock.lock,
279 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
281 td->td_retval[0] = 1;
282 return (copyout(&ev, uap->out, sizeof(ev)));
283 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) {
284 /* Acquire a write lock. */
285 ev.lock.lock = sub.lock.lock;
286 ev.error = cloudabi_convert_errno(
287 cloudabi_futex_lock_wrlock(
288 td, (cloudabi_lock_t *)sub.lock.lock,
289 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
291 td->td_retval[0] = 1;
292 return (copyout(&ev, uap->out, sizeof(ev)));
294 } else if (uap->nevents == 2) {
295 cloudabi64_subscription_t sub[2];
296 cloudabi64_event_t ev[2] = {};
299 error = copyin(uap->in, &sub, sizeof(sub));
302 ev[0].userdata = sub[0].userdata;
303 ev[0].type = sub[0].type;
304 ev[1].userdata = sub[1].userdata;
305 ev[1].type = sub[1].type;
306 if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR &&
307 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
308 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
309 /* Wait for a condition variable with timeout. */
310 ev[0].condvar.condvar = sub[0].condvar.condvar;
311 ev[1].clock.identifier = sub[1].clock.identifier;
312 error = cloudabi_futex_condvar_wait(
313 td, (cloudabi_condvar_t *)sub[0].condvar.condvar,
314 sub[0].condvar.condvar_scope,
315 (cloudabi_lock_t *)sub[0].condvar.lock,
316 sub[0].condvar.lock_scope, sub[1].clock.clock_id,
317 sub[1].clock.timeout, sub[1].clock.precision);
318 if (error == ETIMEDOUT) {
319 td->td_retval[0] = 1;
320 return (copyout(&ev[1], uap->out,
324 ev[0].error = cloudabi_convert_errno(error);
325 td->td_retval[0] = 1;
326 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
327 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK &&
328 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
329 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
330 /* Acquire a read lock with a timeout. */
331 ev[0].lock.lock = sub[0].lock.lock;
332 ev[1].clock.identifier = sub[1].clock.identifier;
333 error = cloudabi_futex_lock_rdlock(
334 td, (cloudabi_lock_t *)sub[0].lock.lock,
335 sub[0].lock.lock_scope, sub[1].clock.clock_id,
336 sub[1].clock.timeout, sub[1].clock.precision);
337 if (error == ETIMEDOUT) {
338 td->td_retval[0] = 1;
339 return (copyout(&ev[1], uap->out,
343 ev[0].error = cloudabi_convert_errno(error);
344 td->td_retval[0] = 1;
345 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
346 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK &&
347 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
348 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
349 /* Acquire a write lock with a timeout. */
350 ev[0].lock.lock = sub[0].lock.lock;
351 ev[1].clock.identifier = sub[1].clock.identifier;
352 error = cloudabi_futex_lock_wrlock(
353 td, (cloudabi_lock_t *)sub[0].lock.lock,
354 sub[0].lock.lock_scope, sub[1].clock.clock_id,
355 sub[1].clock.timeout, sub[1].clock.precision);
356 if (error == ETIMEDOUT) {
357 td->td_retval[0] = 1;
358 return (copyout(&ev[1], uap->out,
362 ev[0].error = cloudabi_convert_errno(error);
363 td->td_retval[0] = 1;
364 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
368 return (kern_kevent_anonymous(td, uap->nevents, ©ops));
372 cloudabi64_sys_poll_fd(struct thread *td,
373 struct cloudabi64_sys_poll_fd_args *uap)
375 struct cloudabi64_kevent_args args = {
380 struct kevent_copyops copyops = {
381 .k_copyin = cloudabi64_kevent_copyin,
382 .k_copyout = cloudabi64_kevent_copyout,
385 cloudabi64_subscription_t subtimo;
386 struct timespec timeout;
389 if (uap->timeout != NULL) {
390 /* Poll with a timeout. */
391 error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
394 if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
395 subtimo.clock.flags != 0)
397 timeout.tv_sec = subtimo.clock.timeout / 1000000000;
398 timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
399 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops,
402 /* Poll without a timeout. */
403 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops,