cloudabi64_poll.c revision 307144
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 307144 2016-10-12 12:17:41Z 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	cloudabi64_event_t *out;
82	bool once;
83};
84
85/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
86static int
87cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
88{
89	cloudabi64_subscription_t sub;
90	struct cloudabi64_kevent_args *args;
91	cloudabi_timestamp_t ts;
92	int error;
93
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 = TO_PTR(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. */
165static int
166cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
167{
168	cloudabi64_event_t ev;
169	struct cloudabi64_kevent_args *args;
170	int error;
171
172	args = arg;
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) {
178		case EVFILT_TIMER:
179			ev.type = CLOUDABI_EVENTTYPE_CLOCK;
180			ev.clock.identifier = kevp->ident;
181			break;
182		case EVFILT_READ:
183			ev.type = CLOUDABI_EVENTTYPE_FD_READ;
184			ev.fd_readwrite.fd = kevp->ident;
185			break;
186		case EVFILT_WRITE:
187			ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
188			ev.fd_readwrite.fd = kevp->ident;
189			break;
190		case EVFILT_PROCDESC:
191			ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
192			ev.proc_terminate.fd = kevp->ident;
193			break;
194		}
195
196		if ((kevp->flags & EV_ERROR) == 0) {
197			/* Success. */
198			switch (kevp->filter) {
199			case EVFILT_READ:
200			case EVFILT_WRITE:
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;
205				}
206				break;
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;
213				} else {
214					/* Process exited. */
215					ev.proc_terminate.signal = 0;
216					ev.proc_terminate.exitcode =
217					    WEXITSTATUS(kevp->data);
218				}
219				break;
220			}
221		} else {
222			/* Error. */
223			ev.error = cloudabi_convert_errno(kevp->data);
224		}
225		++kevp;
226
227		/* TODO(ed): Copy out multiple entries at once. */
228		error = copyout(&ev, args->out++, sizeof(ev));
229		if (error != 0)
230			return (error);
231	}
232	return (0);
233}
234
235int
236cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
237{
238	struct cloudabi64_kevent_args args = {
239		.in	= uap->in,
240		.out	= uap->out,
241		.once	= true,
242	};
243	struct kevent_copyops copyops = {
244		.k_copyin	= cloudabi64_kevent_copyin,
245		.k_copyout	= cloudabi64_kevent_copyout,
246		.arg		= &args,
247	};
248
249	/*
250	 * Bandaid to support CloudABI futex constructs that are not
251	 * implemented through FreeBSD's kqueue().
252	 */
253	if (uap->nsubscriptions == 1) {
254		cloudabi64_subscription_t sub;
255		cloudabi64_event_t ev = {};
256		int error;
257
258		error = copyin(uap->in, &sub, sizeof(sub));
259		if (error != 0)
260			return (error);
261		ev.userdata = sub.userdata;
262		ev.type = sub.type;
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,
282			        UINT64_MAX, 0));
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,
292			        UINT64_MAX, 0));
293			td->td_retval[0] = 1;
294			return (copyout(&ev, uap->out, sizeof(ev)));
295		}
296	} else if (uap->nsubscriptions == 2) {
297		cloudabi64_subscription_t sub[2];
298		cloudabi64_event_t ev[2] = {};
299		int error;
300
301		error = copyin(uap->in, &sub, sizeof(sub));
302		if (error != 0)
303			return (error);
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,
323				    sizeof(ev[1])));
324			}
325
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,
342				    sizeof(ev[1])));
343			}
344
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,
361				    sizeof(ev[1])));
362			}
363
364			ev[0].error = cloudabi_convert_errno(error);
365			td->td_retval[0] = 1;
366			return (copyout(&ev[0], uap->out, sizeof(ev[0])));
367		}
368	}
369
370	return (kern_kevent_anonymous(td, uap->nsubscriptions, &copyops));
371}
372
373int
374cloudabi64_sys_poll_fd(struct thread *td,
375    struct cloudabi64_sys_poll_fd_args *uap)
376{
377	struct cloudabi64_kevent_args args = {
378		.in	= uap->in,
379		.out	= uap->out,
380		.once	= false,
381	};
382	struct kevent_copyops copyops = {
383		.k_copyin	= cloudabi64_kevent_copyin,
384		.k_copyout	= cloudabi64_kevent_copyout,
385		.arg		= &args,
386	};
387	cloudabi64_subscription_t subtimo;
388	struct timespec timeout;
389	int error;
390
391	if (uap->timeout != NULL) {
392		/* Poll with a timeout. */
393		error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
394		if (error != 0)
395			return (error);
396		if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
397		    subtimo.clock.flags != 0)
398			return (EINVAL);
399		timeout.tv_sec = subtimo.clock.timeout / 1000000000;
400		timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
401		return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
402		    &timeout));
403	} else {
404		/* Poll without a timeout. */
405		return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
406		    NULL));
407	}
408}
409