xref: /NextBSD/sys/compat/cloudabi64/cloudabi64_poll.c (revision c21ffb8d6aca32c9584cfa072f309a5890a21aea)
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, &copyops));
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, &copyops,
404 		    &timeout));
405 	} else {
406 		/* Poll without a timeout. */
407 		return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
408 		    NULL));
409 	}
410 }
411