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 <contrib/cloudabi/cloudabi32_types.h>
34 
35 #include <compat/cloudabi/cloudabi_util.h>
36 
37 #include <compat/cloudabi32/cloudabi32_proto.h>
38 #include <compat/cloudabi32/cloudabi32_util.h>
39 
40 /* Converts a FreeBSD signal number to a CloudABI signal number. */
41 static cloudabi_signal_t
convert_signal(int sig)42 convert_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 
79 struct cloudabi32_kevent_args {
80 	const cloudabi32_subscription_t *in;
81 	cloudabi_event_t *out;
82 };
83 
84 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
85 static int
cloudabi32_kevent_copyin(void * arg,struct kevent * kevp,int count)86 cloudabi32_kevent_copyin(void *arg, struct kevent *kevp, int count)
87 {
88 	cloudabi32_subscription_t sub;
89 	struct cloudabi32_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. */
145 static int
cloudabi32_kevent_copyout(void * arg,struct kevent * kevp,int count)146 cloudabi32_kevent_copyout(void *arg, struct kevent *kevp, int count)
147 {
148 	cloudabi_event_t ev;
149 	struct cloudabi32_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 
211 int
cloudabi32_sys_poll(struct thread * td,struct cloudabi32_sys_poll_args * uap)212 cloudabi32_sys_poll(struct thread *td, struct cloudabi32_sys_poll_args *uap)
213 {
214 	struct cloudabi32_kevent_args args = {
215 		.in	= uap->in,
216 		.out	= uap->out,
217 	};
218 	struct kevent_copyops copyops = {
219 		.k_copyin	= cloudabi32_kevent_copyin,
220 		.k_copyout	= cloudabi32_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 		cloudabi32_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 		cloudabi32_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