diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -420,6 +420,7 @@ _Fork; fspacectl; kqueuex; + signalfd; swapoff; timerfd_create; timerfd_gettime; diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h --- a/sys/bsm/audit_kevents.h +++ b/sys/bsm/audit_kevents.h @@ -664,6 +664,7 @@ #define AUE_AIO_READV 43268 /* FreeBSD-specific. */ #define AUE_FSPACECTL 43269 /* FreeBSD-specific. */ #define AUE_TIMERFD 43270 /* FreeBSD/Linux. */ +#define AUE_SIGNALFD 43271 /* FreeBSD/Linux. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -504,4 +504,5 @@ #define FREEBSD32_SYS_timerfd_create 584 #define FREEBSD32_SYS_freebsd32_timerfd_gettime 585 #define FREEBSD32_SYS_freebsd32_timerfd_settime 586 -#define FREEBSD32_SYS_MAXSYSCALL 587 +#define FREEBSD32_SYS_signalfd 587 +#define FREEBSD32_SYS_MAXSYSCALL 588 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -592,4 +592,5 @@ "timerfd_create", /* 584 = timerfd_create */ "freebsd32_timerfd_gettime", /* 585 = freebsd32_timerfd_gettime */ "freebsd32_timerfd_settime", /* 586 = freebsd32_timerfd_settime */ + "signalfd", /* 587 = signalfd */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -648,4 +648,5 @@ { .sy_narg = AS(timerfd_create_args), .sy_call = (sy_call_t *)sys_timerfd_create, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 584 = timerfd_create */ { .sy_narg = AS(freebsd32_timerfd_gettime_args), .sy_call = (sy_call_t *)freebsd32_timerfd_gettime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 585 = freebsd32_timerfd_gettime */ { .sy_narg = AS(freebsd32_timerfd_settime_args), .sy_call = (sy_call_t *)freebsd32_timerfd_settime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 586 = freebsd32_timerfd_settime */ + { .sy_narg = AS(signalfd_args), .sy_call = (sy_call_t *)sys_signalfd, .sy_auevent = AUE_SIGNALFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 587 = signalfd */ }; diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c --- a/sys/compat/freebsd32/freebsd32_systrace_args.c +++ b/sys/compat/freebsd32/freebsd32_systrace_args.c @@ -3353,6 +3353,15 @@ *n_args = 4; break; } + /* signalfd */ + case 587: { + struct signalfd_args *p = params; + iarg[a++] = p->fd; /* int */ + uarg[a++] = (intptr_t)p->mask; /* const sigset_t * */ + iarg[a++] = p->flags; /* int */ + *n_args = 3; + break; + } default: *n_args = 0; break; @@ -9051,6 +9060,22 @@ break; }; break; + /* signalfd */ + case 587: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland const sigset_t *"; + break; + case 2: + p = "int"; + break; + default: + break; + }; + break; default: break; }; @@ -10929,6 +10954,11 @@ if (ndx == 0 || ndx == 1) p = "int"; break; + /* signalfd */ + case 587: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -3918,6 +3918,7 @@ kern/sys_pipe.c standard kern/sys_procdesc.c standard kern/sys_process.c standard +kern/sys_signalfd.c standard kern/sys_socket.c standard kern/sys_timerfd.c standard kern/syscalls.c standard diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -647,4 +647,5 @@ { .sy_narg = AS(timerfd_create_args), .sy_call = (sy_call_t *)sys_timerfd_create, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 584 = timerfd_create */ { .sy_narg = AS(timerfd_gettime_args), .sy_call = (sy_call_t *)sys_timerfd_gettime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 585 = timerfd_gettime */ { .sy_narg = AS(timerfd_settime_args), .sy_call = (sy_call_t *)sys_timerfd_settime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 586 = timerfd_settime */ + { .sy_narg = AS(signalfd_args), .sy_call = (sy_call_t *)sys_signalfd, .sy_auevent = AUE_SIGNALFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 587 = signalfd */ }; diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -2284,13 +2285,16 @@ /* * If the signal is being ignored, then we forget about it - * immediately, except when the target process executes - * sigwait(). (Note: we don't set SIGCONT in ps_sigignore, - * and if it is set to SIG_IGN, action will be SIG_DFL here.) + * immediately, except when the target process has an active + * signalfd or executes sigwait(). (Note: we don't set + * SIGCONT in ps_sigignore, and if it is set to SIG_IGN, + * action will be SIG_DFL here.) */ mtx_lock(&ps->ps_mtx); if (SIGISMEMBER(ps->ps_sigignore, sig)) { - if (kern_sig_discard_ign && + if (!LIST_EMPTY(&p->p_sfd)) { + action = SIG_HOLD; + } else if (kern_sig_discard_ign && (p->p_sysent->sv_flags & SV_SIG_DISCIGN) == 0) { SDT_PROBE3(proc, , , signal__discard, td, p, sig); @@ -2344,6 +2348,8 @@ ret = sigqueue_add(sigqueue, sig, ksi); if (ret != 0) return (ret); + if (!LIST_EMPTY(&p->p_sfd) && action != SIG_CATCH) + signalfd_post(p, sig); signotify(td); /* * Defer further processing for signals which are held, diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -97,11 +97,11 @@ "struct proc KBI p_flag"); _Static_assert(offsetof(struct proc, p_pid) == 0xc4, "struct proc KBI p_pid"); -_Static_assert(offsetof(struct proc, p_filemon) == 0x3c8, +_Static_assert(offsetof(struct proc, p_filemon) == 0x3d0, "struct proc KBI p_filemon"); -_Static_assert(offsetof(struct proc, p_comm) == 0x3e0, +_Static_assert(offsetof(struct proc, p_comm) == 0x3e8, "struct proc KBI p_comm"); -_Static_assert(offsetof(struct proc, p_emuldata) == 0x4d0, +_Static_assert(offsetof(struct proc, p_emuldata) == 0x4d8, "struct proc KBI p_emuldata"); #endif #ifdef __i386__ @@ -117,11 +117,11 @@ "struct proc KBI p_flag"); _Static_assert(offsetof(struct proc, p_pid) == 0x78, "struct proc KBI p_pid"); -_Static_assert(offsetof(struct proc, p_filemon) == 0x270, +_Static_assert(offsetof(struct proc, p_filemon) == 0x274, "struct proc KBI p_filemon"); -_Static_assert(offsetof(struct proc, p_comm) == 0x284, +_Static_assert(offsetof(struct proc, p_comm) == 0x288, "struct proc KBI p_comm"); -_Static_assert(offsetof(struct proc, p_emuldata) == 0x318, +_Static_assert(offsetof(struct proc, p_emuldata) == 0x31c, "struct proc KBI p_emuldata"); #endif diff --git a/sys/kern/sys_signalfd.c b/sys/kern/sys_signalfd.c new file mode 100644 --- /dev/null +++ b/sys/kern/sys_signalfd.c @@ -0,0 +1,382 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Jake Freeland + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static MALLOC_DEFINE(M_SIGNALFD, "signalfd", "signalfd structures"); + +struct signalfd { + sigset_t sfd_mask; + int sfd_flags; + struct mtx sfd_lock; + struct selinfo sfd_sel; + LIST_ENTRY(signalfd) sfd_link; +}; + +void +signalfd_post(struct proc *p, int signo) +{ + struct signalfd *sfd; + if (signo == SIGKILL || signo == SIGSTOP) + return; + LIST_FOREACH(sfd, &p->p_sfd, sfd_link) { + mtx_lock(&sfd->sfd_lock); + if (SIGISMEMBER(sfd->sfd_mask, signo)) + wakeup(&sfd->sfd_mask); + mtx_unlock(&sfd->sfd_lock); + } +} + +static bool +signalfd_pending(struct proc *p, struct signalfd *sfd) +{ + int sig; + SIG_FOREACH(sig, &p->p_siglist) { + if (SIGISMEMBER(sfd->sfd_mask, sig)) + return (true); + } + return (false); +} + +static void +sitossi(siginfo_t *si, struct signalfd_siginfo *ssi) +{ + _Static_assert(sizeof(*ssi) == 128, "signalfd_siginfo"); + ssi->ssi_signo = si->si_signo; + ssi->ssi_errno = si->si_errno; + ssi->ssi_code = si->si_code; + ssi->ssi_pid = si->si_pid; + ssi->ssi_uid = si->si_uid; + ssi->ssi_status = si->si_status; + ssi->ssi_addr = (uint64_t)(uintptr_t)si->si_addr; + ssi->ssi_int = si->si_value.sival_int; + ssi->ssi_ptr = (uint64_t)(uintptr_t)si->si_value.sival_ptr; + ssi->ssi_addr_lsb = ssi->ssi_addr & 0x1; + + /* Linux-specific fields. Extend later, if applicable. */ + ssi->ssi_fd = -1; + /* ssi->ssi_utime = si-> */ + /* ssi->ssi_stime = si-> */ + ssi->ssi_call_addr = (uint64_t)(uintptr_t)NULL; + /* ssi->ssi_arch = si-> */ + + /* Unioned fields. Only one at a time. */ + if (si->si_signo == SIGBUS) { + ssi->ssi_trapno = si->si_trapno; + } else if (si->si_code == SI_TIMER) { + ssi->ssi_tid = si->si_timerid; + ssi->ssi_overrun = si->si_overrun; + } else if (si->si_code == SI_MESGQ) { + ssi->ssi_fd = si->si_mqd; + } else if (si->si_signo == SIGIO) { + ssi->ssi_band = si->si_band; + } else if (si->si_code == TRAP_CAP) { + ssi->ssi_syscall = si->si_syscall; + } +} + +static int +signalfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred, + int flags, struct thread *td) +{ + struct signalfd *sfd = fp->f_data; + struct signalfd_siginfo ssi; + struct proc *p = td->td_proc; + sigqueue_t *sq = &p->p_sigqueue; + ksiginfo_t *ksi, *next; + int error = 0; + + if (uio->uio_resid < sizeof(struct signalfd_siginfo)) + return (EINVAL); + +retry: + PROC_LOCK(p); + mtx_lock(&sfd->sfd_lock); + if (!signalfd_pending(p, sfd)) { + if ((fp->f_flag & O_NONBLOCK) != 0) { + error = EAGAIN; + goto out; + } + PROC_UNLOCK(p); + error = mtx_sleep(&sfd->sfd_mask, &sfd->sfd_lock, + PCATCH | PDROP, "sfdrd", 0); + if (error != 0) + return (error); + goto retry; + } + + TAILQ_FOREACH_SAFE(ksi, &sq->sq_list, ksi_link, next) { + if (SIGISMEMBER(sfd->sfd_mask, ksi->ksi_signo)) { + sitossi(&ksi->ksi_info, &ssi); + error = uiomove_nofault(&ssi, sizeof(ssi), uio); + if (error != 0) + goto out; + TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); + ksi->ksi_sigq = NULL; + if ((ksi->ksi_flags & KSI_EXT) == 0) { + ksiginfo_free(ksi); + --p->p_pendingcnt; + } + } + } + SIGSETNAND(sq->sq_kill, sfd->sfd_mask); + SIGSETNAND(sq->sq_ptrace, sfd->sfd_mask); + SIGSETNAND(sq->sq_signals, sfd->sfd_mask); + +out: + mtx_unlock(&sfd->sfd_lock); + PROC_UNLOCK(p); + return (error); +} + +static int +signalfd_ioctl(struct file *fp, u_long cmd, void *data, + struct ucred *active_cred, struct thread *td) +{ + switch (cmd) { + case FIOASYNC: + if (*(int *)data != 0) + atomic_set_int(&fp->f_flag, O_ASYNC); + else + atomic_clear_int(&fp->f_flag, O_ASYNC); + return (0); + case FIONBIO: + if (*(int *)data != 0) + atomic_set_int(&fp->f_flag, O_NONBLOCK); + else + atomic_clear_int(&fp->f_flag, O_NONBLOCK); + return (0); + } + return (ENOTTY); +} + +static int +signalfd_poll(struct file *fp, int events, struct ucred *active_cred, + struct thread *td) +{ + struct proc *p = td->td_proc; + struct signalfd *sfd = fp->f_data; + int revents = 0; + + PROC_LOCK(p); + mtx_lock(&sfd->sfd_lock); + if ((events & (POLLIN | POLLRDNORM)) != 0 && + signalfd_pending(p, sfd)) + revents |= events & (POLLIN | POLLRDNORM); + + if (revents == 0) + selrecord(td, &sfd->sfd_sel); + mtx_unlock(&sfd->sfd_lock); + PROC_UNLOCK(p); + return (revents); +} + +static void +filt_signalfddetach(struct knote *kn) +{ + struct signalfd *sfd = kn->kn_hook; + + mtx_lock(&sfd->sfd_lock); + knlist_remove(&sfd->sfd_sel.si_note, kn, 1); + mtx_unlock(&sfd->sfd_lock); +} + +static int +filt_signalfdread(struct knote *kn, long hint) +{ + struct signalfd *sfd = kn->kn_hook; + return (signalfd_pending(curthread->td_proc, sfd)); +} + +static struct filterops signalfd_rfiltops = { + .f_isfd = 1, + .f_detach = filt_signalfddetach, + .f_event = filt_signalfdread, +}; + +static int +signalfd_kqfilter(struct file *fp, struct knote *kn) +{ + struct signalfd *sfd = fp->f_data; + + if (kn->kn_filter != EVFILT_READ) + return (EINVAL); + + kn->kn_fop = &signalfd_rfiltops; + kn->kn_hook = sfd; + knlist_add(&sfd->sfd_sel.si_note, kn, 0); + + return (0); +} + +static int +signalfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) +{ + bzero(sb, sizeof(*sb)); + sb->st_nlink = fp->f_count - 1; + sb->st_uid = fp->f_cred->cr_uid; + sb->st_gid = fp->f_cred->cr_gid; + sb->st_blksize = PAGE_SIZE; + return (0); +} + +static int +signalfd_close(struct file *fp, struct thread *td) +{ + struct proc *p = td->td_proc; + struct signalfd *sfd = fp->f_data; + + PROC_LOCK(p); + LIST_REMOVE(sfd, sfd_link); + PROC_UNLOCK(p); + seldrain(&sfd->sfd_sel); + knlist_destroy(&sfd->sfd_sel.si_note); + mtx_destroy(&sfd->sfd_lock); + free(sfd, M_SIGNALFD); + fp->f_ops = &badfileops; + + return (0); +} + +static int +signalfd_fill_kinfo(struct file *fp, struct kinfo_file *kif, + struct filedesc *fdp) +{ + + struct signalfd *sfd = fp->f_data; + + kif->kf_type = KF_TYPE_SIGNALFD; + mtx_lock(&sfd->sfd_lock); + kif->kf_un.kf_signalfd.kf_signalfd_mask = (uintptr_t)&sfd->sfd_mask; + kif->kf_un.kf_signalfd.kf_signalfd_flags = sfd->sfd_flags; + kif->kf_un.kf_signalfd.kf_signalfd_addr = (uintptr_t)sfd; + mtx_unlock(&sfd->sfd_lock); + + return (0); +} + +static struct fileops signalfdops = { + .fo_read = signalfd_read, + .fo_write = invfo_rdwr, + .fo_truncate = invfo_truncate, + .fo_ioctl = signalfd_ioctl, + .fo_poll = signalfd_poll, + .fo_kqfilter = signalfd_kqfilter, + .fo_stat = signalfd_stat, + .fo_close = signalfd_close, + .fo_chmod = invfo_chmod, + .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, + .fo_fill_kinfo = signalfd_fill_kinfo, + .fo_flags = DFLAG_PASSABLE, +}; + +int +kern_signalfd(struct thread *td, int fd, const sigset_t *mask, int flags) +{ + struct proc *p = td->td_proc; + struct signalfd *sfd; + struct file *fp; + int error, fflags = 0; + bool new = fd == -1 ? true : false; + + if ((flags & ~(SFD_NONBLOCK | SFD_CLOEXEC)) != 0) + return (EINVAL); + if ((flags & SFD_CLOEXEC) != 0) + fflags |= O_CLOEXEC; + + if (new) { + sfd = malloc(sizeof(*sfd), M_SIGNALFD, M_WAITOK | M_ZERO); + if (sfd == NULL) + return (ENOMEM); + mtx_init(&sfd->sfd_lock, "signalfd", NULL, MTX_DEF); + knlist_init_mtx(&sfd->sfd_sel.si_note, &sfd->sfd_lock); + + error = falloc(td, &fp, &fd, fflags); + if (error != 0) + return (error); + fflags = FREAD; + if ((flags & SFD_NONBLOCK) != 0) + fflags |= O_NONBLOCK; + finit(fp, fflags, DTYPE_SIGNALFD, sfd, &signalfdops); + } else { + error = fget(td, fd, &cap_write_rights, &fp); + if (error != 0) + return (error); + sfd = fp->f_data; + if (sfd == NULL || fp->f_type != DTYPE_SIGNALFD) { + fdrop(fp, td); + return (EINVAL); + } + mtx_lock(&sfd->sfd_lock); + if ((flags & SFD_NONBLOCK) != 0) + fp->f_flag |= O_NONBLOCK; + else + fp->f_flag &= ~O_NONBLOCK; + } + sfd->sfd_mask = *mask; + SIG_CANTMASK(sfd->sfd_mask); + sfd->sfd_flags = flags; + if (new) { + PROC_LOCK(p); + LIST_INSERT_HEAD(&p->p_sfd, sfd, sfd_link); + PROC_UNLOCK(p); + } else { + mtx_unlock(&sfd->sfd_lock); + } + + fdrop(fp, td); + td->td_retval[0] = fd; + return (0); +} + +int +sys_signalfd(struct thread *td, struct signalfd_args *uap) +{ + sigset_t mask; + int error; + + error = copyin(uap->mask, &mask, sizeof(mask)); + if (error != 0) + return (error); + return (kern_signalfd(td, uap->fd, &mask, uap->flags)); +} diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -592,4 +592,5 @@ "timerfd_create", /* 584 = timerfd_create */ "timerfd_gettime", /* 585 = timerfd_gettime */ "timerfd_settime", /* 586 = timerfd_settime */ + "signalfd", /* 587 = signalfd */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -3333,5 +3333,12 @@ _Out_opt_ _Contains_long_timet_ struct itimerspec *old_value ); } +587 AUE_SIGNALFD STD|CAPENABLED { + int signalfd( + int fd, + _In_ const sigset_t *mask, + int flags + ); + } ; vim: syntax=off diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -3449,6 +3449,15 @@ *n_args = 4; break; } + /* signalfd */ + case 587: { + struct signalfd_args *p = params; + iarg[a++] = p->fd; /* int */ + uarg[a++] = (intptr_t)p->mask; /* const sigset_t * */ + iarg[a++] = p->flags; /* int */ + *n_args = 3; + break; + } default: *n_args = 0; break; @@ -9221,6 +9230,22 @@ break; }; break; + /* signalfd */ + case 587: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland const sigset_t *"; + break; + case 2: + p = "int"; + break; + default: + break; + }; + break; default: break; }; @@ -11194,6 +11219,11 @@ if (ndx == 0 || ndx == 1) p = "int"; break; + /* signalfd */ + case 587: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/sys/file.h b/sys/sys/file.h --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -71,6 +71,7 @@ #define DTYPE_PROCDESC 12 /* process descriptor */ #define DTYPE_EVENTFD 13 /* eventfd */ #define DTYPE_TIMERFD 14 /* timerfd */ +#define DTYPE_SIGNALFD 15 /* signalfd */ #ifdef _KERNEL diff --git a/sys/sys/proc.h b/sys/sys/proc.h --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -707,6 +707,7 @@ struct sigiolst p_sigiolst; /* (c) List of sigio sources. */ int p_sigparent; /* (c) Signal to parent on exit. */ int p_sig; /* (n) For core dump/debugger XXX. */ + LIST_HEAD(, signalfd) p_sfd; /* (c) Sig descriptor queue. */ u_int p_ptevents; /* (c + e) ptrace() event mask. */ struct kaioinfo *p_aioinfo; /* (y) ASYNC I/O info. */ struct thread *p_singlethread;/* (c + j) If single threading this is it */ diff --git a/sys/sys/signalfd.h b/sys/sys/signalfd.h new file mode 100644 --- /dev/null +++ b/sys/sys/signalfd.h @@ -0,0 +1,80 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Jake Freeland + * + * 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. + */ + +#ifndef _SYS_SIGNALFD_H_ +#define _SYS_SIGNALFD_H_ + +#include +#include +#include +#include + +/* Creation flags. */ +#define SFD_NONBLOCK O_NONBLOCK +#define SFD_CLOEXEC O_CLOEXEC + +/* Signal information returned by signalfd. */ +struct signalfd_siginfo { + uint32_t ssi_signo; + int32_t ssi_errno; + int32_t ssi_code; + uint32_t ssi_pid; + uint32_t ssi_uid; + int32_t ssi_fd; + uint32_t ssi_tid; + uint32_t ssi_band; + uint32_t ssi_overrun; + uint32_t ssi_trapno; + int32_t ssi_status; + int32_t ssi_int; + uint64_t ssi_ptr; + uint64_t ssi_utime; + uint64_t ssi_stime; + uint64_t ssi_addr; + uint16_t ssi_addr_lsb; + uint16_t __pad2; + int32_t ssi_syscall; + uint64_t ssi_call_addr; + uint32_t ssi_arch; + uint8_t __pad[28]; + /* sizeof(struct signalfd_siginfo) must be 128. */ +}; + +#ifndef _KERNEL + +__BEGIN_DECLS +int signalfd(int fd, const sigset_t *mask, int flags); +__END_DECLS + +#else /* _KERNEL */ + +int kern_signalfd(struct thread *td, int fd, const sigset_t *mask, int flags); +void signalfd_post(struct proc *p, int signo); + +#endif /* !_KERNEL */ + +#endif /* !_SYS_SIGNALFD_H_ */ diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -523,4 +523,5 @@ #define SYS_timerfd_create 584 #define SYS_timerfd_gettime 585 #define SYS_timerfd_settime 586 -#define SYS_MAXSYSCALL 587 +#define SYS_signalfd 587 +#define SYS_MAXSYSCALL 588 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -428,4 +428,5 @@ kqueuex.o \ timerfd_create.o \ timerfd_gettime.o \ - timerfd_settime.o + timerfd_settime.o \ + signalfd.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -1871,6 +1871,11 @@ char new_value_l_[PADL_(const struct itimerspec *)]; const struct itimerspec * new_value; char new_value_r_[PADR_(const struct itimerspec *)]; char old_value_l_[PADL_(struct itimerspec *)]; struct itimerspec * old_value; char old_value_r_[PADR_(struct itimerspec *)]; }; +struct signalfd_args { + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; + char mask_l_[PADL_(const sigset_t *)]; const sigset_t * mask; char mask_r_[PADR_(const sigset_t *)]; + char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)]; +}; int sys_exit(struct thread *, struct exit_args *); int sys_fork(struct thread *, struct fork_args *); int sys_read(struct thread *, struct read_args *); @@ -2270,6 +2275,7 @@ int sys_timerfd_create(struct thread *, struct timerfd_create_args *); int sys_timerfd_gettime(struct thread *, struct timerfd_gettime_args *); int sys_timerfd_settime(struct thread *, struct timerfd_settime_args *); +int sys_signalfd(struct thread *, struct signalfd_args *); #ifdef COMPAT_43 @@ -3243,6 +3249,7 @@ #define SYS_AUE_timerfd_create AUE_TIMERFD #define SYS_AUE_timerfd_gettime AUE_TIMERFD #define SYS_AUE_timerfd_settime AUE_TIMERFD +#define SYS_AUE_signalfd AUE_SIGNALFD #undef PAD_ #undef PADL_ diff --git a/sys/sys/user.h b/sys/sys/user.h --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -269,6 +269,7 @@ #define KF_TYPE_DEV 12 #define KF_TYPE_EVENTFD 13 #define KF_TYPE_TIMERFD 14 +#define KF_TYPE_SIGNALFD 15 #define KF_TYPE_UNKNOWN 255 #define KF_VTYPE_VNON 0 @@ -455,6 +456,12 @@ uint32_t kf_timerfd_flags; uint64_t kf_timerfd_addr; } kf_timerfd; + struct { + uint64_t kf_signalfd_mask; + uint32_t kf_signalfd_flags; + uint32_t kf_signalfd_spareint[3]; + uint64_t kf_signalfd_addr; + } kf_signalfd; struct { uint64_t kf_kqueue_addr; int32_t kf_kqueue_count;