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$");
28
29 #include <sys/param.h>
30 #include <sys/proc.h>
31 #include <sys/syscallsubr.h>
32
33 #include <compat/cloudabi/cloudabi_util.h>
34
35 #include <compat/cloudabi64/cloudabi64_syscalldefs.h>
36 #include <compat/cloudabi64/cloudabi64_proto.h>
37
38 /* Converts a FreeBSD signal number to a CloudABI signal number. */
39 static cloudabi_signal_t
convert_signal(int sig)40 convert_signal(int sig)
41 {
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,
69 };
70
71 /* Convert unknown signals to SIGABRT. */
72 if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
73 return (SIGABRT);
74 return (signals[sig]);
75 }
76
77 struct cloudabi64_kevent_args {
78 const cloudabi64_subscription_t *in;
79 cloudabi64_event_t *out;
80 bool once;
81 };
82
83 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
84 static int
cloudabi64_kevent_copyin(void * arg,void * _kevp,int count)85 cloudabi64_kevent_copyin(void *arg, void *_kevp, int count)
86 {
87 cloudabi64_subscription_t sub;
88 struct cloudabi64_kevent_args *args;
89 cloudabi_timestamp_t ts;
90 struct kevent *kevp;
91 int error;
92
93 kevp = _kevp;
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 = (void *)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. */
165 static int
cloudabi64_kevent_copyout(void * arg,void * _kevp,int count)166 cloudabi64_kevent_copyout(void *arg, void *_kevp, int count)
167 {
168 cloudabi64_event_t ev;
169 struct cloudabi64_kevent_args *args;
170 struct kevent *kevp;
171 int error;
172
173 kevp = _kevp;
174 args = arg;
175 while (count-- > 0) {
176 /* Convert fields that should always be present. */
177 memset(&ev, 0, sizeof(ev));
178 ev.userdata = (uintptr_t)kevp->udata;
179 switch (kevp->filter) {
180 case EVFILT_TIMER:
181 ev.type = CLOUDABI_EVENTTYPE_CLOCK;
182 ev.clock.identifier = kevp->ident;
183 break;
184 case EVFILT_READ:
185 ev.type = CLOUDABI_EVENTTYPE_FD_READ;
186 ev.fd_readwrite.fd = kevp->ident;
187 break;
188 case EVFILT_WRITE:
189 ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
190 ev.fd_readwrite.fd = kevp->ident;
191 break;
192 case EVFILT_PROCDESC:
193 ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
194 ev.proc_terminate.fd = kevp->ident;
195 break;
196 }
197
198 if ((kevp->flags & EV_ERROR) == 0) {
199 /* Success. */
200 switch (kevp->filter) {
201 case EVFILT_READ:
202 case EVFILT_WRITE:
203 ev.fd_readwrite.nbytes = kevp->data;
204 if ((kevp->flags & EV_EOF) != 0) {
205 ev.fd_readwrite.flags |=
206 CLOUDABI_EVENT_FD_READWRITE_HANGUP;
207 }
208 break;
209 case EVFILT_PROCDESC:
210 if (WIFSIGNALED(kevp->data)) {
211 /* Process got signalled. */
212 ev.proc_terminate.signal =
213 convert_signal(WTERMSIG(kevp->data));
214 ev.proc_terminate.exitcode = 0;
215 } else {
216 /* Process exited. */
217 ev.proc_terminate.signal = 0;
218 ev.proc_terminate.exitcode =
219 WEXITSTATUS(kevp->data);
220 }
221 break;
222 }
223 } else {
224 /* Error. */
225 ev.error = cloudabi_convert_errno(kevp->data);
226 }
227 ++kevp;
228
229 /* TODO(ed): Copy out multiple entries at once. */
230 error = copyout(&ev, args->out++, sizeof(ev));
231 if (error != 0)
232 return (error);
233 }
234 return (0);
235 }
236
237 int
cloudabi64_sys_poll(struct thread * td,struct cloudabi64_sys_poll_args * uap)238 cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
239 {
240 struct cloudabi64_kevent_args args = {
241 .in = uap->in,
242 .out = uap->out,
243 .once = true,
244 };
245 struct kevent_copyops copyops = {
246 .k_copyin = cloudabi64_kevent_copyin,
247 .k_copyout = cloudabi64_kevent_copyout,
248 .arg = &args,
249 };
250
251 /*
252 * Bandaid to support CloudABI futex constructs that are not
253 * implemented through FreeBSD's kqueue().
254 */
255 if (uap->nevents == 1) {
256 cloudabi64_subscription_t sub;
257 cloudabi64_event_t ev = {};
258 int error;
259
260 error = copyin(uap->in, &sub, sizeof(sub));
261 if (error != 0)
262 return (error);
263 ev.userdata = sub.userdata;
264 ev.type = sub.type;
265 if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) {
266 /* Wait on a condition variable. */
267 ev.condvar.condvar = sub.condvar.condvar;
268 ev.error = cloudabi_convert_errno(
269 cloudabi_futex_condvar_wait(
270 td, (cloudabi_condvar_t *)sub.condvar.condvar,
271 sub.condvar.condvar_scope,
272 (cloudabi_lock_t *)sub.condvar.lock,
273 sub.condvar.lock_scope,
274 CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0));
275 td->td_retval[0] = 1;
276 return (copyout(&ev, uap->out, sizeof(ev)));
277 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) {
278 /* Acquire a read lock. */
279 ev.lock.lock = sub.lock.lock;
280 ev.error = cloudabi_convert_errno(
281 cloudabi_futex_lock_rdlock(
282 td, (cloudabi_lock_t *)sub.lock.lock,
283 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
284 UINT64_MAX, 0));
285 td->td_retval[0] = 1;
286 return (copyout(&ev, uap->out, sizeof(ev)));
287 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) {
288 /* Acquire a write lock. */
289 ev.lock.lock = sub.lock.lock;
290 ev.error = cloudabi_convert_errno(
291 cloudabi_futex_lock_wrlock(
292 td, (cloudabi_lock_t *)sub.lock.lock,
293 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
294 UINT64_MAX, 0));
295 td->td_retval[0] = 1;
296 return (copyout(&ev, uap->out, sizeof(ev)));
297 }
298 } else if (uap->nevents == 2) {
299 cloudabi64_subscription_t sub[2];
300 cloudabi64_event_t ev[2] = {};
301 int error;
302
303 error = copyin(uap->in, &sub, sizeof(sub));
304 if (error != 0)
305 return (error);
306 ev[0].userdata = sub[0].userdata;
307 ev[0].type = sub[0].type;
308 ev[1].userdata = sub[1].userdata;
309 ev[1].type = sub[1].type;
310 if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR &&
311 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
312 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
313 /* Wait for a condition variable with timeout. */
314 ev[0].condvar.condvar = sub[0].condvar.condvar;
315 ev[1].clock.identifier = sub[1].clock.identifier;
316 error = cloudabi_futex_condvar_wait(
317 td, (cloudabi_condvar_t *)sub[0].condvar.condvar,
318 sub[0].condvar.condvar_scope,
319 (cloudabi_lock_t *)sub[0].condvar.lock,
320 sub[0].condvar.lock_scope, sub[1].clock.clock_id,
321 sub[1].clock.timeout, sub[1].clock.precision);
322 if (error == ETIMEDOUT) {
323 td->td_retval[0] = 1;
324 return (copyout(&ev[1], uap->out,
325 sizeof(ev[1])));
326 }
327
328 ev[0].error = cloudabi_convert_errno(error);
329 td->td_retval[0] = 1;
330 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
331 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK &&
332 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
333 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
334 /* Acquire a read lock with a timeout. */
335 ev[0].lock.lock = sub[0].lock.lock;
336 ev[1].clock.identifier = sub[1].clock.identifier;
337 error = cloudabi_futex_lock_rdlock(
338 td, (cloudabi_lock_t *)sub[0].lock.lock,
339 sub[0].lock.lock_scope, sub[1].clock.clock_id,
340 sub[1].clock.timeout, sub[1].clock.precision);
341 if (error == ETIMEDOUT) {
342 td->td_retval[0] = 1;
343 return (copyout(&ev[1], uap->out,
344 sizeof(ev[1])));
345 }
346
347 ev[0].error = cloudabi_convert_errno(error);
348 td->td_retval[0] = 1;
349 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
350 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK &&
351 sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
352 sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
353 /* Acquire a write lock with a timeout. */
354 ev[0].lock.lock = sub[0].lock.lock;
355 ev[1].clock.identifier = sub[1].clock.identifier;
356 error = cloudabi_futex_lock_wrlock(
357 td, (cloudabi_lock_t *)sub[0].lock.lock,
358 sub[0].lock.lock_scope, sub[1].clock.clock_id,
359 sub[1].clock.timeout, sub[1].clock.precision);
360 if (error == ETIMEDOUT) {
361 td->td_retval[0] = 1;
362 return (copyout(&ev[1], uap->out,
363 sizeof(ev[1])));
364 }
365
366 ev[0].error = cloudabi_convert_errno(error);
367 td->td_retval[0] = 1;
368 return (copyout(&ev[0], uap->out, sizeof(ev[0])));
369 }
370 }
371
372 return (kern_kevent_anonymous(td, uap->nevents, ©ops));
373 }
374
375 int
cloudabi64_sys_poll_fd(struct thread * td,struct cloudabi64_sys_poll_fd_args * uap)376 cloudabi64_sys_poll_fd(struct thread *td,
377 struct cloudabi64_sys_poll_fd_args *uap)
378 {
379 struct cloudabi64_kevent_args args = {
380 .in = uap->in,
381 .out = uap->out,
382 .once = false,
383 };
384 struct kevent_copyops copyops = {
385 .k_copyin = cloudabi64_kevent_copyin,
386 .k_copyout = cloudabi64_kevent_copyout,
387 .arg = &args,
388 };
389 cloudabi64_subscription_t subtimo;
390 struct timespec timeout;
391 int error;
392
393 if (uap->timeout != NULL) {
394 /* Poll with a timeout. */
395 error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
396 if (error != 0)
397 return (error);
398 if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
399 subtimo.clock.flags != 0)
400 return (EINVAL);
401 timeout.tv_sec = subtimo.clock.timeout / 1000000000;
402 timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
403 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops,
404 &timeout));
405 } else {
406 /* Poll without a timeout. */
407 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops,
408 NULL));
409 }
410 }
411