Nucleus
Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
* This file implements the Signals namespace and the associated signal
* functions. It registers and dispatches signals to tasks and allows them to
* register signal handlers, which it runs when appropriate.
*/
#include <sys/errno.h>
#include <sys/signal.h>
#include <nucleus/memory.h>
#include <nucleus/object.h>
#include <nucleus/task.h>
/* Signals namespace */
struct Signals {
Object obj;
sighandler_t sig_handler[32];
sigset_t sigset;
sigset_t blocked;
};
static void signals_copy(Object *, Object *);
/* Signals object type */
ObjectType signalsType = {
.name = "SIGNALS",
.size = sizeof(Signals),
.copy = signals_copy,
};
/* Copy a Signals object */
static void
signals_copy(Object *a, Object *b)
{
Signals *sa = (void *) a, *sb = (void *) b;
int i;
for (i = 0; i < 32; i++)
sb->sig_handler[i] = sa->sig_handler[i];
sb->sigset = sa->sigset;
sb->blocked = sa->blocked;
}
/* Send a signal to a task */
void
send_signal(Task *target, int sig)
{
target->signals->sigset |= (1 << (sig - 1));
}
/* Send a signal to a thread */
int
tgkill(pid_t tgid, pid_t tid, int sig)
{
if (sig < 0 || sig > 32)
return -EINVAL;
Task *task = find_task(tid);
if (!task || task->tgid != tgid)
return -ESRCH;
if (sig)
send_signal(task, sig);
return 0;
}
/* Send a signal to a process */
int
kill(pid_t pid, int sig)
{
if (sig < 0 || sig > 32)
return -EINVAL;
size_t sent = 0;
Task *task;
foreach (taskType.objects, task) {
if (task->tgid != pid)
continue;
if (sig)
send_signal(task, sig);
sent++;
}
return sent ? 0 : -ESRCH;
}
/* Install a signal handler */
sighandler_t
signal(int signum, sighandler_t handler)
{
if (signum <= 0 || signum > 32)
return (sighandler_t) -EINVAL;
if (!verify_access(handler, 8, PROT_EXEC))
return (sighandler_t) -EFAULT;
sighandler_t old = current->signals->sig_handler[signum - 1];
current->signals->sig_handler[signum - 1] = handler;
return old;
}
/* Examine and change blocked signals */
int
sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
{
if (set && !verify_access(set, sizeof(sigset_t), PROT_READ))
return -EFAULT;
if (oldset && !verify_access(oldset, sizeof(sigset_t), PROT_WRITE))
return -EFAULT;
if (oldset)
*oldset = current->signals->blocked;
if (!set)
return 0;
switch (how) {
case SIG_BLOCK:
current->signals->blocked |= *set;
return 0;
case SIG_UNBLOCK:
current->signals->blocked &= ~*set;
return 0;
case SIG_SETMASK:
current->signals->blocked = *set;
return 0;
default:
return -EINVAL;
}
}