803 lines
27 KiB
C
803 lines
27 KiB
C
/// @file signal.c
|
|
/// @brief Signals definition.
|
|
/// @copyright (c) 2014-2022 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
// Include the kernel log levels.
|
|
#include "sys/kernel_levels.h"
|
|
/// Change the header.
|
|
#define __DEBUG_HEADER__ "[SIGNAL]"
|
|
/// Set the log level.
|
|
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE
|
|
|
|
#include "system/signal.h"
|
|
#include "process/wait.h"
|
|
#include "process/scheduler.h"
|
|
#include "process/process.h"
|
|
#include "sys/errno.h"
|
|
#include "assert.h"
|
|
#include "io/debug.h"
|
|
#include "string.h"
|
|
#include "klib/irqflags.h"
|
|
#include "klib/stack_helper.h"
|
|
|
|
/// SLAB caches for signal bits.
|
|
static kmem_cache_t *sigqueue_cachep;
|
|
|
|
/// Contains all stopped process waiting for a continue signal
|
|
static struct wait_queue_head_t stopped_queue;
|
|
|
|
static const char *sys_siglist[] = {
|
|
"HUP",
|
|
"INT",
|
|
"QUIT",
|
|
"ILL",
|
|
"TRAP",
|
|
"ABRT",
|
|
"EMT",
|
|
"FPE",
|
|
"KILL",
|
|
"BUS",
|
|
"SEGV",
|
|
"SYS",
|
|
"PIPE",
|
|
"ALRM",
|
|
"TERM",
|
|
"USR1",
|
|
"USR2",
|
|
"CHLD",
|
|
"PWR",
|
|
"WINCH",
|
|
"URG",
|
|
"POLL",
|
|
"STOP",
|
|
"TSTP",
|
|
"CONT",
|
|
"TTIN",
|
|
"TTOU",
|
|
"VTALRM",
|
|
"PROF",
|
|
"XCPU",
|
|
"XFSZ",
|
|
NULL,
|
|
};
|
|
|
|
static inline void __copy_siginfo(siginfo_t *to, const siginfo_t *from)
|
|
{
|
|
memcpy(to, from, sizeof(*to));
|
|
}
|
|
|
|
static inline void __clear_siginfo(siginfo_t *info)
|
|
{
|
|
memset(info, 0, sizeof(*info));
|
|
}
|
|
|
|
static inline void __lock_task_sighand(struct task_struct *t)
|
|
{
|
|
assert(t && "Null task struct.");
|
|
spinlock_lock(&t->sighand.siglock);
|
|
}
|
|
|
|
static inline void __unlock_task_sighand(struct task_struct *t)
|
|
{
|
|
assert(t && "Null task struct.");
|
|
spinlock_unlock(&t->sighand.siglock);
|
|
}
|
|
|
|
static sighandler_t __get_handler(struct task_struct *t, int sig)
|
|
{
|
|
assert(t && "Null task struct.");
|
|
return t->sighand.action[sig - 1].sa_handler;
|
|
}
|
|
|
|
static int __sig_is_ignored(struct task_struct *t, int sig)
|
|
{
|
|
// Blocked signals are never ignored, since the
|
|
// signal handler may change by the time it is
|
|
// unblocked.
|
|
if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig))
|
|
return 0;
|
|
// Get the signal handler.
|
|
sighandler_t handler = __get_handler(t, sig);
|
|
// Check the type of the handler.
|
|
return (handler == SIG_IGN) && (sig != SIGCHLD);
|
|
// TODO: do_signal() specifically checks if the handler is IGN and the signal
|
|
// is SIGCHLD, in that case it forces a wait for the parent, that's why
|
|
// here I'm also accepting as not-ignored a SIG_IGN which is a SIGCHLD.
|
|
}
|
|
|
|
/// @brief Allocate a new signal queue record.
|
|
/// @param t The task to which the signal belongs.
|
|
/// @param sig The signal to set.
|
|
/// @param flags Flags identifying from where we are going to take the memory.
|
|
static sigqueue_t *__sigqueue_alloc(struct task_struct *t, int sig, gfp_t flags)
|
|
{
|
|
sigqueue_t *q = NULL;
|
|
if ((q = kmem_cache_alloc(sigqueue_cachep, flags)) == NULL)
|
|
return NULL;
|
|
// Initiliaze the values.
|
|
q->flags = 0;
|
|
list_head_init(&q->list);
|
|
return q;
|
|
}
|
|
|
|
static void __sigqueue_free(sigqueue_t *q)
|
|
{
|
|
if (q)
|
|
kmem_cache_free(q);
|
|
}
|
|
|
|
/// @brief
|
|
/// @param sig Signal to be sent.
|
|
/// @param info The signal info
|
|
/// @param t The process to which we send the signal.
|
|
/// @return
|
|
static int __send_signal(int sig, siginfo_t *info, struct task_struct *t)
|
|
{
|
|
// Lock the signal handling for the given task.
|
|
__lock_task_sighand(t);
|
|
pr_debug("Trying to add signal (%2d)`%s` to task (%2d)`%s`, currently pending `%d, %d`.\n",
|
|
sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]);
|
|
// Check if the signal is ignored.
|
|
if (__sig_is_ignored(t, sig)) {
|
|
pr_debug("Trying to send signal (%2d)`%s` to task (%2d)`%s`: ignored.\n",
|
|
sig, strsignal(sig), t->pid, t->name);
|
|
__unlock_task_sighand(t);
|
|
return 0;
|
|
}
|
|
// Check if the process is in an invalid status.
|
|
if ((t->state == EXIT_ZOMBIE) || (t->state == EXIT_DEAD)) {
|
|
pr_debug("Trying to send signal (%2d)`%s` to task (%2d)`%s`: zombie or dead.\n",
|
|
sig, strsignal(sig), t->pid, t->name);
|
|
__unlock_task_sighand(t);
|
|
return -EINVAL;
|
|
}
|
|
sigqueue_t *q = __sigqueue_alloc(t, sig, GFP_KERNEL);
|
|
if (q == NULL) {
|
|
__unlock_task_sighand(t);
|
|
return -EAGAIN;
|
|
}
|
|
list_head_add_tail(&q->list, &t->pending.list);
|
|
if (info != SEND_SIG_NOINFO)
|
|
memcpy(&q->info, info, sizeof(siginfo_t));
|
|
// Set that there is a signal pending.
|
|
sigaddset(&t->pending.signal, sig);
|
|
pr_debug("Added pending signal (%2d)`%s` to task (%2d)`%s`, pending `%d, %d`.\n",
|
|
sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]);
|
|
__unlock_task_sighand(t);
|
|
return 0;
|
|
}
|
|
|
|
static inline int __next_signal(sigpending_t *pending, sigset_t *mask)
|
|
{
|
|
pr_debug("__next_signal(%p, %p)\n", pending, mask);
|
|
assert(pending && "Null `pending` structure.");
|
|
assert(mask && "Null `mask` structure.");
|
|
unsigned long x;
|
|
if ((x = bitmask_clear(pending->signal.sig[0], mask->sig[0])) != 0)
|
|
return 1 + find_first_non_zero(x);
|
|
if ((x = bitmask_clear(pending->signal.sig[1], mask->sig[1])) != 0)
|
|
return 33 + find_first_non_zero(x);
|
|
return 0;
|
|
}
|
|
|
|
static inline void __collect_signal(int sig, sigpending_t *list, siginfo_t *info)
|
|
{
|
|
pr_debug("__collect_signal(%d, %p, %p)\n", sig, list, info);
|
|
assert(list && "Null `list` structure.");
|
|
assert(info && "Null `info` structure.");
|
|
|
|
sigqueue_t *queue_entry = NULL;
|
|
bool_t still_pending = false;
|
|
// Collect the siginfo appropriate to this signal. Check if
|
|
// there is another siginfo for the same signal.
|
|
list_for_each_decl(it, &list->list)
|
|
{
|
|
sigqueue_t *q = list_entry(it, sigqueue_t, list);
|
|
pr_debug("__collect_signal(%d, %p, %p) : Signal in queue : %p(%d : %s).\n", sig, list, info,
|
|
q, q->info.si_signo, strsignal(q->info.si_signo));
|
|
if (q->info.si_signo == sig) {
|
|
// If the entry is already set, this means that there are several handlers
|
|
// pending for this particular signal.
|
|
if (queue_entry) {
|
|
pr_debug("__collect_signal(%d, %p, %p) : Still pending, do not remove from set.\n", sig, list, info);
|
|
still_pending = true;
|
|
break;
|
|
}
|
|
// Store the entry we encounter.
|
|
queue_entry = q;
|
|
}
|
|
}
|
|
// If there are no other signals pending of the same type,
|
|
// remove the signal from the set.
|
|
if (!still_pending) {
|
|
sigdelset(&list->signal, sig);
|
|
pr_debug("__collect_signal(%d, %p, %p) : Remove signal from set: %d.\n", sig, list, info,
|
|
list->signal.sig[0]);
|
|
}
|
|
// If we have found an entry.
|
|
if (queue_entry) {
|
|
pr_debug("__collect_signal(%d, %p, %p) : Remove and delete sigqueue entry : %p.\n", sig, list, info, queue_entry);
|
|
// Remove the entry from the queue.
|
|
list_head_del(&queue_entry->list);
|
|
// Copy the details about the entry inside the info structure.
|
|
__copy_siginfo(info, &queue_entry->info);
|
|
// Free the memory for the queue entry.
|
|
__sigqueue_free(queue_entry);
|
|
} else {
|
|
pr_debug("__collect_signal(%d, %p, %p) : Cannot find the signal in the queue.\n", sig, list, info);
|
|
// Ok, it wasn't in the queue, zero out the info.
|
|
__clear_siginfo(info);
|
|
// Get the current process.
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
assert(current && "There is no running process.");
|
|
// Initialize the info.
|
|
info->si_signo = sig;
|
|
info->si_code = SI_USER;
|
|
info->si_value.sival_int = 0;
|
|
info->si_errno = 0;
|
|
info->si_pid = current->pid;
|
|
info->si_uid = current->uid;
|
|
info->si_addr = NULL;
|
|
info->si_status = 0;
|
|
info->si_band = 0;
|
|
}
|
|
}
|
|
|
|
static inline int __dequeue_signal(sigpending_t *pending, sigset_t *mask, siginfo_t *info)
|
|
{
|
|
pr_debug("__dequeue_signal(%p, %p, %p)\n", pending, mask, info);
|
|
// The dequeue_signal( ) always considers the lowest-numbered pending signal.
|
|
// It updates the data structures to indicate that the signal is no longer
|
|
// pending and returns its number.
|
|
int sig = __next_signal(pending, mask);
|
|
if ((sig > 0) && (sig < NSIG)) {
|
|
__collect_signal(sig, pending, info);
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
static inline int __handle_signal(int signr, siginfo_t *info, sigaction_t *ka, struct pt_regs *regs)
|
|
{
|
|
pr_debug("__handle_signal(%d, %p, %p, %p)\n", signr, info, ka, regs);
|
|
// The do_signal() function is usually only invoked when the CPU is going
|
|
// to return in User Mode.
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
assert(current && "There is no running process.");
|
|
// Skip the `init` process, always.
|
|
if (current->pid == 1) {
|
|
errno = ESRCH;
|
|
return 0;
|
|
}
|
|
// Save the previous signal mask.
|
|
memcpy(¤t->saved_sigmask, ¤t->blocked, sizeof(sigset_t));
|
|
|
|
// Add the signal to the list of blocked signals.
|
|
sigaddset(¤t->blocked, signr);
|
|
|
|
// Store the registers before setting the ones required by the signal handling.
|
|
current->thread.signal_regs = *regs;
|
|
|
|
// Restore the registers for the process that has set the signal.
|
|
*regs = current->thread.regs;
|
|
|
|
// Set the instruction pointer.
|
|
regs->eip = (uintptr_t)ka->sa_handler;
|
|
|
|
// If the user is also asking for the signal info, push it into the stack.
|
|
if (bitmask_check(ka->sa_flags, SA_SIGINFO)) {
|
|
// Move the stack so that we have space for storing the siginfo.
|
|
regs->useresp -= sizeof(siginfo_t);
|
|
// Save the pointer where the siginfo is stored.
|
|
siginfo_t *siginfo_addr = (siginfo_t *)regs->useresp;
|
|
// We push on the stack the entire siginfo.
|
|
__copy_siginfo(siginfo_addr, info);
|
|
// We push on the stack the pointer to the siginfo we copied on the stack.
|
|
PUSH_VALUE_ON_STACK(regs->useresp, siginfo_addr);
|
|
}
|
|
|
|
// Push on the stack the signal number, first and only argument of the handler.
|
|
PUSH_VALUE_ON_STACK(regs->useresp, signr);
|
|
|
|
// Push on the stack the function required to handle the signal return.
|
|
PUSH_VALUE_ON_STACK(regs->useresp, current->sigreturn_eip);
|
|
|
|
return 1;
|
|
}
|
|
|
|
long sys_sigreturn(struct pt_regs *f)
|
|
{
|
|
pr_debug("sys_sigreturn(%p)\n", f);
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
assert(current && "There is no running process.");
|
|
// Restore the registers before the signal handling.
|
|
*f = current->thread.signal_regs;
|
|
// Restore the previous signal mask.
|
|
memcpy(¤t->blocked, ¤t->saved_sigmask, sizeof(sigset_t));
|
|
// Switch to process page directory
|
|
paging_switch_directory_va(current->mm->pgd);
|
|
pr_debug("sys_sigreturn(%p) : done!\n", f);
|
|
return 0;
|
|
}
|
|
|
|
// Send signal to parent
|
|
static int __notify_parent(struct task_struct *current, int signr)
|
|
{
|
|
siginfo_t info;
|
|
info.si_signo = signr;
|
|
info.si_code = SI_KERNEL;
|
|
info.si_value.sival_int = 0;
|
|
info.si_errno = 0;
|
|
info.si_pid = current->pid;
|
|
info.si_uid = current->uid;
|
|
info.si_addr = NULL;
|
|
info.si_status = 0;
|
|
info.si_band = 0;
|
|
return __send_signal(signr, &info, current->parent);
|
|
}
|
|
|
|
// Removes from the pending signal queue q the pending signals corresponding to
|
|
// the bit mask mask
|
|
static void __rm_from_queue(sigset_t *mask, sigpending_t *q)
|
|
{
|
|
list_head *it, *tmp;
|
|
list_for_each_safe (it, tmp, &q->list) {
|
|
struct sigqueue_t *entry = list_entry(it, struct sigqueue_t, list);
|
|
int sig = entry->info.si_signo;
|
|
|
|
if (sigismember(mask, sig)) {
|
|
list_head_del(it);
|
|
kfree(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We do not consider group stopping because for now we don't have thread groups
|
|
static void __do_signal_stop(struct task_struct *current, struct pt_regs *f, int signr)
|
|
{
|
|
// The do_signal( ) function also sends a SIGCHLD signal to
|
|
// the parent process of current, unless the parent has set
|
|
// the SA_NOCLDSTOP flag of SIGCHLD.
|
|
if (!(SA_NOCLDSTOP & current->parent->sighand.action[SIGCHLD - 1].sa_flags))
|
|
if (__notify_parent(current, SIGCHLD) != 0)
|
|
pr_debug("Failed to notify parent with signal: %d", signr);
|
|
|
|
// The state is now TASK_UNINTERRUPTABLE
|
|
sleep_on(&stopped_queue);
|
|
|
|
current->state = TASK_STOPPED;
|
|
current->exit_code = signr;
|
|
|
|
scheduler_run(f);
|
|
}
|
|
|
|
int do_signal(struct pt_regs *f)
|
|
{
|
|
// The do_signal() function is usually only invoked when the CPU is going
|
|
// to return in User Mode.
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
if (current == NULL)
|
|
return 0;
|
|
|
|
// First, checks whether the function itself was triggered by an interrupt;
|
|
// if so, it simply returns. Otherwise, if the function was triggered by an
|
|
// exception that was raised while the process was running in User Mode,
|
|
// the function continues executing.
|
|
if ((f->cs & 3) != 3)
|
|
return 0;
|
|
|
|
// Create a siginfo.
|
|
siginfo_t info;
|
|
// The return code of __dequeue_signal( ) is stored in signr.
|
|
int signr, exit_code;
|
|
|
|
// Lock the signal handling for the given task.
|
|
__lock_task_sighand(current);
|
|
|
|
// The heart of the do_signal( ) function consists of a loop that
|
|
// repeatedly invokes the __dequeue_signal( ) function until no
|
|
// non-blocked pending signals are left.
|
|
while (!list_head_empty(¤t->pending.list)) {
|
|
// Get the signal to deliver.
|
|
signr = exit_code = __dequeue_signal(¤t->pending, ¤t->blocked, &info);
|
|
|
|
// Check the signal that we want to send.
|
|
if ((signr < 0) || (signr >= NSIG)) {
|
|
pr_err("Wrong signal number!\n");
|
|
break;
|
|
}
|
|
|
|
// If its value is 0, it means that all pending signals have been
|
|
// handled and do_signal( ) can finish.
|
|
if (signr == 0) {
|
|
pr_notice("There are no more signals to handle.\n");
|
|
__unlock_task_sighand(current);
|
|
return 0;
|
|
}
|
|
|
|
// Get the associated signal action.
|
|
sigaction_t *ka = ¤t->sighand.action[signr - 1];
|
|
|
|
// The only exception comes when the receiving process is init, in
|
|
// which case the signal is discarded.
|
|
if (current->pid == 1)
|
|
continue;
|
|
|
|
// When a delivered signal is explicitly ignored, the do_signal( )
|
|
// function normally just continues with a new execution of the loop
|
|
// and therefore considers another pending signal.
|
|
if (ka->sa_handler == SIG_IGN) {
|
|
if (signr == SIGCHLD)
|
|
while (sys_waitpid(-1, NULL, WNOHANG) > 0) {}
|
|
continue;
|
|
}
|
|
|
|
// When a delivered signal is the default one, do_signal( ) must
|
|
// perform the default action of the signal.
|
|
if (ka->sa_handler == SIG_DFL) {
|
|
// For other processes, since the default action depends on the
|
|
// type of signal, the function executes a switch statement based
|
|
// on the value of signr.
|
|
switch (signr) {
|
|
// The signals whose default action is "ignore" are easily handled:
|
|
case SIGCONT:
|
|
case SIGCHLD:
|
|
case SIGURG:
|
|
case SIGWINCH:
|
|
continue;
|
|
// The signals whose default action is "stop" may stop the
|
|
// current process. To do this, do_signal( ) sets the state
|
|
// of current to TASK_STOPPED and then invokes the schedule( )
|
|
// function (see Section 11.2.2).
|
|
// The difference between SIGSTOP and the other signals is:
|
|
// SIGSTOP always stops the process;
|
|
// The other signals stop the process only if it is not
|
|
// in an "orphaned process group."
|
|
case SIGTSTP:
|
|
case SIGTTIN:
|
|
case SIGTTOU:
|
|
if (is_orphaned_pgrp(current->pgid))
|
|
continue;
|
|
|
|
case SIGSTOP:
|
|
__unlock_task_sighand(current);
|
|
__do_signal_stop(current, f, signr);
|
|
__lock_task_sighand(current);
|
|
|
|
continue;
|
|
case SIGQUIT:
|
|
case SIGILL:
|
|
case SIGTRAP:
|
|
|
|
case SIGABRT:
|
|
sys_exit(3);
|
|
|
|
continue;
|
|
case SIGFPE:
|
|
case SIGSEGV:
|
|
case SIGBUS:
|
|
case SIGSYS:
|
|
case SIGXCPU:
|
|
case SIGXFSZ:
|
|
#if 0
|
|
if (do_coredump(signr, f))
|
|
exit_code |= 0x80;
|
|
#endif
|
|
default:
|
|
#if 0
|
|
current->flags |= PF_SIGNALED;
|
|
#endif
|
|
sys_exit(exit_code);
|
|
__unlock_task_sighand(current);
|
|
return 1;
|
|
}
|
|
}
|
|
if (__handle_signal(signr, &info, ka, f) == 1) {
|
|
__unlock_task_sighand(current);
|
|
return 1;
|
|
}
|
|
pr_emerg("Failed to handle signal.\n");
|
|
}
|
|
__unlock_task_sighand(current);
|
|
return 0;
|
|
}
|
|
|
|
int signals_init()
|
|
{
|
|
if ((sigqueue_cachep = KMEM_CREATE(sigqueue_t)) == NULL) {
|
|
pr_emerg("Failed to allocate cache for signals.\n");
|
|
return 0;
|
|
}
|
|
|
|
list_head_init(&stopped_queue.task_list);
|
|
return 1;
|
|
}
|
|
|
|
/// @brief Checks for some types of signals that might nullify other pending
|
|
/// signals for the destination thread group
|
|
/// @param sig Signal number
|
|
/// @param info siginfo struct of the signal
|
|
/// @param p Target process of the signal
|
|
void handle_stop_signal(int sig, siginfo_t *info, struct task_struct *p)
|
|
{
|
|
// remove the SIGCONT signal from the shared
|
|
// pending signal queue p->signal->shared_pending and from the private
|
|
// queues of all members of the thread group.
|
|
if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) {
|
|
// TODO: shared and thread group
|
|
|
|
sigset_t mask;
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGCONT);
|
|
|
|
__rm_from_queue(&mask, &p->pending);
|
|
}
|
|
|
|
// remove any SIGSTOP, SIGTSTP, SIGTTIN, and SIGTTOU signal from the shared pending signal queue p->signal->shared_pending;
|
|
// then, removes the same signals from the private pending signal queues of the processes belonging to the thread
|
|
// group, and awakens them
|
|
if (sig == SIGCONT) {
|
|
sigset_t mask;
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGSTOP);
|
|
sigaddset(&mask, SIGTSTP);
|
|
sigaddset(&mask, SIGTTIN);
|
|
sigaddset(&mask, SIGTTOU);
|
|
|
|
__rm_from_queue(&mask, &p->pending);
|
|
|
|
struct list_head *it, *tmp;
|
|
list_for_each_safe (it, tmp, &stopped_queue.task_list) {
|
|
struct wait_queue_entry_t *entry = list_entry(it, struct wait_queue_entry_t, task_list);
|
|
|
|
// Select only the waiting entry for the timer task pid
|
|
task_struct *task = entry->task;
|
|
if (task->pid == p->pid) {
|
|
// Executed entry's wakeup test function
|
|
int res = entry->func(entry, 0, 0);
|
|
if (res == 1) {
|
|
// Removes entry from list and memory
|
|
remove_wait_queue(&stopped_queue, entry);
|
|
kfree(entry);
|
|
|
|
pr_debug("Process (pid: %d) restored from stop\n", p->pid);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// @brief Send siginfo to target process
|
|
/// @param sig Signal number
|
|
/// @param info siginfo struct of the signal to be sent
|
|
/// @param p Target process where the signal will be sent
|
|
/// @return Returns 0 if there is no error, otherwise returns an error code
|
|
int __send_sig_info(int sig, siginfo_t *info, struct task_struct *p)
|
|
{
|
|
if (sig < 0 || sig > NSIG)
|
|
return -EINVAL;
|
|
|
|
// If the signal is being sent by a User Mode process,
|
|
// it checks whether the operation is allowed.
|
|
if (info->si_code == SI_USER) {
|
|
// TODO
|
|
}
|
|
|
|
// If the sig parameter has the value 0,
|
|
// it returns immediately without generating any signal
|
|
if (!sig)
|
|
return 0;
|
|
|
|
__lock_task_sighand(p);
|
|
|
|
// Checks for some types of signals that might nullify other pending
|
|
// signals for the destination thread group
|
|
handle_stop_signal(sig, info, p);
|
|
|
|
#if 0
|
|
// Checks whether the signal is non-real-time and another occurrence of the same
|
|
// signal is already pending in the shared pending signal queue of the thread group
|
|
if (sig < 32 && sigismember(&p->signal->shared_pending.signal,sig))
|
|
return 0;
|
|
#endif
|
|
|
|
__unlock_task_sighand(p);
|
|
__send_signal(sig, info, p);
|
|
return 0;
|
|
}
|
|
|
|
int sys_kill(pid_t pid, int sig)
|
|
{
|
|
pr_debug("sys_kill(%d, %d)\n", pid, sig);
|
|
struct task_struct *current = scheduler_get_running_process(pid);
|
|
// Check the task associated with the pid.
|
|
if (!current)
|
|
return -ESRCH;
|
|
// Check the signal that we want to send.
|
|
if ((sig < 0) || (sig >= NSIG))
|
|
return -EINVAL;
|
|
siginfo_t info;
|
|
info.si_signo = sig;
|
|
info.si_code = SI_USER;
|
|
info.si_value.sival_int = 0;
|
|
info.si_errno = 0;
|
|
info.si_pid = current->pid;
|
|
info.si_uid = current->uid;
|
|
info.si_addr = NULL;
|
|
info.si_status = 0;
|
|
info.si_band = 0;
|
|
return __send_sig_info(sig, &info, current);
|
|
}
|
|
|
|
sighandler_t sys_signal(int signum, sighandler_t handler)
|
|
{
|
|
pr_notice("sys_signal(%d, %p)\n", signum, handler);
|
|
// Check the signal that we want to send.
|
|
if ((signum < 0) || (signum >= NSIG)) {
|
|
pr_err("sys_signal(%d, %p): Wrong signal number!\n", signum, handler);
|
|
return SIG_ERR;
|
|
}
|
|
// The do_signal() function is usually only invoked when the CPU is going
|
|
// to return in User Mode.
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
assert(current && "There is no running process.");
|
|
// Skip the `init` process, always.
|
|
if (current->pid == 1) {
|
|
pr_err("sys_signal(%d, %p): Cannot signal number!\n", signum, handler);
|
|
return SIG_ERR;
|
|
}
|
|
// Create a new signal action.
|
|
sigaction_t new_sigaction;
|
|
// Set the handler.
|
|
new_sigaction.sa_handler = handler;
|
|
// Set the handler.
|
|
new_sigaction.sa_flags = SA_RESETHAND | SA_NODEFER;
|
|
// Reset the set for the signal action.
|
|
sigemptyset(&new_sigaction.sa_mask);
|
|
// Lock the signal handling for the given task.
|
|
__lock_task_sighand(current);
|
|
// Get the old sigaction.
|
|
sigaction_t *old_sigaction = ¤t->sighand.action[signum - 1];
|
|
pr_err("sys_signal(%d, %p): Signal action ptr %p\n", signum, handler, old_sigaction);
|
|
pr_err("sys_signal(%d, %p): Old signal handler %p\n", signum, handler, old_sigaction->sa_handler);
|
|
// Get the old handler (to return).
|
|
sighandler_t old_handler = current->sighand.action[signum - 1].sa_handler;
|
|
// Set the new action.
|
|
memcpy(old_sigaction, &new_sigaction, sizeof(sigaction_t));
|
|
// Unlock the signal handling for the given task.
|
|
__unlock_task_sighand(current);
|
|
// Return the old sighandler.
|
|
return old_handler;
|
|
}
|
|
|
|
int sys_sigaction(int signum, const sigaction_t *act, sigaction_t *oldact)
|
|
{
|
|
pr_debug("sys_sigaction(%d, %p, %p)\n", signum, act, oldact);
|
|
// Check the signal that we want to send.
|
|
if ((signum < 0) || (signum >= NSIG)) {
|
|
pr_debug("sys_sigaction(%d, %p, %p): Wrong signal number!\n", signum, act, oldact);
|
|
return -EINVAL;
|
|
}
|
|
// The do_signal() function is usually only invoked when the CPU is going
|
|
// to return in User Mode.
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
assert(current && "There is no running process.");
|
|
// Skip the `init` process, always.
|
|
if (current->pid == 1) {
|
|
pr_debug("sys_sigaction(%d, %p, %p): Cannot set signal for init!\n", signum, act, oldact);
|
|
return -EINVAL;
|
|
}
|
|
// Lock the signal handling for the given task.
|
|
__lock_task_sighand(current);
|
|
// Get a pointer to the entry in the sighand.action array.
|
|
sigaction_t *current_sigaction = ¤t->sighand.action[signum - 1];
|
|
pr_debug("sys_sigaction(%d, %p, %p): : Signal old action ptr %p\n", signum, act, oldact, current_sigaction);
|
|
// If requested, get the old sigaction.
|
|
if (oldact) {
|
|
memcpy(oldact, current_sigaction, sizeof(sigaction_t));
|
|
}
|
|
// Set the new action.
|
|
memcpy(current_sigaction, act, sizeof(sigaction_t));
|
|
// Unlock the signal handling for the given task.
|
|
__unlock_task_sighand(current);
|
|
// Return the old sighandler.
|
|
return 0;
|
|
}
|
|
|
|
int sys_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
|
|
{
|
|
pr_notice("sys_sigprocmask(%d, %p, %p)\n", how, set, oldset);
|
|
if (!set && !oldset) {
|
|
return -EFAULT;
|
|
}
|
|
if ((how < SIG_BLOCK) || (how > SIG_SETMASK)) {
|
|
return -EINVAL;
|
|
}
|
|
// The do_signal() function is usually only invoked when the CPU is going
|
|
// to return in User Mode.
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
assert(current && "There is no running process.");
|
|
// Skip the `init` process, always.
|
|
if (current->pid == 1) {
|
|
pr_notice("sys_sigprocmask(%d, %p, %p): Cannot set signal for init!\n", how, set, oldset);
|
|
return -EINVAL;
|
|
}
|
|
// If `oldset` is not, return the old set.
|
|
if (oldset) {
|
|
oldset->sig[0] = current->blocked.sig[0];
|
|
oldset->sig[1] = current->blocked.sig[1];
|
|
}
|
|
// Set the new signal mask.
|
|
if (set) {
|
|
if (how == SIG_BLOCK) {
|
|
// The set of blocked signals is the union of the current set
|
|
// and the set argument.
|
|
current->blocked.sig[0] |= set->sig[0];
|
|
current->blocked.sig[1] |= set->sig[1];
|
|
} else if (how == SIG_UNBLOCK) {
|
|
// The signals in set are removed from the current set of
|
|
// blocked signals. It is permissible to attempt to unblock
|
|
// a signal which is not blocked.
|
|
current->blocked.sig[0] &= ~(set->sig[0]);
|
|
current->blocked.sig[1] &= ~(set->sig[1]);
|
|
} else if (how == SIG_SETMASK) {
|
|
// The set of blocked signals is set to the argument set.
|
|
current->blocked.sig[0] = set->sig[0];
|
|
current->blocked.sig[1] = set->sig[1];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *strsignal(int sig)
|
|
{
|
|
if ((sig >= SIGHUP) && (sig < NSIG))
|
|
return sys_siglist[sig - 1];
|
|
return NULL;
|
|
}
|
|
|
|
int sigemptyset(sigset_t *set)
|
|
{
|
|
if (set) {
|
|
set->sig[0] = 0;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int sigfillset(sigset_t *set)
|
|
{
|
|
if (set) {
|
|
set->sig[0] = ~0UL;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int sigaddset(sigset_t *set, int signum)
|
|
{
|
|
if (set && ((signum))) {
|
|
bit_set_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32));
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int sigdelset(sigset_t *set, int signum)
|
|
{
|
|
if (set) {
|
|
bit_clear_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32));
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int sigismember(sigset_t *set, int signum)
|
|
{
|
|
if (set)
|
|
return bit_check(set->sig[(signum - 1) / 32], (signum - 1) % 32);
|
|
return -1;
|
|
}
|