409 lines
12 KiB
C
409 lines
12 KiB
C
/*-
|
|
* Copyright (c) 2015 Nuxi, https://nuxi.nl/
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/syscallsubr.h>
|
|
|
|
#include <contrib/cloudabi/cloudabi64_types.h>
|
|
|
|
#include <compat/cloudabi/cloudabi_util.h>
|
|
|
|
#include <compat/cloudabi64/cloudabi64_proto.h>
|
|
#include <compat/cloudabi64/cloudabi64_util.h>
|
|
|
|
/* Converts a FreeBSD signal number to a CloudABI signal number. */
|
|
static cloudabi_signal_t
|
|
convert_signal(int sig)
|
|
{
|
|
static const cloudabi_signal_t signals[] = {
|
|
[SIGABRT] = CLOUDABI_SIGABRT,
|
|
[SIGALRM] = CLOUDABI_SIGALRM,
|
|
[SIGBUS] = CLOUDABI_SIGBUS,
|
|
[SIGCHLD] = CLOUDABI_SIGCHLD,
|
|
[SIGCONT] = CLOUDABI_SIGCONT,
|
|
[SIGFPE] = CLOUDABI_SIGFPE,
|
|
[SIGHUP] = CLOUDABI_SIGHUP,
|
|
[SIGILL] = CLOUDABI_SIGILL,
|
|
[SIGINT] = CLOUDABI_SIGINT,
|
|
[SIGKILL] = CLOUDABI_SIGKILL,
|
|
[SIGPIPE] = CLOUDABI_SIGPIPE,
|
|
[SIGQUIT] = CLOUDABI_SIGQUIT,
|
|
[SIGSEGV] = CLOUDABI_SIGSEGV,
|
|
[SIGSTOP] = CLOUDABI_SIGSTOP,
|
|
[SIGSYS] = CLOUDABI_SIGSYS,
|
|
[SIGTERM] = CLOUDABI_SIGTERM,
|
|
[SIGTRAP] = CLOUDABI_SIGTRAP,
|
|
[SIGTSTP] = CLOUDABI_SIGTSTP,
|
|
[SIGTTIN] = CLOUDABI_SIGTTIN,
|
|
[SIGTTOU] = CLOUDABI_SIGTTOU,
|
|
[SIGURG] = CLOUDABI_SIGURG,
|
|
[SIGUSR1] = CLOUDABI_SIGUSR1,
|
|
[SIGUSR2] = CLOUDABI_SIGUSR2,
|
|
[SIGVTALRM] = CLOUDABI_SIGVTALRM,
|
|
[SIGXCPU] = CLOUDABI_SIGXCPU,
|
|
[SIGXFSZ] = CLOUDABI_SIGXFSZ,
|
|
};
|
|
|
|
/* Convert unknown signals to SIGABRT. */
|
|
if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
|
|
return (SIGABRT);
|
|
return (signals[sig]);
|
|
}
|
|
|
|
struct cloudabi64_kevent_args {
|
|
const cloudabi64_subscription_t *in;
|
|
cloudabi64_event_t *out;
|
|
bool once;
|
|
};
|
|
|
|
/* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
|
|
static int
|
|
cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
|
|
{
|
|
cloudabi64_subscription_t sub;
|
|
struct cloudabi64_kevent_args *args;
|
|
cloudabi_timestamp_t ts;
|
|
int error;
|
|
|
|
args = arg;
|
|
while (count-- > 0) {
|
|
/* TODO(ed): Copy in multiple entries at once. */
|
|
error = copyin(args->in++, &sub, sizeof(sub));
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
memset(kevp, 0, sizeof(*kevp));
|
|
kevp->udata = TO_PTR(sub.userdata);
|
|
switch (sub.type) {
|
|
case CLOUDABI_EVENTTYPE_CLOCK:
|
|
kevp->filter = EVFILT_TIMER;
|
|
kevp->ident = sub.clock.identifier;
|
|
kevp->fflags = NOTE_NSECONDS;
|
|
if ((sub.clock.flags &
|
|
CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 &&
|
|
sub.clock.timeout > 0) {
|
|
/* Convert absolute timestamp to a relative. */
|
|
error = cloudabi_clock_time_get(curthread,
|
|
sub.clock.clock_id, &ts);
|
|
if (error != 0)
|
|
return (error);
|
|
ts = ts > sub.clock.timeout ? 0 :
|
|
sub.clock.timeout - ts;
|
|
} else {
|
|
/* Relative timestamp. */
|
|
ts = sub.clock.timeout;
|
|
}
|
|
kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
|
|
break;
|
|
case CLOUDABI_EVENTTYPE_FD_READ:
|
|
kevp->filter = EVFILT_READ;
|
|
kevp->ident = sub.fd_readwrite.fd;
|
|
if ((sub.fd_readwrite.flags &
|
|
CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0)
|
|
kevp->fflags = NOTE_FILE_POLL;
|
|
break;
|
|
case CLOUDABI_EVENTTYPE_FD_WRITE:
|
|
kevp->filter = EVFILT_WRITE;
|
|
kevp->ident = sub.fd_readwrite.fd;
|
|
break;
|
|
case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
|
|
kevp->filter = EVFILT_PROCDESC;
|
|
kevp->ident = sub.proc_terminate.fd;
|
|
kevp->fflags = NOTE_EXIT;
|
|
break;
|
|
}
|
|
if (args->once) {
|
|
/* Ignore flags. Simply use oneshot mode. */
|
|
kevp->flags = EV_ADD | EV_ONESHOT;
|
|
} else {
|
|
/* Translate flags. */
|
|
if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0)
|
|
kevp->flags |= EV_ADD;
|
|
if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0)
|
|
kevp->flags |= EV_CLEAR;
|
|
if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0)
|
|
kevp->flags |= EV_DELETE;
|
|
if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0)
|
|
kevp->flags |= EV_DISABLE;
|
|
if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0)
|
|
kevp->flags |= EV_ENABLE;
|
|
if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0)
|
|
kevp->flags |= EV_ONESHOT;
|
|
}
|
|
++kevp;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* Converts FreeBSD's struct kevent to CloudABI's event objects. */
|
|
static int
|
|
cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
|
|
{
|
|
cloudabi64_event_t ev;
|
|
struct cloudabi64_kevent_args *args;
|
|
int error;
|
|
|
|
args = arg;
|
|
while (count-- > 0) {
|
|
/* Convert fields that should always be present. */
|
|
memset(&ev, 0, sizeof(ev));
|
|
ev.userdata = (uintptr_t)kevp->udata;
|
|
switch (kevp->filter) {
|
|
case EVFILT_TIMER:
|
|
ev.type = CLOUDABI_EVENTTYPE_CLOCK;
|
|
ev.clock.identifier = kevp->ident;
|
|
break;
|
|
case EVFILT_READ:
|
|
ev.type = CLOUDABI_EVENTTYPE_FD_READ;
|
|
ev.fd_readwrite.fd = kevp->ident;
|
|
break;
|
|
case EVFILT_WRITE:
|
|
ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
|
|
ev.fd_readwrite.fd = kevp->ident;
|
|
break;
|
|
case EVFILT_PROCDESC:
|
|
ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
|
|
ev.proc_terminate.fd = kevp->ident;
|
|
break;
|
|
}
|
|
|
|
if ((kevp->flags & EV_ERROR) == 0) {
|
|
/* Success. */
|
|
switch (kevp->filter) {
|
|
case EVFILT_READ:
|
|
case EVFILT_WRITE:
|
|
ev.fd_readwrite.nbytes = kevp->data;
|
|
if ((kevp->flags & EV_EOF) != 0) {
|
|
ev.fd_readwrite.flags |=
|
|
CLOUDABI_EVENT_FD_READWRITE_HANGUP;
|
|
}
|
|
break;
|
|
case EVFILT_PROCDESC:
|
|
if (WIFSIGNALED(kevp->data)) {
|
|
/* Process got signalled. */
|
|
ev.proc_terminate.signal =
|
|
convert_signal(WTERMSIG(kevp->data));
|
|
ev.proc_terminate.exitcode = 0;
|
|
} else {
|
|
/* Process exited. */
|
|
ev.proc_terminate.signal = 0;
|
|
ev.proc_terminate.exitcode =
|
|
WEXITSTATUS(kevp->data);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
/* Error. */
|
|
ev.error = cloudabi_convert_errno(kevp->data);
|
|
}
|
|
++kevp;
|
|
|
|
/* TODO(ed): Copy out multiple entries at once. */
|
|
error = copyout(&ev, args->out++, sizeof(ev));
|
|
if (error != 0)
|
|
return (error);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
|
|
{
|
|
struct cloudabi64_kevent_args args = {
|
|
.in = uap->in,
|
|
.out = uap->out,
|
|
.once = true,
|
|
};
|
|
struct kevent_copyops copyops = {
|
|
.k_copyin = cloudabi64_kevent_copyin,
|
|
.k_copyout = cloudabi64_kevent_copyout,
|
|
.arg = &args,
|
|
};
|
|
|
|
/*
|
|
* Bandaid to support CloudABI futex constructs that are not
|
|
* implemented through FreeBSD's kqueue().
|
|
*/
|
|
if (uap->nsubscriptions == 1) {
|
|
cloudabi64_subscription_t sub;
|
|
cloudabi64_event_t ev = {};
|
|
int error;
|
|
|
|
error = copyin(uap->in, &sub, sizeof(sub));
|
|
if (error != 0)
|
|
return (error);
|
|
ev.userdata = sub.userdata;
|
|
ev.type = sub.type;
|
|
if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) {
|
|
/* Wait on a condition variable. */
|
|
ev.condvar.condvar = sub.condvar.condvar;
|
|
ev.error = cloudabi_convert_errno(
|
|
cloudabi_futex_condvar_wait(
|
|
td, TO_PTR(sub.condvar.condvar),
|
|
sub.condvar.condvar_scope,
|
|
TO_PTR(sub.condvar.lock),
|
|
sub.condvar.lock_scope,
|
|
CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0));
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev, uap->out, sizeof(ev)));
|
|
} else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) {
|
|
/* Acquire a read lock. */
|
|
ev.lock.lock = sub.lock.lock;
|
|
ev.error = cloudabi_convert_errno(
|
|
cloudabi_futex_lock_rdlock(
|
|
td, TO_PTR(sub.lock.lock),
|
|
sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
|
|
UINT64_MAX, 0));
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev, uap->out, sizeof(ev)));
|
|
} else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) {
|
|
/* Acquire a write lock. */
|
|
ev.lock.lock = sub.lock.lock;
|
|
ev.error = cloudabi_convert_errno(
|
|
cloudabi_futex_lock_wrlock(
|
|
td, TO_PTR(sub.lock.lock),
|
|
sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
|
|
UINT64_MAX, 0));
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev, uap->out, sizeof(ev)));
|
|
}
|
|
} else if (uap->nsubscriptions == 2) {
|
|
cloudabi64_subscription_t sub[2];
|
|
cloudabi64_event_t ev[2] = {};
|
|
int error;
|
|
|
|
error = copyin(uap->in, &sub, sizeof(sub));
|
|
if (error != 0)
|
|
return (error);
|
|
ev[0].userdata = sub[0].userdata;
|
|
ev[0].type = sub[0].type;
|
|
ev[1].userdata = sub[1].userdata;
|
|
ev[1].type = sub[1].type;
|
|
if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR &&
|
|
sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
|
|
sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
|
|
/* Wait for a condition variable with timeout. */
|
|
ev[0].condvar.condvar = sub[0].condvar.condvar;
|
|
ev[1].clock.identifier = sub[1].clock.identifier;
|
|
error = cloudabi_futex_condvar_wait(
|
|
td, TO_PTR(sub[0].condvar.condvar),
|
|
sub[0].condvar.condvar_scope,
|
|
TO_PTR(sub[0].condvar.lock),
|
|
sub[0].condvar.lock_scope, sub[1].clock.clock_id,
|
|
sub[1].clock.timeout, sub[1].clock.precision);
|
|
if (error == ETIMEDOUT) {
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev[1], uap->out,
|
|
sizeof(ev[1])));
|
|
}
|
|
|
|
ev[0].error = cloudabi_convert_errno(error);
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev[0], uap->out, sizeof(ev[0])));
|
|
} else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK &&
|
|
sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
|
|
sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
|
|
/* Acquire a read lock with a timeout. */
|
|
ev[0].lock.lock = sub[0].lock.lock;
|
|
ev[1].clock.identifier = sub[1].clock.identifier;
|
|
error = cloudabi_futex_lock_rdlock(
|
|
td, TO_PTR(sub[0].lock.lock),
|
|
sub[0].lock.lock_scope, sub[1].clock.clock_id,
|
|
sub[1].clock.timeout, sub[1].clock.precision);
|
|
if (error == ETIMEDOUT) {
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev[1], uap->out,
|
|
sizeof(ev[1])));
|
|
}
|
|
|
|
ev[0].error = cloudabi_convert_errno(error);
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev[0], uap->out, sizeof(ev[0])));
|
|
} else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK &&
|
|
sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
|
|
sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
|
|
/* Acquire a write lock with a timeout. */
|
|
ev[0].lock.lock = sub[0].lock.lock;
|
|
ev[1].clock.identifier = sub[1].clock.identifier;
|
|
error = cloudabi_futex_lock_wrlock(
|
|
td, TO_PTR(sub[0].lock.lock),
|
|
sub[0].lock.lock_scope, sub[1].clock.clock_id,
|
|
sub[1].clock.timeout, sub[1].clock.precision);
|
|
if (error == ETIMEDOUT) {
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev[1], uap->out,
|
|
sizeof(ev[1])));
|
|
}
|
|
|
|
ev[0].error = cloudabi_convert_errno(error);
|
|
td->td_retval[0] = 1;
|
|
return (copyout(&ev[0], uap->out, sizeof(ev[0])));
|
|
}
|
|
}
|
|
|
|
return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops));
|
|
}
|
|
|
|
int
|
|
cloudabi64_sys_poll_fd(struct thread *td,
|
|
struct cloudabi64_sys_poll_fd_args *uap)
|
|
{
|
|
struct cloudabi64_kevent_args args = {
|
|
.in = uap->in,
|
|
.out = uap->out,
|
|
.once = false,
|
|
};
|
|
struct kevent_copyops copyops = {
|
|
.k_copyin = cloudabi64_kevent_copyin,
|
|
.k_copyout = cloudabi64_kevent_copyout,
|
|
.arg = &args,
|
|
};
|
|
cloudabi64_subscription_t subtimo;
|
|
struct timespec timeout;
|
|
int error;
|
|
|
|
if (uap->timeout != NULL) {
|
|
/* Poll with a timeout. */
|
|
error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
|
|
if (error != 0)
|
|
return (error);
|
|
if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
|
|
subtimo.clock.flags != 0)
|
|
return (EINVAL);
|
|
timeout.tv_sec = subtimo.clock.timeout / 1000000000;
|
|
timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
|
|
return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len,
|
|
©ops, &timeout));
|
|
} else {
|
|
/* Poll without a timeout. */
|
|
return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len,
|
|
©ops, NULL));
|
|
}
|
|
}
|