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: stable/11/sys/compat/cloudabi64/cloudabi64_poll.c 328127 2018-01-18 13:43:09Z ed $");
28
29#include <sys/param.h>
30#include <sys/proc.h>
31#include <sys/syscallsubr.h>
32
33#include <contrib/cloudabi/cloudabi64_types.h>
34
35#include <compat/cloudabi/cloudabi_util.h>
36
37#include <compat/cloudabi64/cloudabi64_proto.h>
38#include <compat/cloudabi64/cloudabi64_util.h>
39
40/* Converts a FreeBSD signal number to a CloudABI signal number. */
41static cloudabi_signal_t
42convert_signal(int sig)
43{
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,
71	};
72
73	/* Convert unknown signals to SIGABRT. */
74	if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
75		return (SIGABRT);
76	return (signals[sig]);
77}
78
79struct cloudabi64_kevent_args {
80	const cloudabi64_subscription_t *in;
81	cloudabi_event_t *out;
82};
83
84/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
85static int
86cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
87{
88	cloudabi64_subscription_t sub;
89	struct cloudabi64_kevent_args *args;
90	cloudabi_timestamp_t ts;
91	int error;
92
93	args = arg;
94	while (count-- > 0) {
95		/* TODO(ed): Copy in multiple entries at once. */
96		error = copyin(args->in++, &sub, sizeof(sub));
97		if (error != 0)
98			return (error);
99
100		memset(kevp, 0, sizeof(*kevp));
101		kevp->udata = TO_PTR(sub.userdata);
102		switch (sub.type) {
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);
113				if (error != 0)
114					return (error);
115				ts = ts > sub.clock.timeout ? 0 :
116				    sub.clock.timeout - ts;
117			} else {
118				/* Relative timestamp. */
119				ts = sub.clock.timeout;
120			}
121			kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
122			break;
123		case CLOUDABI_EVENTTYPE_FD_READ:
124			kevp->filter = EVFILT_READ;
125			kevp->ident = sub.fd_readwrite.fd;
126			kevp->fflags = NOTE_FILE_POLL;
127			break;
128		case CLOUDABI_EVENTTYPE_FD_WRITE:
129			kevp->filter = EVFILT_WRITE;
130			kevp->ident = sub.fd_readwrite.fd;
131			break;
132		case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
133			kevp->filter = EVFILT_PROCDESC;
134			kevp->ident = sub.proc_terminate.fd;
135			kevp->fflags = NOTE_EXIT;
136			break;
137		}
138		kevp->flags = EV_ADD | EV_ONESHOT;
139		++kevp;
140	}
141	return (0);
142}
143
144/* Converts FreeBSD's struct kevent to CloudABI's event objects. */
145static int
146cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
147{
148	cloudabi_event_t ev;
149	struct cloudabi64_kevent_args *args;
150	int error;
151
152	args = arg;
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) {
158		case EVFILT_TIMER:
159			ev.type = CLOUDABI_EVENTTYPE_CLOCK;
160			break;
161		case EVFILT_READ:
162			ev.type = CLOUDABI_EVENTTYPE_FD_READ;
163			break;
164		case EVFILT_WRITE:
165			ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
166			break;
167		case EVFILT_PROCDESC:
168			ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
169			break;
170		}
171
172		if ((kevp->flags & EV_ERROR) == 0) {
173			/* Success. */
174			switch (kevp->filter) {
175			case EVFILT_READ:
176			case EVFILT_WRITE:
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;
181				}
182				break;
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;
189				} else {
190					/* Process exited. */
191					ev.proc_terminate.signal = 0;
192					ev.proc_terminate.exitcode =
193					    WEXITSTATUS(kevp->data);
194				}
195				break;
196			}
197		} else {
198			/* Error. */
199			ev.error = cloudabi_convert_errno(kevp->data);
200		}
201		++kevp;
202
203		/* TODO(ed): Copy out multiple entries at once. */
204		error = copyout(&ev, args->out++, sizeof(ev));
205		if (error != 0)
206			return (error);
207	}
208	return (0);
209}
210
211int
212cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
213{
214	struct cloudabi64_kevent_args args = {
215		.in	= uap->in,
216		.out	= uap->out,
217	};
218	struct kevent_copyops copyops = {
219		.k_copyin	= cloudabi64_kevent_copyin,
220		.k_copyout	= cloudabi64_kevent_copyout,
221		.arg		= &args,
222	};
223
224	/*
225	 * Bandaid to support CloudABI futex constructs that are not
226	 * implemented through FreeBSD's kqueue().
227	 */
228	if (uap->nsubscriptions == 1) {
229		cloudabi64_subscription_t sub;
230		cloudabi_event_t ev = {};
231		int error;
232
233		error = copyin(uap->in, &sub, sizeof(sub));
234		if (error != 0)
235			return (error);
236		ev.userdata = sub.userdata;
237		ev.type = sub.type;
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)));
267		}
268	} else if (uap->nsubscriptions == 2) {
269		cloudabi64_subscription_t sub[2];
270		cloudabi_event_t ev[2] = {};
271		int error;
272
273		error = copyin(uap->in, &sub, sizeof(sub));
274		if (error != 0)
275			return (error);
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,
294				    sizeof(ev[1])));
295			}
296
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,
312				    sizeof(ev[1])));
313			}
314
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,
330				    sizeof(ev[1])));
331			}
332
333			ev[0].error = cloudabi_convert_errno(error);
334			td->td_retval[0] = 1;
335			return (copyout(&ev[0], uap->out, sizeof(ev[0])));
336		}
337	}
338
339	return (kern_kevent_anonymous(td, uap->nsubscriptions, &copyops));
340}
341