]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/cloudabi32/cloudabi32_poll.c
Revert and redo r306083.
[FreeBSD/FreeBSD.git] / sys / compat / cloudabi32 / cloudabi32_poll.c
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
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         cloudabi32_event_t *out;
82         bool once;
83 };
84
85 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
86 static int
87 cloudabi32_kevent_copyin(void *arg, struct kevent *kevp, int count)
88 {
89         cloudabi32_subscription_t sub;
90         struct cloudabi32_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. */
165 static int
166 cloudabi32_kevent_copyout(void *arg, struct kevent *kevp, int count)
167 {
168         cloudabi32_event_t ev;
169         struct cloudabi32_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
235 int
236 cloudabi32_sys_poll(struct thread *td, struct cloudabi32_sys_poll_args *uap)
237 {
238         struct cloudabi32_kevent_args args = {
239                 .in     = uap->in,
240                 .out    = uap->out,
241                 .once   = true,
242         };
243         struct kevent_copyops copyops = {
244                 .k_copyin       = cloudabi32_kevent_copyin,
245                 .k_copyout      = cloudabi32_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                 cloudabi32_subscription_t sub;
255                 cloudabi32_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                 cloudabi32_subscription_t sub[2];
298                 cloudabi32_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
373 int
374 cloudabi32_sys_poll_fd(struct thread *td,
375     struct cloudabi32_sys_poll_fd_args *uap)
376 {
377         struct cloudabi32_kevent_args args = {
378                 .in     = uap->in,
379                 .out    = uap->out,
380                 .once   = false,
381         };
382         struct kevent_copyops copyops = {
383                 .k_copyin       = cloudabi32_kevent_copyin,
384                 .k_copyout      = cloudabi32_kevent_copyout,
385                 .arg            = &args,
386         };
387         cloudabi32_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 }