diff options
Diffstat (limited to 'sys/kern/kern_sig.c')
-rw-r--r-- | sys/kern/kern_sig.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c new file mode 100644 index 0000000..ed29397 --- /dev/null +++ b/sys/kern/kern_sig.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * 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. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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/signal.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/param.h> +#include <vm/dynalloc.h> +#include <string.h> + +#define sigmask(SIGNO) BIT(SIGNO) + +static struct sigaction sa_tab[] = { + [SIGFPE] = { + .sa_handler = sigfpe_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + }, + + [SIGKILL] = { + .sa_handler = sigkill_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + }, + + [SIGSEGV] = { + .sa_handler = sigsegv_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + }, +}; + +/* + * Register a new signal descriptor, set it in the process + * structure and return it in `ksig`. + * + * @td: Process to register signal to. + * @signo: Signal number to register. + * @ksig: Will contain a pointer to new signal descriptor. + */ +int +newsig(struct proc *td, int signo, struct ksiginfo **ksig) +{ + struct ksiginfo *ksig_tmp; + + /* Ensure we have valid args */ + if (td == NULL || signo >= PROC_SIGMAX) + return -EINVAL; + + /* + * Make sure we have a valid signal list. If we already + * have a signal registered in a slot, free up memory used + * for that signal descriptor so we can override it with the + * new one. + */ + if (td->ksig_list == NULL) + return -EIO; + if ((ksig_tmp = td->ksig_list[signo]) != NULL) + dynfree(ksig_tmp); + + /* Allocate our new signal */ + ksig_tmp = dynalloc(sizeof(*ksig_tmp)); + if (ksig_tmp == NULL) + return -ENOMEM; + + memset(ksig_tmp, 0, sizeof(*ksig_tmp)); + ksig_tmp->signo = signo; + td->ksig_list[signo] = ksig_tmp; + *ksig = ksig_tmp; + return 0; +} + +/* + * Remove a signal from the signal table. + * + * @td: Process to remove signal from. + * @signo: Signal to remove. + */ +int +delsig(struct proc *td, int signo) +{ + struct ksiginfo *ksig_tmp; + + /* Ensure we have valid args */ + if (td == NULL || signo >= PROC_SIGMAX) + return -EINVAL; + + /* Don't do anything if it doesn't exist */ + if ((ksig_tmp = td->ksig_list[signo]) == NULL) + return 0; + + dynfree(ksig_tmp); + td->ksig_list[signo] = NULL; + return 0; +} + +int +sendsig(struct proc *td, const sigset_t *set) +{ + struct ksiginfo *ksig_tmp; + + /* Ensure arguments are correct */ + if (td == NULL || set == NULL) + return -EINVAL; + + /* Enqueue required ksiginfo structures */ + for (int i = 0; i < PROC_SIGMAX; ++i) { + if ((ksig_tmp = td->ksig_list[i]) == NULL) { + continue; + } + + /* Enqueue if it is a member of the sigset */ + if (sigismember(set, i)) { + spinlock_acquire(&td->ksigq_lock); + TAILQ_INSERT_TAIL(&td->ksigq, ksig_tmp, link); + spinlock_release(&td->ksigq_lock); + } + } + + return 0; +} + +int +signals_init(struct proc *td) +{ + struct sigaction *sa; + struct ksiginfo *ksig; + int error, i; + + TAILQ_INIT(&td->ksigq); + + /* Populate process signal table with defaults */ + for (i = 0; i < NELEM(sa_tab); ++i) { + sa = &sa_tab[i]; + + /* Drop actions that aren't set up */ + if (sa->sa_handler == NULL && sa->sa_sigaction == NULL) { + continue; + } + + /* Attempt to register the new signal */ + if ((error = newsig(td, i, &ksig)) != 0) { + goto fail; + } + + ksig->si = sa; + } + + return 0; + +fail: + /* + * We do not want the process signal table to be in + * an inconsistent state so we clean up on failure. + */ + for (int j = 0; j < i; ++j) { + if ((error = delsig(td, j)) != 0) { + kprintf("delsig() failed on signal %d (returned %d)\n", + j, error); + } + } + + return error; +} + +void +dispatch_signals(struct proc *td) +{ + struct ksiginfo *ksig_tmp; + struct sigaction *action; + + spinlock_acquire(&td->ksigq_lock); + while (!TAILQ_EMPTY(&td->ksigq)) { + /* Dequeue signal descriptor */ + ksig_tmp = TAILQ_FIRST(&td->ksigq); + TAILQ_REMOVE(&td->ksigq, ksig_tmp, link); + + /* Invoke handler */ + action = ksig_tmp->si; + action->sa_handler(ksig_tmp->signo); + } + + spinlock_release(&td->ksigq_lock); +} + +int +sigemptyset(sigset_t *set) +{ + *set = 0; + return 0; +} + +int +sigfillset(sigset_t *set) +{ + *set = ~(sigset_t)0; + return 0; +} + +int +sigaddset(sigset_t *set, int signo) +{ + if (signo <= 0 || signo >= PROC_SIGMAX) + return -EINVAL; + + *set |= sigmask(signo); + return 0; +} + +int +sigdelset(sigset_t *set, int signo) +{ + if (signo <= 0 || signo >= PROC_SIGMAX) + return -EINVAL; + + *set &= ~sigmask(signo); + return 0; +} + +int +sigismember(const sigset_t *set, int signo) +{ + if (signo <= 0 || signo >= PROC_SIGMAX) + return -EINVAL; + + return (*set & sigmask(signo)) != 0; +} |