diff --git a/apps/plasma.c b/apps/plasma.c index c27a5028..51cfeaf1 100644 --- a/apps/plasma.c +++ b/apps/plasma.c @@ -37,9 +37,12 @@ uint16_t off_y; static int volatile draw_lock = 0; gfx_context_t * ctx; +pthread_t thread; void sigint_handler() { should_exit = 1; + pthread_join(thread, NULL); + exit(1); } void redraw_borders() { @@ -145,7 +148,6 @@ int main (int argc, char ** argv) { yutani_window_advertise_icon(yctx, wina, "Plasma", "plasma"); - pthread_t thread; pthread_create(&thread, NULL, draw_thread, NULL); signal(SIGINT, sigint_handler); diff --git a/apps/strace.c b/apps/strace.c index f0a7e14d..bc4a90e2 100644 --- a/apps/strace.c +++ b/apps/strace.c @@ -772,6 +772,7 @@ static void handle_syscall(pid_t pid, struct regs * r) { case SYS_GETTIMEOFDAY: /* two output args */ break; + case SYS_SIGACTION: break; /* These have no arguments: */ case SYS_YIELD: case SYS_FORK: diff --git a/base/usr/include/kernel/process.h b/base/usr/include/kernel/process.h index c8591ace..ef0a36c2 100644 --- a/base/usr/include/kernel/process.h +++ b/base/usr/include/kernel/process.h @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef __x86_64__ #include @@ -73,6 +74,12 @@ typedef struct file_descriptors { spin_lock_t lock; } fd_table_t; +struct signal_config { + uintptr_t handler; + sigset_t mask; + int flags; +}; + #define PROC_FLAG_IS_TASKLET 0x01 #define PROC_FLAG_FINISHED 0x02 #define PROC_FLAG_STARTED 0x04 @@ -114,7 +121,6 @@ typedef struct process { list_t * wait_queue; list_t * shm_mappings; list_t * node_waits; - list_t * signal_queue; node_t sched_node; node_t sleep_node; @@ -125,12 +131,14 @@ typedef struct process { int awoken_index; thread_t thread; - thread_t signal_state; image_t image; spin_lock_t sched_lock; - uintptr_t signals[NUMSIGNALS+1]; + struct signal_config signals[NUMSIGNALS+1]; + sigset_t blocked_signals; + sigset_t pending_signals; + sigset_t active_signals; int supplementary_group_count; gid_t * supplementary_group_list; @@ -273,5 +281,5 @@ extern void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char __attribute__((noreturn)) extern void arch_enter_signal_handler(uintptr_t,int,struct regs*); extern void arch_wakeup_others(void); -extern void arch_return_from_signal_handler(struct regs *r); +extern int arch_return_from_signal_handler(struct regs *r); diff --git a/base/usr/include/signal.h b/base/usr/include/signal.h index f446c4cb..4be48961 100644 --- a/base/usr/include/signal.h +++ b/base/usr/include/signal.h @@ -16,5 +16,7 @@ typedef int sig_atomic_t; extern sighandler_t signal(int signum, sighandler_t handler); extern int raise(int sig); +extern int sigaction(int signum, struct sigaction *act, struct sigaction *oldact); +extern int sigemptyset(sigset_t *); _End_C_Header diff --git a/base/usr/include/sys/signal.h b/base/usr/include/sys/signal.h index 1606dd16..333b00dd 100644 --- a/base/usr/include/sys/signal.h +++ b/base/usr/include/sys/signal.h @@ -16,6 +16,9 @@ _Begin_C_Header #define SA_NOCLDSTOP 1 #define SA_SIGINFO 2 +#define SA_NODEFER 4 +#define SA_RESETHAND 8 +#define SA_RESTART 16 #if 0 #define SIG_SETMASK 0 diff --git a/base/usr/include/syscall_nums.h b/base/usr/include/syscall_nums.h index ec2464ae..3122d5d3 100644 --- a/base/usr/include/syscall_nums.h +++ b/base/usr/include/syscall_nums.h @@ -73,3 +73,4 @@ #define SYS_SETGROUPS 70 #define SYS_TIMES 71 #define SYS_SETTIMEOFDAY 72 +#define SYS_SIGACTION 73 diff --git a/kernel/arch/aarch64/arch.c b/kernel/arch/aarch64/arch.c index f0a09b72..d9029052 100644 --- a/kernel/arch/aarch64/arch.c +++ b/kernel/arch/aarch64/arch.c @@ -69,7 +69,7 @@ static void _kill_it(uintptr_t addr, const char * action, const char * desc, siz stack += sizeof(type); \ } while (0) -void arch_return_from_signal_handler(struct regs *r) { +int arch_return_from_signal_handler(struct regs *r) { uintptr_t spsr; uintptr_t sp = r->user_sp; @@ -81,6 +81,10 @@ void arch_return_from_signal_handler(struct regs *r) { } arch_restore_floating((process_t*)this_core->current_process); + POP(sp, sigset_t, this_core->current_process->blocked_signals); + long originalSignal; + POP(sp, long, originalSignal); + /* Interrupt system call status */ POP(sp, long, this_core->current_process->interrupted_system_call); @@ -95,6 +99,7 @@ void arch_return_from_signal_handler(struct regs *r) { POP(sp, struct regs, *r); asm volatile ("msr SP_EL0, %0" :: "r"(r->user_sp)); + return originalSignal; } /** @@ -126,6 +131,12 @@ void arch_enter_signal_handler(uintptr_t entrypoint, int signum, struct regs *r) PUSH(sp, long, this_core->current_process->interrupted_system_call); this_core->current_process->interrupted_system_call = 0; + PUSH(sp, long, signum); + PUSH(sp, sigset_t, this_core->current_process->blocked_signals); + + struct signal_config * config = (struct signal_config*)&this_core->current_process->signals[signum]; + this_core->current_process->blocked_signals |= config->mask | (1 << signum); + /* Save floating point */ arch_save_floating((process_t*)this_core->current_process); for (int i = 0; i < 64; ++i) { diff --git a/kernel/arch/x86_64/user.c b/kernel/arch/x86_64/user.c index 6e05391b..3bd93e28 100644 --- a/kernel/arch/x86_64/user.c +++ b/kernel/arch/x86_64/user.c @@ -68,7 +68,7 @@ static void _kill_it(void) { stack += sizeof(type); \ } while (0) -void arch_return_from_signal_handler(struct regs *r) { +int arch_return_from_signal_handler(struct regs *r) { for (int i = 0; i < 64; ++i) { POP(r->rsp, uint64_t, this_core->current_process->thread.fp_regs[63-i]); @@ -76,6 +76,10 @@ void arch_return_from_signal_handler(struct regs *r) { arch_restore_floating((process_t*)this_core->current_process); + POP(r->rsp, sigset_t, this_core->current_process->blocked_signals); + long originalSignal; + POP(r->rsp, long, originalSignal); + POP(r->rsp, long, this_core->current_process->interrupted_system_call); struct regs out; @@ -91,6 +95,7 @@ void arch_return_from_signal_handler(struct regs *r) { R(rsp); r->rflags = (out.rflags & 0xcd5) | (1 << 21) | (1 << 9) | ((r->rflags & (1 << 8)) ? (1 << 8) : 0); + return originalSignal; } @@ -120,6 +125,12 @@ void arch_enter_signal_handler(uintptr_t entrypoint, int signum, struct regs *r) PUSH(ret.rsp, long, this_core->current_process->interrupted_system_call); this_core->current_process->interrupted_system_call = 0; + PUSH(ret.rsp, long, signum); + PUSH(ret.rsp, sigset_t, this_core->current_process->blocked_signals); + + struct signal_config * config = (struct signal_config*)&this_core->current_process->signals[signum]; + this_core->current_process->blocked_signals |= config->mask | (1 << signum); + arch_save_floating((process_t*)this_core->current_process); for (int i = 0; i < 64; ++i) { PUSH(ret.rsp, uint64_t, this_core->current_process->thread.fp_regs[i]); diff --git a/kernel/sys/process.c b/kernel/sys/process.c index 03cf7edd..c1a6c570 100644 --- a/kernel/sys/process.c +++ b/kernel/sys/process.c @@ -123,11 +123,10 @@ void switch_next(void) { mmu_set_directory(this_core->current_process->thread.page_directory->directory); arch_set_kernel_stack(this_core->current_process->image.stack); - if ((this_core->current_process->flags & PROC_FLAG_FINISHED) || (!this_core->current_process->signal_queue)) { + if (this_core->current_process->flags & PROC_FLAG_FINISHED) { arch_fatal_prepare(); printf("Should not have this process...\n"); if (this_core->current_process->flags & PROC_FLAG_FINISHED) printf("It is marked finished.\n"); - if (!this_core->current_process->signal_queue) printf("It doesn't have a signal queue.\n"); arch_dump_traceback(); arch_fatal(); __builtin_unreachable(); @@ -356,7 +355,6 @@ process_t * spawn_kidle(int bsp) { * Can we make sure these are never referenced and not allocate them? */ idle->wait_queue = list_create("process wait queue (kidle)",idle); idle->shm_mappings = list_create("process shm mappings (kidle)",idle); - idle->signal_queue = list_create("process signal queue (kidle)",idle); gettimeofday(&idle->start, NULL); idle->thread.page_directory = malloc(sizeof(page_directory_t)); idle->thread.page_directory->refcount = 1; @@ -406,7 +404,6 @@ process_t * spawn_init(void) { init->flags = PROC_FLAG_STARTED | PROC_FLAG_RUNNING; init->wait_queue = list_create("process wait queue (init)", init); init->shm_mappings = list_create("process shm mapping (init)", init); - init->signal_queue = list_create("process signal queue (init)", init); init->sched_node.prev = NULL; init->sched_node.next = NULL; @@ -495,7 +492,6 @@ process_t * spawn_process(volatile process_t * parent, int flags) { proc->wait_queue = list_create("process wait queue",proc); proc->shm_mappings = list_create("process shm mappings",proc); - proc->signal_queue = list_create("process signal queue",proc); proc->sched_node.value = proc; proc->sleep_node.value = proc; @@ -1216,8 +1212,6 @@ void task_exit(int retval) { /* free whatever we can */ list_free(this_core->current_process->wait_queue); free(this_core->current_process->wait_queue); - list_free(this_core->current_process->signal_queue); - free(this_core->current_process->signal_queue); free(this_core->current_process->wd_name); if (this_core->current_process->node_waits) { list_free(this_core->current_process->node_waits); @@ -1417,7 +1411,6 @@ process_t * spawn_worker_thread(void (*entrypoint)(void * argp), const char * na proc->wait_queue = list_create("worker thread wait queue",proc); proc->shm_mappings = list_create("worker thread shm mappings",proc); - proc->signal_queue = list_create("worker thread signal queue",proc); proc->sched_node.value = proc; proc->sleep_node.value = proc; diff --git a/kernel/sys/signal.c b/kernel/sys/signal.c index b3f23093..c0b3783b 100644 --- a/kernel/sys/signal.c +++ b/kernel/sys/signal.c @@ -91,11 +91,16 @@ static char sig_defaults[] = { * * @param r Registers after restoration from signal return. */ -static void maybe_restart_system_call(struct regs * r) { +static void maybe_restart_system_call(struct regs * r, int signum) { if (this_core->current_process->interrupted_system_call && arch_syscall_number(r) == -ERESTARTSYS) { - arch_syscall_return(r, this_core->current_process->interrupted_system_call); - this_core->current_process->interrupted_system_call = 0; - syscall_handler(r); + if (sig_defaults[signum] == SIG_DISP_Cont || (this_core->current_process->signals[signum].flags & SA_RESTART)) { + arch_syscall_return(r, this_core->current_process->interrupted_system_call); + this_core->current_process->interrupted_system_call = 0; + syscall_handler(r); + } else { + this_core->current_process->interrupted_system_call = 0; + arch_syscall_return(r, -EINTR); + } } } @@ -119,11 +124,8 @@ static void maybe_restart_system_call(struct regs * r) { * forward to @c arch_enter_signal_handler * @returns 0 if another signal needs to be handled, 1 otherwise. */ -int handle_signal(process_t * proc, signal_t * sig, struct regs *r) { - uintptr_t signum = sig->signum; - free(sig); - - uintptr_t handler = proc->signals[signum]; +int handle_signal(process_t * proc, int signum, struct regs *r) { + struct signal_config config = proc->signals[signum]; /* Are we being traced? */ if (this_core->current_process->flags & PROC_FLAG_TRACE_SIGNALS) { @@ -138,7 +140,7 @@ int handle_signal(process_t * proc, signal_t * sig, struct regs *r) { goto _ignore_signal; } - if (!handler) { + if (!config.handler) { char dowhat = sig_defaults[signum]; if (dowhat == SIG_DISP_Term || dowhat == SIG_DISP_Core) { task_exit(((128 + signum) << 8) | signum); @@ -155,7 +157,7 @@ int handle_signal(process_t * proc, signal_t * sig, struct regs *r) { do { switch_task(0); - } while (!this_core->current_process->signal_queue->length); + } while (!(this_core->current_process->pending_signals & ~this_core->current_process->blocked_signals)); return 0; /* Return and handle another */ } else if (dowhat == SIG_DISP_Cont) { @@ -166,19 +168,21 @@ int handle_signal(process_t * proc, signal_t * sig, struct regs *r) { } /* If the handler value is 1 we treat it as IGN. */ - if (handler == 1) goto _ignore_signal; + if (config.handler == 1) goto _ignore_signal; - proc->signals[signum] = 0; + if (config.flags & SA_RESETHAND) { + proc->signals[signum].handler = 0; + } - arch_enter_signal_handler(handler, signum, r); + arch_enter_signal_handler(config.handler, signum, r); return 1; /* Should not be reachable */ _ignore_signal: /* we still need to check if we need to restart something */ - maybe_restart_system_call(r); + maybe_restart_system_call(r, signum); - return !this_core->current_process->signal_queue->length; + return !(this_core->current_process->pending_signals & ~this_core->current_process->blocked_signals); } /** @@ -224,12 +228,19 @@ int send_signal(pid_t process, int signal, int force_root) { return -EINVAL; } - if (!receiver->signals[signal] && !sig_defaults[signal]) { + if (!receiver->signals[signal].handler && !sig_defaults[signal]) { /* If there is no handler for a signal and its default disposition is IGNORE, * we don't even bother sending it, to avoid having to interrupt + restart system calls. */ return 0; } + if (receiver->blocked_signals & (1 << signal)) { + spin_lock(sig_lock); + receiver->pending_signals |= (1 << signal); + spin_unlock(sig_lock); + return 0; + } + if (sig_defaults[signal] == SIG_DISP_Cont) { /* XXX: I'm not sure this check is necessary? And the SUSPEND flag flip probably * should be on the receiving end. */ @@ -242,11 +253,8 @@ int send_signal(pid_t process, int signal, int force_root) { } /* Append signal to list */ - signal_t * sig = malloc(sizeof(signal_t)); - sig->signum = signal; - spin_lock(sig_lock); - list_insert(receiver->signal_queue, sig); + receiver->pending_signals |= (1 << signal); spin_unlock(sig_lock); /* Informs any blocking events that the process has been interrupted @@ -311,20 +319,20 @@ int group_send_signal(pid_t group, int signal, int force_root) { */ void process_check_signals(struct regs * r) { spin_lock(sig_lock); - if (this_core->current_process && - !(this_core->current_process->flags & PROC_FLAG_FINISHED) && - this_core->current_process->signal_queue && - this_core->current_process->signal_queue->length > 0) { - while (1) { - node_t * node = list_dequeue(this_core->current_process->signal_queue); - spin_unlock(sig_lock); + if (this_core->current_process && !(this_core->current_process->flags & PROC_FLAG_FINISHED)) { + /* Set an pending signals that were previously blocked */ + sigset_t active_signals = this_core->current_process->pending_signals & ~this_core->current_process->blocked_signals; - signal_t * sig = node->value; - free(node); - - if (handle_signal((process_t*)this_core->current_process,sig,r)) return; - - spin_lock(sig_lock); + int signal = 0; + while (active_signals && signal <= NUMSIGNALS) { + if (active_signals & 1) { + this_core->current_process->pending_signals &= ~(1 << signal); + spin_unlock(sig_lock); + if (handle_signal((process_t*)this_core->current_process, signal, r)) return; + spin_lock(sig_lock); + } + active_signals >>= 1; + signal++; } } spin_unlock(sig_lock); @@ -342,6 +350,9 @@ void process_check_signals(struct regs * r) { * then to @c maybe_restart_system_call to handle system call restarts. */ void return_from_signal_handler(struct regs *r) { - arch_return_from_signal_handler(r); - maybe_restart_system_call(r); + int signum = arch_return_from_signal_handler(r); + if (this_core->current_process->pending_signals & ~this_core->current_process->blocked_signals) { + process_check_signals(r); + } + maybe_restart_system_call(r,signum); } diff --git a/kernel/sys/syscall.c b/kernel/sys/syscall.c index 09a1b7bc..97a23839 100644 --- a/kernel/sys/syscall.c +++ b/kernel/sys/syscall.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -947,11 +948,33 @@ long sys_signal(long signum, uintptr_t handler) { if (signum > NUMSIGNALS) { return -EINVAL; } - uintptr_t old = this_core->current_process->signals[signum]; - this_core->current_process->signals[signum] = handler; + uintptr_t old = this_core->current_process->signals[signum].handler; + this_core->current_process->signals[signum].handler = handler; + this_core->current_process->signals[signum].flags = SA_RESTART; return old; } +long sys_sigaction(int signum, struct sigaction *act, struct sigaction *oldact) { + if (act) PTRCHECK(act,sizeof(struct sigaction),0); + if (oldact) PTRCHECK(oldact,sizeof(struct sigaction),0); + + if (signum > NUMSIGNALS) return -EINVAL; + + if (oldact) { + oldact->sa_handler = (_sig_func_ptr)this_core->current_process->signals[signum].handler; + oldact->sa_mask = this_core->current_process->signals[signum].mask; + oldact->sa_flags = this_core->current_process->signals[signum].flags; + } + + if (act) { + this_core->current_process->signals[signum].handler = (uintptr_t)act->sa_handler; + this_core->current_process->signals[signum].mask = act->sa_mask; + this_core->current_process->signals[signum].flags = act->sa_flags; + } + + return 0; +} + long sys_fswait(int c, int fds[]) { PTR_VALIDATE(fds); if (!fds) return -EFAULT; @@ -1159,6 +1182,7 @@ static long (*syscalls[])() = { [SYS_TIMES] = sys_times, [SYS_PTRACE] = ptrace_handle, [SYS_SETTIMEOFDAY] = sys_settimeofday, + [SYS_SIGACTION] = sys_sigaction, [SYS_SOCKET] = net_socket, [SYS_SETSOCKOPT] = net_setsockopt, diff --git a/libc/signal/sigaction.c b/libc/signal/sigaction.c new file mode 100644 index 00000000..e4e3171a --- /dev/null +++ b/libc/signal/sigaction.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +DEFN_SYSCALL3(sigaction, SYS_SIGACTION, int, struct sigaction*, struct sigaction*); + +int sigaction(int signum, struct sigaction *act, struct sigaction *oldact) { + __sets_errno(syscall_sigaction(signum, act, oldact)); +} + +int sigemptyset(sigset_t * set) { + *set = 0; + return 0; +}