Implement signals, finally! #30
@ -30,6 +30,7 @@ luna_app(uname.cpp uname)
|
||||
luna_app(base64.cpp base64)
|
||||
luna_app(login.cpp login)
|
||||
luna_app(ipc-test.cpp ipc-test)
|
||||
luna_app(signal-test.cpp signal-test)
|
||||
luna_app(mount.cpp mount)
|
||||
luna_app(umount.cpp umount)
|
||||
luna_app(ps.cpp ps)
|
||||
|
@ -327,8 +327,15 @@ int main()
|
||||
for (auto& service : g_services)
|
||||
{
|
||||
if (service.pid.has_value() && service.pid.value() == child)
|
||||
{
|
||||
if (WIFEXITED(status))
|
||||
{
|
||||
do_log("[init] service %s exited with status %d\n", service.name.chars(), WEXITSTATUS(status));
|
||||
}
|
||||
else
|
||||
{
|
||||
do_log("[init] service %s was terminated by signal %d\n", service.name.chars(), WTERMSIG(status));
|
||||
}
|
||||
|
||||
if (service.restart)
|
||||
{
|
||||
|
10
apps/sh.cpp
10
apps/sh.cpp
@ -6,6 +6,7 @@
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/Process.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -117,7 +118,14 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
if (child == 0) { TRY(execute_command(cmd.view())); }
|
||||
|
||||
TRY(os::Process::wait(child, nullptr));
|
||||
int status;
|
||||
TRY(os::Process::wait(child, &status));
|
||||
|
||||
if (WIFSIGNALED(status))
|
||||
{
|
||||
int sig = WTERMSIG(status);
|
||||
if (sig != SIGINT && sig != SIGQUIT) os::println("[sh] Process %d exited: %s", child, strsignal(sig));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
23
apps/signal-test.cpp
Normal file
23
apps/signal-test.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void handler(int)
|
||||
{
|
||||
puts("I caught a segfault!");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESETHAND;
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wnonnull"
|
||||
char* str = nullptr;
|
||||
memset(str, 0, 2);
|
||||
|
||||
return 0;
|
||||
}
|
@ -40,6 +40,7 @@ set(SOURCES
|
||||
src/sys/uname.cpp
|
||||
src/sys/mount.cpp
|
||||
src/sys/resource.cpp
|
||||
src/sys/signal.cpp
|
||||
src/fs/VFS.cpp
|
||||
src/fs/Pipe.cpp
|
||||
src/fs/Mount.cpp
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "video/TextConsole.h"
|
||||
#include <bits/signal.h>
|
||||
#include <cpuid.h>
|
||||
#include <luna/CString.h>
|
||||
#include <luna/CircularQueue.h>
|
||||
@ -70,64 +71,65 @@ void decode_page_fault_error_code(u64 code)
|
||||
(code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : "");
|
||||
}
|
||||
|
||||
[[noreturn]] void handle_page_fault(Registers* regs)
|
||||
void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
if (err) kerrorln("Caught CPU exception: %s", err);
|
||||
|
||||
kerrorln("RAX: %.16lx RBX: %.16lx RCX: %.16lx RDX: %.16lx", regs->rax, regs->rbx, regs->rcx, regs->rdx);
|
||||
kerrorln("RBP: %.16lx RSP: %.16lx RDI: %.16lx RSI: %.16lx", regs->rbp, regs->rsp, regs->rdi, regs->rsi);
|
||||
kerrorln("R8: %.16lx R9: %.16lx R10: %.16lx R11: %.16lx", regs->r8, regs->r9, regs->r10, regs->r11);
|
||||
kerrorln("R12: %.16lx R13: %.16lx R14: %.16lx R15: %.16lx", regs->r12, regs->r13, regs->r14, regs->r15);
|
||||
kerrorln("RIP: %.16lx CS: %.16lx SS: %.16lx FLAGS: %.16lx", regs->rip, regs->cs, regs->ss, regs->rflags);
|
||||
|
||||
CPU::print_stack_trace_at(regs);
|
||||
|
||||
if (!is_in_kernel(regs))
|
||||
{
|
||||
Scheduler::current()->send_signal(signo);
|
||||
Scheduler::current()->process_pending_signals(regs);
|
||||
return;
|
||||
}
|
||||
|
||||
CPU::efficient_halt();
|
||||
}
|
||||
|
||||
void handle_page_fault(Registers* regs)
|
||||
{
|
||||
u64 cr2;
|
||||
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||
kerrorln("Page fault at RIP %lx while accessing %lx!", regs->rip, cr2);
|
||||
kerrorln("Page fault while accessing %lx!", cr2);
|
||||
|
||||
decode_page_fault_error_code(regs->error);
|
||||
|
||||
CPU::print_stack_trace_at(regs);
|
||||
|
||||
if (!is_in_kernel(regs))
|
||||
{
|
||||
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
||||
kerrorln("Current task %zu was terminated because of a page fault", Scheduler::current()->id);
|
||||
Scheduler::current()->exit_and_signal_parent(127);
|
||||
handle_cpu_exception(SIGSEGV, nullptr, regs);
|
||||
}
|
||||
|
||||
CPU::efficient_halt();
|
||||
}
|
||||
|
||||
[[noreturn]] void handle_general_protection_fault(Registers* regs)
|
||||
void handle_general_protection_fault(Registers* regs)
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
|
||||
kerrorln("General protection fault at RIP %lx, error code %lx!", regs->rip, regs->error);
|
||||
|
||||
CPU::print_stack_trace_at(regs);
|
||||
|
||||
if (!is_in_kernel(regs))
|
||||
{
|
||||
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
||||
kerrorln("Current task %zu was terminated because of a general protection fault", Scheduler::current()->id);
|
||||
Scheduler::current()->exit_and_signal_parent(127);
|
||||
}
|
||||
|
||||
CPU::efficient_halt();
|
||||
handle_cpu_exception(SIGSEGV, nullptr, regs);
|
||||
}
|
||||
|
||||
extern "C" void handle_x86_exception(Registers* regs)
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
switch (regs->isr)
|
||||
{
|
||||
case 0: FIXME_UNHANDLED_INTERRUPT("Division by zero");
|
||||
case 0: handle_cpu_exception(SIGFPE, "Division by zero", regs); return;
|
||||
case 1: FIXME_UNHANDLED_INTERRUPT("Debug interrupt");
|
||||
case 2: FIXME_UNHANDLED_INTERRUPT("NMI (Non-maskable interrupt)");
|
||||
case 3: FIXME_UNHANDLED_INTERRUPT("Breakpoint");
|
||||
case 4: FIXME_UNHANDLED_INTERRUPT("Overflow");
|
||||
case 5: FIXME_UNHANDLED_INTERRUPT("Bound range exceeded");
|
||||
case 6: FIXME_UNHANDLED_INTERRUPT("Invalid opcode");
|
||||
case 6: handle_cpu_exception(SIGILL, "Invalid opcode", regs); return;
|
||||
case 7: FIXME_UNHANDLED_INTERRUPT("Device not available");
|
||||
case 10: FIXME_UNHANDLED_INTERRUPT("Invalid TSS");
|
||||
case 11: FIXME_UNHANDLED_INTERRUPT("Segment not present");
|
||||
case 12: FIXME_UNHANDLED_INTERRUPT("Stack-segment fault");
|
||||
case 13: handle_general_protection_fault(regs);
|
||||
case 14: handle_page_fault(regs);
|
||||
case 16: FIXME_UNHANDLED_INTERRUPT("x87 floating-point exception");
|
||||
case 13: handle_general_protection_fault(regs); return;
|
||||
case 14: handle_page_fault(regs); return;
|
||||
case 16: handle_cpu_exception(SIGFPE, "x87 floating-point exception", regs); return;
|
||||
case 17: FIXME_UNHANDLED_INTERRUPT("Alignment check");
|
||||
case 19: FIXME_UNHANDLED_INTERRUPT("SIMD floating-point exception");
|
||||
case 20: FIXME_UNHANDLED_INTERRUPT("Virtualization exception");
|
||||
|
@ -1,5 +1,9 @@
|
||||
#include "thread/Thread.h"
|
||||
#include "Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/CString.h>
|
||||
#include <luna/Check.h>
|
||||
|
||||
bool is_in_kernel(Registers* regs)
|
||||
{
|
||||
@ -31,6 +35,11 @@ void Thread::set_return(u64 ret)
|
||||
regs.rax = ret;
|
||||
}
|
||||
|
||||
u64 Thread::return_register()
|
||||
{
|
||||
return regs.rax;
|
||||
}
|
||||
|
||||
void Thread::init_regs_kernel()
|
||||
{
|
||||
memset(®s, 0, sizeof(Registers));
|
||||
@ -61,3 +70,95 @@ void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
||||
|
||||
memcpy(regs, &new_thread->regs, sizeof(Registers));
|
||||
}
|
||||
|
||||
// FIXME: Move this function to a common location (also used in ThreadImage)
|
||||
Result<u64> Thread::push_mem_on_stack(const u8* mem, usize size)
|
||||
{
|
||||
if ((regs.rsp - size) < stack.bottom()) return err(E2BIG);
|
||||
|
||||
if (!MemoryManager::validate_user_write((void*)(regs.rsp - size), size)) return err(EFAULT);
|
||||
|
||||
regs.rsp -= size;
|
||||
|
||||
memcpy((void*)regs.rsp, mem, size);
|
||||
|
||||
return regs.rsp;
|
||||
}
|
||||
|
||||
Result<u64> Thread::pop_mem_from_stack(u8* mem, usize size)
|
||||
{
|
||||
if ((regs.rsp + size) > stack.top()) return err(E2BIG);
|
||||
|
||||
if (!MemoryManager::validate_user_read((void*)regs.rsp, size)) return err(EFAULT);
|
||||
|
||||
memcpy(mem, (void*)regs.rsp, size);
|
||||
|
||||
regs.rsp += size;
|
||||
|
||||
return regs.rsp;
|
||||
}
|
||||
|
||||
bool Thread::deliver_signal(int signo, Registers* current_regs)
|
||||
{
|
||||
auto handler = signal_handlers[signo - 1];
|
||||
check(handler.sa_handler != SIG_IGN);
|
||||
check(handler.sa_handler != SIG_DFL);
|
||||
|
||||
memcpy(®s, current_regs, sizeof(regs));
|
||||
|
||||
kinfoln("signal: delivering signal %d for thread %ld, ip=%p, sp=%p, handler=%p, sigreturn=%p", signo, id,
|
||||
(void*)regs.rip, (void*)regs.rsp, (void*)handler.sa_handler, (void*)handler.__sa_sigreturn);
|
||||
|
||||
regs.rsp -= 128; // Skip the red zone
|
||||
|
||||
if (push_mem_on_stack((u8*)current_regs, sizeof(*current_regs)).has_error()) return false;
|
||||
if (push_mem_on_stack((u8*)&signal_mask, sizeof(signal_mask)).has_error()) return false;
|
||||
|
||||
u64 rsp = regs.rsp;
|
||||
|
||||
regs.rsp = align_down<16>(regs.rsp);
|
||||
if (push_mem_on_stack((u8*)&rsp, sizeof(u64)).has_error()) return false;
|
||||
if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false;
|
||||
|
||||
signal_mask = handler.sa_mask;
|
||||
if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask |= (1 << (signo - 1));
|
||||
|
||||
rsp = regs.rsp;
|
||||
|
||||
init_regs_user();
|
||||
regs.rsp = rsp;
|
||||
regs.rip = (u64)handler.sa_handler;
|
||||
regs.rdi = signo;
|
||||
|
||||
memcpy(current_regs, ®s, sizeof(regs));
|
||||
|
||||
if (handler.sa_flags & SA_RESETHAND)
|
||||
{
|
||||
handler.sa_handler = SIG_DFL;
|
||||
handler.sa_mask = 0;
|
||||
handler.sa_flags = 0;
|
||||
signal_handlers[signo - 1] = handler;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Thread::sigreturn(Registers* current_regs)
|
||||
{
|
||||
memcpy(®s, current_regs, sizeof(regs));
|
||||
|
||||
u64 rsp;
|
||||
pop_mem_from_stack((u8*)&rsp, sizeof(rsp));
|
||||
regs.rsp = rsp;
|
||||
pop_mem_from_stack((u8*)&signal_mask, sizeof(signal_mask));
|
||||
pop_mem_from_stack((u8*)current_regs, sizeof(*current_regs));
|
||||
memcpy(®s, current_regs, sizeof(regs));
|
||||
regs.cs = 0x18 | 3;
|
||||
regs.ss = 0x20 | 3;
|
||||
// FIXME: Using this, a program can craft a special RFLAGS that gives them a higher IOPL or other stuff. Find out
|
||||
// exactly what bits to block from modifying.
|
||||
|
||||
kinfoln("sigreturn: restored program state, sp=%p, ip=%p", (void*)regs.rsp, (void*)regs.rip);
|
||||
|
||||
memcpy(current_regs, ®s, sizeof(regs));
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <luna/SystemError.h>
|
||||
|
||||
syscall_func_t syscalls[] = {
|
||||
@ -13,6 +14,9 @@ i64 invoke_syscall(Registers* regs, SyscallArgs args, u64 syscall)
|
||||
if (syscall >= Syscalls::__count) { return -ENOSYS; }
|
||||
|
||||
auto rc = syscalls[syscall](regs, args);
|
||||
|
||||
Scheduler::current()->process_pending_signals(regs);
|
||||
|
||||
if (rc.has_error()) return -rc.error();
|
||||
return (i64)rc.value();
|
||||
}
|
||||
|
@ -112,6 +112,11 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
|
||||
memcpy(regs, ¤t->regs, sizeof(*regs));
|
||||
|
||||
for (int i = 0; i < NSIG; i++)
|
||||
{
|
||||
current->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||
}
|
||||
|
||||
kinfoln("exec: thread %lu was replaced with %s", current->id, path.chars());
|
||||
|
||||
return 0;
|
||||
@ -151,6 +156,13 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||
|
||||
memcpy(&thread->regs, regs, sizeof(*regs));
|
||||
|
||||
for (int i = 0; i < NSIG; i++)
|
||||
{
|
||||
auto sighandler = current->signal_handlers[i].sa_handler;
|
||||
thread->signal_handlers[i] = { .sa_handler = sighandler, .sa_mask = 0, .sa_flags = 0 };
|
||||
}
|
||||
thread->signal_mask = current->signal_mask;
|
||||
|
||||
thread->set_return(0);
|
||||
|
||||
Scheduler::add_thread(thread);
|
||||
|
99
kernel/src/sys/signal.cpp
Normal file
99
kernel/src/sys/signal.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
#include "Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <bits/signal.h>
|
||||
|
||||
Result<u64> sys_sigreturn(Registers* regs, SyscallArgs)
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
current->sigreturn(regs);
|
||||
|
||||
// Since we could be returning to anywhere in the program, we don't want to be changing the return value register
|
||||
// (RAX on x86_64). But our syscall framework doesn't allow that, so we just set it to its current value.
|
||||
return current->return_register();
|
||||
}
|
||||
|
||||
Result<u64> sys_sigaction(Registers*, SyscallArgs args)
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
int signo = (int)args[0];
|
||||
const struct sigaction* act = (const struct sigaction*)args[1];
|
||||
struct sigaction* oldact = (struct sigaction*)args[2];
|
||||
void* sigreturn = (void*)args[3];
|
||||
|
||||
if (signo > NSIG) return err(EINVAL);
|
||||
if (signo <= 0) return err(EINVAL);
|
||||
|
||||
if (oldact)
|
||||
{
|
||||
if (!MemoryManager::copy_to_user_typed(oldact, ¤t->signal_handlers[signo - 1])) return err(EFAULT);
|
||||
}
|
||||
|
||||
if (act)
|
||||
{
|
||||
struct sigaction kact;
|
||||
if (!MemoryManager::copy_from_user_typed(act, &kact)) return err(EFAULT);
|
||||
kact.__sa_sigreturn = sigreturn;
|
||||
|
||||
kinfoln("sigaction: installing signal handler for signo=%d: handler=%p, sigreturn=%p", signo,
|
||||
(void*)kact.sa_handler, sigreturn);
|
||||
|
||||
current->signal_handlers[signo - 1] = kact;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_kill(Registers*, SyscallArgs args)
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
pid_t pid = (pid_t)args[0];
|
||||
int signo = (int)args[1];
|
||||
|
||||
// FIXME: Support this case.
|
||||
if (pid <= 0) return err(ENOTSUP);
|
||||
|
||||
auto* target = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||
if (current->auth.euid != 0 && current->auth.euid != target->auth.euid && current->auth.egid != target->auth.egid)
|
||||
return err(EPERM);
|
||||
if (target->is_kernel) return 0;
|
||||
if (signo == 0) return 0;
|
||||
|
||||
target->send_signal(signo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
int how = (int)args[0];
|
||||
const sigset_t* set = (const sigset_t*)args[1];
|
||||
sigset_t* oldset = (sigset_t*)args[2];
|
||||
|
||||
if (oldset)
|
||||
{
|
||||
if (!MemoryManager::copy_to_user_typed(oldset, ¤t->signal_mask)) return err(EFAULT);
|
||||
}
|
||||
|
||||
if (set)
|
||||
{
|
||||
sigset_t kset;
|
||||
if (!MemoryManager::copy_from_user_typed(set, &kset)) return err(EFAULT);
|
||||
|
||||
switch (how)
|
||||
{
|
||||
case SIG_BLOCK: current->signal_mask |= kset; break;
|
||||
case SIG_UNBLOCK: current->signal_mask &= ~kset; break;
|
||||
case SIG_SETMASK: current->signal_mask = kset; break;
|
||||
default: return err(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -155,6 +155,11 @@ namespace Scheduler
|
||||
image->apply(thread);
|
||||
thread->set_arguments(args.size(), argv, env.size(), envp);
|
||||
|
||||
for (int i = 0; i < NSIG; i++)
|
||||
{
|
||||
thread->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||
}
|
||||
|
||||
kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
|
||||
thread->sp(), thread->kernel_stack.top());
|
||||
|
||||
@ -259,6 +264,7 @@ namespace Scheduler
|
||||
Thread* old_thread = g_current;
|
||||
Thread* new_thread = pick_task();
|
||||
generic_switch_context(old_thread, new_thread, regs);
|
||||
if (!is_in_kernel(regs)) new_thread->process_pending_signals(regs);
|
||||
}
|
||||
|
||||
void invoke(Registers* regs)
|
||||
|
@ -1,8 +1,11 @@
|
||||
#include "thread/Thread.h"
|
||||
#include "Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <bits/atfile.h>
|
||||
#include <bits/open-flags.h>
|
||||
#include <bits/signal.h>
|
||||
#include <bits/waitpid.h>
|
||||
#include <luna/Alloc.h>
|
||||
#include <luna/Atomic.h>
|
||||
#include <luna/PathParser.h>
|
||||
@ -69,7 +72,7 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
|
||||
}
|
||||
|
||||
[[noreturn]] void Thread::exit_and_signal_parent(u8 _status)
|
||||
[[noreturn]] void Thread::exit_and_signal_parent(int _status)
|
||||
{
|
||||
if (this->id == 1) fail("the init process exited");
|
||||
if (is_kernel) state = ThreadState::Dying;
|
||||
@ -80,7 +83,9 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
return true;
|
||||
});
|
||||
|
||||
if (parent && parent->state == ThreadState::Waiting)
|
||||
if (parent)
|
||||
{
|
||||
if (parent->state == ThreadState::Waiting)
|
||||
{
|
||||
auto child = *parent->child_being_waited_for;
|
||||
if (child == -1 || child == (pid_t)id)
|
||||
@ -89,6 +94,8 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
parent->wake_up();
|
||||
}
|
||||
}
|
||||
else { parent->send_signal(SIGCHLD); }
|
||||
}
|
||||
|
||||
state = ThreadState::Exited;
|
||||
}
|
||||
@ -97,6 +104,80 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
unreachable();
|
||||
}
|
||||
|
||||
enum class DefaultSignalAction
|
||||
{
|
||||
Ignore,
|
||||
Terminate,
|
||||
};
|
||||
|
||||
static constexpr DefaultSignalAction default_actions[] = {
|
||||
DefaultSignalAction::Terminate, // SIGHUP
|
||||
DefaultSignalAction::Terminate, // SIGINT
|
||||
DefaultSignalAction::Terminate, // SIGQUIT (dump core)
|
||||
DefaultSignalAction::Terminate, // SIGILL (dump core)
|
||||
DefaultSignalAction::Terminate, // SIGTRAP (dump core)
|
||||
DefaultSignalAction::Terminate, // SIGABRT (dump core)
|
||||
DefaultSignalAction::Ignore, // SIGCHLD
|
||||
DefaultSignalAction::Terminate, // SIGFPE (dump core)
|
||||
DefaultSignalAction::Terminate, // SIGKILL
|
||||
DefaultSignalAction::Terminate, // SIGSTOP (FIXME: Support stopping and continuing)
|
||||
DefaultSignalAction::Terminate, // SIGSEGV (dump core)
|
||||
DefaultSignalAction::Ignore, // SIGCONT (FIXME: Support stopping and continuing)
|
||||
DefaultSignalAction::Terminate, // SIGPIPE
|
||||
DefaultSignalAction::Terminate, // SIGALRM
|
||||
DefaultSignalAction::Terminate, // SIGTERM
|
||||
};
|
||||
|
||||
void Thread::process_pending_signals(Registers* current_regs)
|
||||
{
|
||||
for (int i = 0; i < NSIG; i++)
|
||||
{
|
||||
int signo = i + 1;
|
||||
if (signo != SIGKILL && signo != SIGSTOP && signal_mask & (1 << i)) continue;
|
||||
if (pending_signals & (1 << i))
|
||||
{
|
||||
pending_signals &= ~(1 << i);
|
||||
kinfoln("signal: executing signal %d for thread %ld", signo, id);
|
||||
auto handler = signal_handlers[i];
|
||||
if (signo != SIGKILL && signo != SIGSTOP && handler.sa_handler == SIG_IGN)
|
||||
{
|
||||
kinfoln("signal: ignoring signal (handler=SIG_IGN)");
|
||||
return;
|
||||
}
|
||||
if (handler.sa_handler == SIG_DFL || signo == SIGKILL || signo == SIGSTOP)
|
||||
{
|
||||
default_signal:
|
||||
if (id == 1)
|
||||
{
|
||||
kwarnln("signal: init got a signal it has no handler for, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
kinfoln("signal: using default behavior (handler=SIG_DFL)");
|
||||
|
||||
auto action = default_actions[i];
|
||||
switch (action)
|
||||
{
|
||||
case DefaultSignalAction::Ignore: return;
|
||||
// FIXME: Add signal exit codes.
|
||||
case DefaultSignalAction::Terminate: exit_and_signal_parent(signo | _SIGBIT);
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
// If we fail to deliver the signal (usually because there's not enough space on the stack), execute the
|
||||
// default action.
|
||||
if (!deliver_signal(signo, current_regs)) goto default_signal;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::send_signal(int signo)
|
||||
{
|
||||
check(signo > 0 && signo <= NSIG);
|
||||
pending_signals |= 1 << (signo - 1);
|
||||
}
|
||||
|
||||
bool FileDescriptor::should_append()
|
||||
{
|
||||
return flags & O_APPEND;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "arch/MMU.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "memory/AddressSpace.h"
|
||||
#include <bits/signal.h>
|
||||
#include <luna/LinkedList.h>
|
||||
#include <luna/OwnedPtr.h>
|
||||
#include <luna/Result.h>
|
||||
@ -79,13 +80,18 @@ struct Thread : public LinkedListNode<Thread>
|
||||
bool follow_last_symlink,
|
||||
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
||||
|
||||
struct sigaction signal_handlers[NSIG];
|
||||
sigset_t signal_mask { 0 };
|
||||
|
||||
int pending_signals { 0 };
|
||||
|
||||
FPData fp_data;
|
||||
|
||||
ThreadState state = ThreadState::Runnable;
|
||||
|
||||
bool is_kernel { true };
|
||||
|
||||
u8 status { 0 };
|
||||
int status { 0 };
|
||||
|
||||
mode_t umask { 0 };
|
||||
|
||||
@ -104,7 +110,7 @@ struct Thread : public LinkedListNode<Thread>
|
||||
|
||||
PageDirectory* active_directory { nullptr };
|
||||
|
||||
[[noreturn]] void exit_and_signal_parent(u8 status);
|
||||
[[noreturn]] void exit_and_signal_parent(int status);
|
||||
|
||||
bool is_idle()
|
||||
{
|
||||
@ -128,6 +134,17 @@ struct Thread : public LinkedListNode<Thread>
|
||||
u64 sp();
|
||||
|
||||
void set_return(u64 ret);
|
||||
u64 return_register();
|
||||
|
||||
void process_pending_signals(Registers* current_regs);
|
||||
|
||||
bool deliver_signal(int signo, Registers* current_regs);
|
||||
void sigreturn(Registers* current_regs);
|
||||
|
||||
Result<u64> push_mem_on_stack(const u8* mem, usize size);
|
||||
Result<u64> pop_mem_from_stack(u8* mem, usize size);
|
||||
|
||||
void send_signal(int signo);
|
||||
|
||||
static void init();
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ set(SOURCES
|
||||
src/grp.cpp
|
||||
src/locale.cpp
|
||||
src/scanf.cpp
|
||||
src/signal.cpp
|
||||
src/sys/stat.cpp
|
||||
src/sys/mman.cpp
|
||||
src/sys/wait.cpp
|
||||
@ -36,6 +37,7 @@ if(${LUNA_ARCH} STREQUAL "x86_64")
|
||||
${SOURCES}
|
||||
src/arch/x86_64/syscall.S
|
||||
src/arch/x86_64/setjmp.S
|
||||
src/arch/x86_64/sigreturn.S
|
||||
)
|
||||
endif()
|
||||
|
||||
|
58
libc/include/bits/signal.h
Normal file
58
libc/include/bits/signal.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* bits/signal.h: Signal-related definitions. */
|
||||
|
||||
#ifndef _BITS_SIGNAL_H
|
||||
#define _BITS_SIGNAL_H
|
||||
|
||||
typedef void (*__simple_sighandler_t)(int);
|
||||
|
||||
#define SIG_IGN (__simple_sighandler_t)(-1)
|
||||
#define SIG_DFL (__simple_sighandler_t)(-2)
|
||||
|
||||
typedef int sigset_t;
|
||||
|
||||
struct sigaction
|
||||
{
|
||||
__simple_sighandler_t sa_handler;
|
||||
sigset_t sa_mask;
|
||||
int sa_flags;
|
||||
#ifdef __cplusplus
|
||||
void* __sa_sigreturn = nullptr;
|
||||
#else
|
||||
void* __sa_sigreturn;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Constants for sigaction's sa_flags field.
|
||||
#define SA_NODEFER (1 << 0)
|
||||
#define SA_RESETHAND (1 << 1)
|
||||
|
||||
// Constants for the 'how' parameter in sigprocmask().
|
||||
#define SIG_BLOCK 0
|
||||
#define SIG_UNBLOCK 1
|
||||
#define SIG_SETMASK 2
|
||||
|
||||
// The signals with explicit numbers have portable signal numbers.
|
||||
enum __signals
|
||||
{
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGCHLD,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGSTOP,
|
||||
SIGSEGV = 11,
|
||||
SIGCONT,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
// FIXME: Add the remaining signals.
|
||||
__NSIG,
|
||||
};
|
||||
|
||||
#define NSIG (__NSIG - 1)
|
||||
|
||||
#endif
|
@ -3,6 +3,8 @@
|
||||
#ifndef _BITS_WAITPID_H
|
||||
#define _BITS_WAITPID_H
|
||||
|
||||
#define _SIGBIT 0x100
|
||||
|
||||
#define WNOHANG 1
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,53 @@
|
||||
#ifndef _SIGNAL_H
|
||||
#define _SIGNAL_H
|
||||
|
||||
#include <bits/signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef int sig_atomic_t;
|
||||
|
||||
#define SIG_ERR (__simple_sighandler_t)(-3)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
/* Examine/change the current thread's signal disposition for a specific signal. */
|
||||
int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact);
|
||||
#pragma GCC pop_options
|
||||
|
||||
/* Change the current thread's signal disposition for a specific signal. */
|
||||
__simple_sighandler_t signal(int signo, __simple_sighandler_t handler);
|
||||
|
||||
/* Send a signal to a specific process. */
|
||||
int kill(pid_t pid, int signo);
|
||||
|
||||
/* Send a signal to the current thread. */
|
||||
int raise(int signo);
|
||||
|
||||
/* Modify the current thread's signal mask. */
|
||||
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
|
||||
|
||||
/* Clear all signals from set. */
|
||||
int sigemptyset(sigset_t* set);
|
||||
|
||||
/* Add all signals to set. */
|
||||
int sigfillset(sigset_t* set);
|
||||
|
||||
/* Add a specific signal to set. */
|
||||
int sigaddset(sigset_t* set, int signo);
|
||||
|
||||
/* Remove a specific signal from set. */
|
||||
int sigdelset(sigset_t* set, int signo);
|
||||
|
||||
/* Check if a signal is in set.*/
|
||||
int sigismember(const sigset_t* set, int signo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -62,6 +62,9 @@ extern "C"
|
||||
/* Return the human-readable description of an error number. */
|
||||
char* strerror(int errnum);
|
||||
|
||||
/* Return the human-readable description of a signal number. */
|
||||
char* strsignal(int signo);
|
||||
|
||||
/* Return the length of the initial segment of str which consists only of bytes in accept. */
|
||||
size_t strspn(const char* str, const char* accept);
|
||||
|
||||
|
@ -6,8 +6,10 @@
|
||||
#include <bits/waitpid.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define WIFEXITED(ret) ((ret) | 0)
|
||||
#define WIFEXITED(ret) (((ret)&_SIGBIT) == 0)
|
||||
#define WEXITSTATUS(ret) ((ret)&0xff)
|
||||
#define WIFSIGNALED(ret) (((ret)&_SIGBIT) == _SIGBIT)
|
||||
#define WTERMSIG(ret) ((ret)&0xff)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
|
5
libc/src/arch/x86_64/sigreturn.S
Normal file
5
libc/src/arch/x86_64/sigreturn.S
Normal file
@ -0,0 +1,5 @@
|
||||
.global sigreturn
|
||||
sigreturn:
|
||||
mov $47, %rax
|
||||
int $66
|
||||
ud2
|
91
libc/src/signal.cpp
Normal file
91
libc/src/signal.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include <bits/errno-return.h>
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" void sigreturn();
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact)
|
||||
{
|
||||
long rc = syscall(SYS_sigaction, signo, act, oldact, sigreturn);
|
||||
__errno_return(rc, int);
|
||||
}
|
||||
|
||||
__simple_sighandler_t signal(int signo, __simple_sighandler_t handler)
|
||||
{
|
||||
struct sigaction act, oldact;
|
||||
act.sa_handler = handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
|
||||
if (sigaction(signo, &act, &oldact) < 0) return SIG_ERR;
|
||||
|
||||
return oldact.sa_handler;
|
||||
}
|
||||
|
||||
int kill(pid_t pid, int signo)
|
||||
{
|
||||
long rc = syscall(SYS_kill, pid, signo);
|
||||
__errno_return(rc, int);
|
||||
}
|
||||
|
||||
int raise(int signo)
|
||||
{
|
||||
return kill(getpid(), signo);
|
||||
}
|
||||
|
||||
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset)
|
||||
{
|
||||
long rc = syscall(SYS_sigprocmask, how, set, oldset);
|
||||
__errno_return(rc, int);
|
||||
}
|
||||
|
||||
int sigemptyset(sigset_t* set)
|
||||
{
|
||||
*set = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigfillset(sigset_t* set)
|
||||
{
|
||||
*set = 0xffffffff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigaddset(sigset_t* set, int signo)
|
||||
{
|
||||
if (signo <= 0 || signo > NSIG)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*set |= (1 << (signo - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigdelset(sigset_t* set, int signo)
|
||||
{
|
||||
if (signo <= 0 || signo > NSIG)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*set &= ~(1 << (signo - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigismember(const sigset_t* set, int signo)
|
||||
{
|
||||
if (signo <= 0 || signo > NSIG)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (*set & (1 << (signo - 1))) > 0;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
#include <luna/NumberParsing.h>
|
||||
#include <luna/Sort.h>
|
||||
#include <luna/Utf8.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
@ -99,7 +100,25 @@ extern "C"
|
||||
|
||||
__noreturn void abort()
|
||||
{
|
||||
syscall(SYS_exit, 255);
|
||||
// First, try to unblock SIGABRT and then raise it.
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGABRT);
|
||||
sigprocmask(SIG_UNBLOCK, &set, nullptr);
|
||||
|
||||
raise(SIGABRT);
|
||||
|
||||
// Still here? The program must have catched it. Reset the disposition to default.
|
||||
struct sigaction act;
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGABRT, &act, nullptr);
|
||||
|
||||
raise(SIGABRT);
|
||||
|
||||
// There is no way we could end up here, unless there is some sort of race condition or the kernel decided to
|
||||
// change the default action for SIGABRT because it's a Tuesday.
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@ -172,9 +191,6 @@ extern "C"
|
||||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
// FIXME: During the execution of system(), SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored, in
|
||||
// the process that calls system().
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == 0)
|
||||
{
|
||||
@ -184,9 +200,21 @@ extern "C"
|
||||
|
||||
if (child < 0) return -1;
|
||||
|
||||
sigset_t set, oldset;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &set, &oldset);
|
||||
|
||||
auto old_int = signal(SIGINT, SIG_IGN);
|
||||
auto old_quit = signal(SIGQUIT, SIG_IGN);
|
||||
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldset, nullptr);
|
||||
signal(SIGINT, old_int);
|
||||
signal(SIGQUIT, old_quit);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <bits/signal.h>
|
||||
#include <luna/SystemError.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -12,4 +13,32 @@ extern "C"
|
||||
{
|
||||
return const_cast<char*>(error_string(errnum));
|
||||
}
|
||||
|
||||
static const char* __internal_strsignal(int signo)
|
||||
{
|
||||
switch (signo)
|
||||
{
|
||||
case SIGHUP: return "Hangup";
|
||||
case SIGINT: return "Terminal interrupt";
|
||||
case SIGQUIT: return "Terminal quit";
|
||||
case SIGILL: return "Invalid opcode";
|
||||
case SIGTRAP: return "Breakpoint trap";
|
||||
case SIGABRT: return "Aborted";
|
||||
case SIGCHLD: return "Child stopped or exited";
|
||||
case SIGFPE: return "Floating point exception";
|
||||
case SIGKILL: return "Killed";
|
||||
case SIGSTOP: return "Stopped";
|
||||
case SIGSEGV: return "Segmentation fault";
|
||||
case SIGCONT: return "Continued";
|
||||
case SIGPIPE: return "Broken pipe";
|
||||
case SIGALRM: return "Alarm signal";
|
||||
case SIGTERM: return "Terminated";
|
||||
default: return "Unknown signal";
|
||||
}
|
||||
}
|
||||
|
||||
char* strsignal(int signo)
|
||||
{
|
||||
return const_cast<char*>(__internal_strsignal(signo));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
_e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \
|
||||
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
|
||||
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
|
||||
_e(pivot_root)
|
||||
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask)
|
||||
|
||||
enum Syscalls
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user