1285307Sed/*-
2285307Sed * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3285307Sed *
4285307Sed * Redistribution and use in source and binary forms, with or without
5285307Sed * modification, are permitted provided that the following conditions
6285307Sed * are met:
7285307Sed * 1. Redistributions of source code must retain the above copyright
8285307Sed *    notice, this list of conditions and the following disclaimer.
9285307Sed * 2. Redistributions in binary form must reproduce the above copyright
10285307Sed *    notice, this list of conditions and the following disclaimer in the
11285307Sed *    documentation and/or other materials provided with the distribution.
12285307Sed *
13285307Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14285307Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15285307Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16285307Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17285307Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18285307Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19285307Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20285307Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21285307Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22285307Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23285307Sed * SUCH DAMAGE.
24285307Sed */
25285307Sed
26285307Sed#include <sys/cdefs.h>
27285307Sed__FBSDID("$FreeBSD: releng/11.0/sys/compat/cloudabi64/cloudabi64_poll.c 297247 2016-03-24 21:47:15Z ed $");
28285307Sed
29286654Sed#include <sys/param.h>
30286654Sed#include <sys/proc.h>
31286654Sed#include <sys/syscallsubr.h>
32286654Sed
33297247Sed#include <contrib/cloudabi/cloudabi64_types.h>
34297247Sed
35286654Sed#include <compat/cloudabi/cloudabi_util.h>
36286654Sed
37285307Sed#include <compat/cloudabi64/cloudabi64_proto.h>
38285307Sed
39286654Sed/* Converts a FreeBSD signal number to a CloudABI signal number. */
40286654Sedstatic cloudabi_signal_t
41286654Sedconvert_signal(int sig)
42286654Sed{
43286654Sed	static const cloudabi_signal_t signals[] = {
44286654Sed		[SIGABRT]	= CLOUDABI_SIGABRT,
45286654Sed		[SIGALRM]	= CLOUDABI_SIGALRM,
46286654Sed		[SIGBUS]	= CLOUDABI_SIGBUS,
47286654Sed		[SIGCHLD]	= CLOUDABI_SIGCHLD,
48286654Sed		[SIGCONT]	= CLOUDABI_SIGCONT,
49286654Sed		[SIGFPE]	= CLOUDABI_SIGFPE,
50286654Sed		[SIGHUP]	= CLOUDABI_SIGHUP,
51286654Sed		[SIGILL]	= CLOUDABI_SIGILL,
52286654Sed		[SIGINT]	= CLOUDABI_SIGINT,
53286654Sed		[SIGKILL]	= CLOUDABI_SIGKILL,
54286654Sed		[SIGPIPE]	= CLOUDABI_SIGPIPE,
55286654Sed		[SIGQUIT]	= CLOUDABI_SIGQUIT,
56286654Sed		[SIGSEGV]	= CLOUDABI_SIGSEGV,
57286654Sed		[SIGSTOP]	= CLOUDABI_SIGSTOP,
58286654Sed		[SIGSYS]	= CLOUDABI_SIGSYS,
59286654Sed		[SIGTERM]	= CLOUDABI_SIGTERM,
60286654Sed		[SIGTRAP]	= CLOUDABI_SIGTRAP,
61286654Sed		[SIGTSTP]	= CLOUDABI_SIGTSTP,
62286654Sed		[SIGTTIN]	= CLOUDABI_SIGTTIN,
63286654Sed		[SIGTTOU]	= CLOUDABI_SIGTTOU,
64286654Sed		[SIGURG]	= CLOUDABI_SIGURG,
65286654Sed		[SIGUSR1]	= CLOUDABI_SIGUSR1,
66286654Sed		[SIGUSR2]	= CLOUDABI_SIGUSR2,
67286654Sed		[SIGVTALRM]	= CLOUDABI_SIGVTALRM,
68286654Sed		[SIGXCPU]	= CLOUDABI_SIGXCPU,
69286654Sed		[SIGXFSZ]	= CLOUDABI_SIGXFSZ,
70286654Sed	};
71286654Sed
72286654Sed	/* Convert unknown signals to SIGABRT. */
73286654Sed	if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
74286654Sed		return (SIGABRT);
75286654Sed	return (signals[sig]);
76286654Sed}
77286654Sed
78286654Sedstruct cloudabi64_kevent_args {
79286654Sed	const cloudabi64_subscription_t *in;
80286654Sed	cloudabi64_event_t *out;
81286654Sed	bool once;
82286654Sed};
83286654Sed
84286654Sed/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
85286654Sedstatic int
86286654Sedcloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
87286654Sed{
88286654Sed	cloudabi64_subscription_t sub;
89286654Sed	struct cloudabi64_kevent_args *args;
90286654Sed	cloudabi_timestamp_t ts;
91286654Sed	int error;
92286654Sed
93286654Sed	args = arg;
94286654Sed	while (count-- > 0) {
95286654Sed		/* TODO(ed): Copy in multiple entries at once. */
96286654Sed		error = copyin(args->in++, &sub, sizeof(sub));
97286654Sed		if (error != 0)
98286654Sed			return (error);
99286654Sed
100286654Sed		memset(kevp, 0, sizeof(*kevp));
101286654Sed		kevp->udata = (void *)sub.userdata;
102286654Sed		switch (sub.type) {
103286654Sed		case CLOUDABI_EVENTTYPE_CLOCK:
104286654Sed			kevp->filter = EVFILT_TIMER;
105286654Sed			kevp->ident = sub.clock.identifier;
106286654Sed			kevp->fflags = NOTE_NSECONDS;
107286654Sed			if ((sub.clock.flags &
108286654Sed			    CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 &&
109286654Sed			    sub.clock.timeout > 0) {
110286654Sed				/* Convert absolute timestamp to a relative. */
111286654Sed				error = cloudabi_clock_time_get(curthread,
112286654Sed				    sub.clock.clock_id, &ts);
113286654Sed				if (error != 0)
114286654Sed					return (error);
115286654Sed				ts = ts > sub.clock.timeout ? 0 :
116286654Sed				    sub.clock.timeout - ts;
117286654Sed			} else {
118286654Sed				/* Relative timestamp. */
119286654Sed				ts = sub.clock.timeout;
120286654Sed			}
121286654Sed			kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
122286654Sed			break;
123286654Sed		case CLOUDABI_EVENTTYPE_FD_READ:
124286654Sed			kevp->filter = EVFILT_READ;
125286654Sed			kevp->ident = sub.fd_readwrite.fd;
126286654Sed			if ((sub.fd_readwrite.flags &
127286654Sed			    CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0)
128286654Sed				kevp->fflags = NOTE_FILE_POLL;
129286654Sed			break;
130286654Sed		case CLOUDABI_EVENTTYPE_FD_WRITE:
131286654Sed			kevp->filter = EVFILT_WRITE;
132286654Sed			kevp->ident = sub.fd_readwrite.fd;
133286654Sed			break;
134286654Sed		case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
135286654Sed			kevp->filter = EVFILT_PROCDESC;
136286654Sed			kevp->ident = sub.proc_terminate.fd;
137286654Sed			kevp->fflags = NOTE_EXIT;
138286654Sed			break;
139286654Sed		}
140286654Sed		if (args->once) {
141286654Sed			/* Ignore flags. Simply use oneshot mode. */
142286654Sed			kevp->flags = EV_ADD | EV_ONESHOT;
143286654Sed		} else {
144286654Sed			/* Translate flags. */
145286654Sed			if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0)
146286654Sed				kevp->flags |= EV_ADD;
147286654Sed			if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0)
148286654Sed				kevp->flags |= EV_CLEAR;
149286654Sed			if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0)
150286654Sed				kevp->flags |= EV_DELETE;
151286654Sed			if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0)
152286654Sed				kevp->flags |= EV_DISABLE;
153286654Sed			if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0)
154286654Sed				kevp->flags |= EV_ENABLE;
155286654Sed			if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0)
156286654Sed				kevp->flags |= EV_ONESHOT;
157286654Sed		}
158286654Sed		++kevp;
159286654Sed	}
160286654Sed	return (0);
161286654Sed}
162286654Sed
163286654Sed/* Converts FreeBSD's struct kevent to CloudABI's event objects. */
164286654Sedstatic int
165286654Sedcloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
166286654Sed{
167286654Sed	cloudabi64_event_t ev;
168286654Sed	struct cloudabi64_kevent_args *args;
169286654Sed	int error;
170286654Sed
171286654Sed	args = arg;
172286654Sed	while (count-- > 0) {
173286654Sed		/* Convert fields that should always be present. */
174286654Sed		memset(&ev, 0, sizeof(ev));
175286654Sed		ev.userdata = (uintptr_t)kevp->udata;
176286654Sed		switch (kevp->filter) {
177286654Sed		case EVFILT_TIMER:
178286654Sed			ev.type = CLOUDABI_EVENTTYPE_CLOCK;
179286654Sed			ev.clock.identifier = kevp->ident;
180286654Sed			break;
181286654Sed		case EVFILT_READ:
182286654Sed			ev.type = CLOUDABI_EVENTTYPE_FD_READ;
183286654Sed			ev.fd_readwrite.fd = kevp->ident;
184286654Sed			break;
185286654Sed		case EVFILT_WRITE:
186286654Sed			ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
187286654Sed			ev.fd_readwrite.fd = kevp->ident;
188286654Sed			break;
189286654Sed		case EVFILT_PROCDESC:
190286654Sed			ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
191286654Sed			ev.proc_terminate.fd = kevp->ident;
192286654Sed			break;
193286654Sed		}
194286654Sed
195286654Sed		if ((kevp->flags & EV_ERROR) == 0) {
196286654Sed			/* Success. */
197286654Sed			switch (kevp->filter) {
198286654Sed			case EVFILT_READ:
199286654Sed			case EVFILT_WRITE:
200286654Sed				ev.fd_readwrite.nbytes = kevp->data;
201286654Sed				if ((kevp->flags & EV_EOF) != 0) {
202286654Sed					ev.fd_readwrite.flags |=
203286654Sed					    CLOUDABI_EVENT_FD_READWRITE_HANGUP;
204286654Sed				}
205286654Sed				break;
206286654Sed			case EVFILT_PROCDESC:
207286654Sed				if (WIFSIGNALED(kevp->data)) {
208286654Sed					/* Process got signalled. */
209286654Sed					ev.proc_terminate.signal =
210286654Sed					   convert_signal(WTERMSIG(kevp->data));
211286654Sed					ev.proc_terminate.exitcode = 0;
212286654Sed				} else {
213286654Sed					/* Process exited. */
214286654Sed					ev.proc_terminate.signal = 0;
215286654Sed					ev.proc_terminate.exitcode =
216286654Sed					    WEXITSTATUS(kevp->data);
217286654Sed				}
218286654Sed				break;
219286654Sed			}
220286654Sed		} else {
221286654Sed			/* Error. */
222286654Sed			ev.error = cloudabi_convert_errno(kevp->data);
223286654Sed		}
224286654Sed		++kevp;
225286654Sed
226286654Sed		/* TODO(ed): Copy out multiple entries at once. */
227286654Sed		error = copyout(&ev, args->out++, sizeof(ev));
228286654Sed		if (error != 0)
229286654Sed			return (error);
230286654Sed	}
231286654Sed	return (0);
232286654Sed}
233286654Sed
234285307Sedint
235285307Sedcloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
236285307Sed{
237286654Sed	struct cloudabi64_kevent_args args = {
238286654Sed		.in	= uap->in,
239286654Sed		.out	= uap->out,
240286654Sed		.once	= true,
241286654Sed	};
242286654Sed	struct kevent_copyops copyops = {
243286654Sed		.k_copyin	= cloudabi64_kevent_copyin,
244286654Sed		.k_copyout	= cloudabi64_kevent_copyout,
245286654Sed		.arg		= &args,
246286654Sed	};
247285307Sed
248286656Sed	/*
249286656Sed	 * Bandaid to support CloudABI futex constructs that are not
250286656Sed	 * implemented through FreeBSD's kqueue().
251286656Sed	 */
252297247Sed	if (uap->nsubscriptions == 1) {
253286656Sed		cloudabi64_subscription_t sub;
254286656Sed		cloudabi64_event_t ev = {};
255286656Sed		int error;
256286656Sed
257286656Sed		error = copyin(uap->in, &sub, sizeof(sub));
258286656Sed		if (error != 0)
259286656Sed			return (error);
260286656Sed		ev.userdata = sub.userdata;
261286656Sed		ev.type = sub.type;
262286656Sed		if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) {
263286656Sed			/* Wait on a condition variable. */
264286656Sed			ev.condvar.condvar = sub.condvar.condvar;
265286656Sed			ev.error = cloudabi_convert_errno(
266286656Sed			    cloudabi_futex_condvar_wait(
267286656Sed			        td, (cloudabi_condvar_t *)sub.condvar.condvar,
268286656Sed			        sub.condvar.condvar_scope,
269286656Sed			        (cloudabi_lock_t *)sub.condvar.lock,
270286656Sed			        sub.condvar.lock_scope,
271286656Sed			        CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0));
272286656Sed			td->td_retval[0] = 1;
273286656Sed			return (copyout(&ev, uap->out, sizeof(ev)));
274286656Sed		} else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) {
275286656Sed			/* Acquire a read lock. */
276286656Sed			ev.lock.lock = sub.lock.lock;
277286656Sed			ev.error = cloudabi_convert_errno(
278286656Sed			    cloudabi_futex_lock_rdlock(
279286656Sed			        td, (cloudabi_lock_t *)sub.lock.lock,
280286656Sed			        sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
281286656Sed			        UINT64_MAX, 0));
282286656Sed			td->td_retval[0] = 1;
283286656Sed			return (copyout(&ev, uap->out, sizeof(ev)));
284286656Sed		} else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) {
285286656Sed			/* Acquire a write lock. */
286286656Sed			ev.lock.lock = sub.lock.lock;
287286656Sed			ev.error = cloudabi_convert_errno(
288286656Sed			    cloudabi_futex_lock_wrlock(
289286656Sed			        td, (cloudabi_lock_t *)sub.lock.lock,
290286656Sed			        sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
291286656Sed			        UINT64_MAX, 0));
292286656Sed			td->td_retval[0] = 1;
293286656Sed			return (copyout(&ev, uap->out, sizeof(ev)));
294286656Sed		}
295297247Sed	} else if (uap->nsubscriptions == 2) {
296286656Sed		cloudabi64_subscription_t sub[2];
297286656Sed		cloudabi64_event_t ev[2] = {};
298286656Sed		int error;
299286656Sed
300286656Sed		error = copyin(uap->in, &sub, sizeof(sub));
301286656Sed		if (error != 0)
302286656Sed			return (error);
303286656Sed		ev[0].userdata = sub[0].userdata;
304286656Sed		ev[0].type = sub[0].type;
305286656Sed		ev[1].userdata = sub[1].userdata;
306286656Sed		ev[1].type = sub[1].type;
307286656Sed		if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR &&
308286656Sed		    sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
309286656Sed		    sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
310286656Sed			/* Wait for a condition variable with timeout. */
311286656Sed			ev[0].condvar.condvar = sub[0].condvar.condvar;
312286656Sed			ev[1].clock.identifier = sub[1].clock.identifier;
313286656Sed			error = cloudabi_futex_condvar_wait(
314286656Sed			    td, (cloudabi_condvar_t *)sub[0].condvar.condvar,
315286656Sed			    sub[0].condvar.condvar_scope,
316286656Sed			    (cloudabi_lock_t *)sub[0].condvar.lock,
317286656Sed			    sub[0].condvar.lock_scope, sub[1].clock.clock_id,
318286656Sed			    sub[1].clock.timeout, sub[1].clock.precision);
319286656Sed			if (error == ETIMEDOUT) {
320286656Sed				td->td_retval[0] = 1;
321286656Sed				return (copyout(&ev[1], uap->out,
322286656Sed				    sizeof(ev[1])));
323286656Sed			}
324286656Sed
325286656Sed			ev[0].error = cloudabi_convert_errno(error);
326286656Sed			td->td_retval[0] = 1;
327286656Sed			return (copyout(&ev[0], uap->out, sizeof(ev[0])));
328286656Sed		} else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK &&
329286656Sed		    sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
330286656Sed		    sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
331286656Sed			/* Acquire a read lock with a timeout. */
332286656Sed			ev[0].lock.lock = sub[0].lock.lock;
333286656Sed			ev[1].clock.identifier = sub[1].clock.identifier;
334286656Sed			error = cloudabi_futex_lock_rdlock(
335286656Sed			    td, (cloudabi_lock_t *)sub[0].lock.lock,
336286656Sed			    sub[0].lock.lock_scope, sub[1].clock.clock_id,
337286656Sed			    sub[1].clock.timeout, sub[1].clock.precision);
338286656Sed			if (error == ETIMEDOUT) {
339286656Sed				td->td_retval[0] = 1;
340286656Sed				return (copyout(&ev[1], uap->out,
341286656Sed				    sizeof(ev[1])));
342286656Sed			}
343286656Sed
344286656Sed			ev[0].error = cloudabi_convert_errno(error);
345286656Sed			td->td_retval[0] = 1;
346286656Sed			return (copyout(&ev[0], uap->out, sizeof(ev[0])));
347286656Sed		} else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK &&
348286656Sed		    sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
349286656Sed		    sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
350286656Sed			/* Acquire a write lock with a timeout. */
351286656Sed			ev[0].lock.lock = sub[0].lock.lock;
352286656Sed			ev[1].clock.identifier = sub[1].clock.identifier;
353286656Sed			error = cloudabi_futex_lock_wrlock(
354286656Sed			    td, (cloudabi_lock_t *)sub[0].lock.lock,
355286656Sed			    sub[0].lock.lock_scope, sub[1].clock.clock_id,
356286656Sed			    sub[1].clock.timeout, sub[1].clock.precision);
357286656Sed			if (error == ETIMEDOUT) {
358286656Sed				td->td_retval[0] = 1;
359286656Sed				return (copyout(&ev[1], uap->out,
360286656Sed				    sizeof(ev[1])));
361286656Sed			}
362286656Sed
363286656Sed			ev[0].error = cloudabi_convert_errno(error);
364286656Sed			td->td_retval[0] = 1;
365286656Sed			return (copyout(&ev[0], uap->out, sizeof(ev[0])));
366286656Sed		}
367286656Sed	}
368286656Sed
369297247Sed	return (kern_kevent_anonymous(td, uap->nsubscriptions, &copyops));
370285307Sed}
371286318Sed
372286318Sedint
373286318Sedcloudabi64_sys_poll_fd(struct thread *td,
374286318Sed    struct cloudabi64_sys_poll_fd_args *uap)
375286318Sed{
376286654Sed	struct cloudabi64_kevent_args args = {
377286654Sed		.in	= uap->in,
378286654Sed		.out	= uap->out,
379286654Sed		.once	= false,
380286654Sed	};
381286654Sed	struct kevent_copyops copyops = {
382286654Sed		.k_copyin	= cloudabi64_kevent_copyin,
383286654Sed		.k_copyout	= cloudabi64_kevent_copyout,
384286654Sed		.arg		= &args,
385286654Sed	};
386286654Sed	cloudabi64_subscription_t subtimo;
387286654Sed	struct timespec timeout;
388286654Sed	int error;
389286318Sed
390286654Sed	if (uap->timeout != NULL) {
391286654Sed		/* Poll with a timeout. */
392286654Sed		error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
393286654Sed		if (error != 0)
394286654Sed			return (error);
395286654Sed		if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
396286654Sed		    subtimo.clock.flags != 0)
397286654Sed			return (EINVAL);
398286654Sed		timeout.tv_sec = subtimo.clock.timeout / 1000000000;
399286654Sed		timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
400286654Sed		return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
401286654Sed		    &timeout));
402286654Sed	} else {
403286654Sed		/* Poll without a timeout. */
404286654Sed		return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
405286654Sed		    NULL));
406286654Sed	}
407286318Sed}
408