diff --git a/Kernel/MemoryManager.cpp b/Kernel/MemoryManager.cpp index 1b0d801ec9..570cf7d451 100644 --- a/Kernel/MemoryManager.cpp +++ b/Kernel/MemoryManager.cpp @@ -218,6 +218,8 @@ Region* MemoryManager::region_from_laddr(Process& process, LinearAddress laddr) if (region->contains(laddr)) return region.ptr(); } + kprintf("%s(%u) Couldn't find region for L%x\n", process.name().characters(), process.pid(), laddr.get()); + process.dumpRegions(); ASSERT_NOT_REACHED(); } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 52f3870172..b55083ef47 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -731,17 +731,18 @@ void Process::sys$exit(int status) switchNow(); } -void Process::send_signal(int signal, Process* sender) +void Process::terminate_due_to_signal(int signal, Process* sender) { ASSERT_INTERRUPTS_DISABLED(); - bool wasCurrent = current == sender; + bool wasCurrent = this == current; + set_state(Exiting); s_processes->remove(this); notify_waiters(m_pid, 0, signal); if (wasCurrent) { - kprintf("Current process committing suicide!\n"); + kprintf("Current process (%u) committing suicide!\n", pid()); if (!scheduleNewProcess()) { kprintf("Process::send_signal: Failed to schedule a new process :(\n"); HANG; @@ -752,6 +753,75 @@ void Process::send_signal(int signal, Process* sender) switchNow(); } +void Process::send_signal(int signal, Process* sender) +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(signal < 32); + + // FIXME: Handle send_signal to self. + ASSERT(this != current); + + auto& action = m_signal_action_data[signal]; + // FIXME: Implement SA_SIGINFO signal handlers. + ASSERT(!(action.flags & SA_SIGINFO)); + + auto handler_laddr = action.handler_or_sigaction; + if (handler_laddr.is_null()) + return terminate_due_to_signal(signal, sender); + + word ret_cs = m_tss.cs; + dword ret_eip = m_tss.eip; + dword ret_eflags = m_tss.eflags; + + if ((ret_cs & 3) == 0) { + // FIXME: Handle send_signal to process currently in kernel code. + ASSERT_NOT_REACHED(); + } + + ProcessPagingScope pagingScope(*this); + dword old_esp = m_tss.esp; + push_value_on_stack(ret_eip); + push_value_on_stack(ret_eflags); + push_value_on_stack(m_tss.eax); + push_value_on_stack(m_tss.ecx); + push_value_on_stack(m_tss.edx); + push_value_on_stack(m_tss.ebx); + push_value_on_stack(old_esp); + push_value_on_stack(m_tss.ebp); + push_value_on_stack(m_tss.esi); + push_value_on_stack(m_tss.edi); + m_tss.eax = (dword)signal; + m_tss.cs = 0x1b; + m_tss.eip = handler_laddr.get(); + + if (m_return_from_signal_trampoline.is_null()) { + auto* region = allocate_region(LinearAddress(), PAGE_SIZE, "signal_trampoline", true, true); // FIXME: Remap as read-only after setup. + m_return_from_signal_trampoline = region->linearAddress; + byte* code_ptr = m_return_from_signal_trampoline.asPtr(); + *code_ptr++ = 0x61; // popa + *code_ptr++ = 0x9d; // popf + *code_ptr++ = 0xc3; // ret + *code_ptr++ = 0x0f; // ud2 + *code_ptr++ = 0x0b; + } + + push_value_on_stack(m_return_from_signal_trampoline.get()); + + dbgprintf("signal: %s(%u) sent %d to %s(%u)\n", sender->name().characters(), sender->pid(), signal, name().characters(), pid()); + + if (sender == this) { + yield(); + ASSERT_NOT_REACHED(); + } +} + +void Process::push_value_on_stack(dword value) +{ + m_tss.esp -= 4; + dword* stack_ptr = (dword*)m_tss.esp; + *stack_ptr = value; +} + void Process::processDidCrash(Process* crashedProcess) { ASSERT_INTERRUPTS_DISABLED(); @@ -1183,12 +1253,6 @@ int Process::sys$isatty(int fd) return 1; } -Unix::sighandler_t Process::sys$signal(int signum, Unix::sighandler_t handler) -{ - dbgprintf("sys$signal: %d => L%x\n", signum, handler); - return nullptr; -} - int Process::sys$kill(pid_t pid, int signal) { if (pid == 0) { @@ -1466,3 +1530,26 @@ int Process::sys$dup2(int old_fd, int new_fd) m_file_descriptors[new_fd] = handle; return new_fd; } + +Unix::sighandler_t Process::sys$signal(int signum, Unix::sighandler_t handler) +{ + // FIXME: Fail with -EINVAL if attepmting to catch or ignore SIGKILL or SIGSTOP. + if (signum >= 32) + return (Unix::sighandler_t)-EINVAL; + dbgprintf("sys$signal: %d => L%x\n", signum, handler); + return nullptr; +} + +int Process::sys$sigaction(int signum, const Unix::sigaction* act, Unix::sigaction* old_act) +{ + // FIXME: Fail with -EINVAL if attepmting to change action for SIGKILL or SIGSTOP. + if (signum >= 32) + return -EINVAL; + VALIDATE_USER_READ(act, sizeof(Unix::sigaction)); + InterruptDisabler disabler; // FIXME: This should use a narrower lock. + auto& action = m_signal_action_data[signum]; + action.restorer = LinearAddress((dword)act->sa_restorer); + action.flags = act->sa_flags; + action.handler_or_sigaction = LinearAddress((dword)act->sa_sigaction); + return 0; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 5c6365d9dd..ad8b1817ee 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -15,6 +15,13 @@ class PageDirectory; class Region; class Zone; +struct SignalActionData { + LinearAddress handler_or_sigaction; + dword mask { 0 }; + int flags { 0 }; + LinearAddress restorer; +}; + class Process : public InlineLinkedListNode { friend class InlineLinkedListNode; public: @@ -129,6 +136,7 @@ public: int sys$getdtablesize(); int sys$dup(int oldfd); int sys$dup2(int oldfd, int newfd); + int sys$sigaction(int signum, const Unix::sigaction* act, Unix::sigaction* old_act); static void initialize(); @@ -162,6 +170,7 @@ public: const FileHandle* file_descriptor(size_t i) const { return m_file_descriptors[i].ptr(); } void send_signal(int signal, Process* sender); + void terminate_due_to_signal(int signal, Process* sender); Process* fork(RegisterDump&); int exec(const String& path, Vector&& arguments, Vector&& environment); @@ -172,7 +181,7 @@ private: Process(String&& name, uid_t, gid_t, pid_t parentPID, RingLevel, RetainPtr&& cwd = nullptr, RetainPtr&& executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr); - void allocateLDT(); + void push_value_on_stack(dword); PageDirectory* m_page_directory { nullptr }; @@ -205,6 +214,7 @@ private: int m_waiteeStatus { 0 }; int m_fdBlockedOnRead { -1 }; size_t m_max_open_file_descriptors { 16 }; + SignalActionData m_signal_action_data[32]; RetainPtr m_cwd; RetainPtr m_executable; @@ -221,6 +231,8 @@ private: // FIXME: Implement some kind of ASLR? LinearAddress m_nextRegion; + LinearAddress m_return_from_signal_trampoline; + pid_t m_parentPID { 0 }; static void notify_waiters(pid_t waitee, int exit_status, int signal); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index c4e5fd704a..48646885ee 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -146,6 +146,8 @@ static DWORD handle(RegisterDump& regs, DWORD function, DWORD arg1, DWORD arg2, return current->sys$dup((int)arg1); case Syscall::Dup2: return current->sys$dup2((int)arg1, (int)arg2); + case Syscall::Sigaction: + return current->sys$sigaction((int)arg1, (const Unix::sigaction*)arg2, (Unix::sigaction*)arg3); default: kprintf("<%u> int0x80: Unknown function %x requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index c5a210d099..db62e9c3c0 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -56,6 +56,7 @@ enum Function { Getdtablesize = 0x2024, Dup = 0x2025, Dup2 = 0x2026, + Sigaction = 0x2027, }; void initialize(); diff --git a/LibC/signal.cpp b/LibC/signal.cpp index f7bc277f14..c198650c99 100644 --- a/LibC/signal.cpp +++ b/LibC/signal.cpp @@ -22,5 +22,11 @@ sighandler_t signal(int signum, sighandler_t handler) return old_handler; } +int sigaction(int signum, const struct sigaction* act, struct sigaction* old_act) +{ + int rc = Syscall::invoke(Syscall::Sigaction, (dword)signum, (dword)act, (dword)old_act); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + } diff --git a/LibC/signal.h b/LibC/signal.h index a9b2fd072e..452840e874 100644 --- a/LibC/signal.h +++ b/LibC/signal.h @@ -11,8 +11,10 @@ typedef uint32_t sigset_t; typedef void siginfo_t; struct sigaction { - void (*sa_handler)(int); - void (*sa_sigaction)(int, siginfo_t*, void*); + union { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t*, void*); + }; sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); @@ -20,11 +22,16 @@ struct sigaction { int kill(pid_t, int sig); sighandler_t signal(int sig, sighandler_t); +int sigaction(int signum, const struct sigaction* act, struct sigaction* old_act); #define SIG_DFL ((__sighandler_t)0) #define SIG_ERR ((__sighandler_t)-1) #define SIG_IGN ((__sighandler_t)1) +#define SA_NOCLDSTOP 1 +#define SA_NOCLDWAIT 2 +#define SA_SIGINFO 4 + #define SIG_BLOCK 0 #define SIG_UNBLOCK 1 #define SIG_SETMASK 2 diff --git a/LibC/stdlib.h b/LibC/stdlib.h index 0a6102719a..0cd6a8e2c8 100644 --- a/LibC/stdlib.h +++ b/LibC/stdlib.h @@ -5,15 +5,15 @@ __BEGIN_DECLS -void* malloc(size_t); +void* malloc(size_t) __MALLOC; void free(void*); void* calloc(size_t nmemb, size_t); void* realloc(void *ptr, size_t); char* getenv(const char* name); int atoi(const char*); -void exit(int status); -void abort(); +void exit(int status) __NORETURN; +void abort() __NORETURN; __END_DECLS diff --git a/LibC/sys/cdefs.h b/LibC/sys/cdefs.h index a04364c61a..cee02fafc0 100644 --- a/LibC/sys/cdefs.h +++ b/LibC/sys/cdefs.h @@ -4,6 +4,7 @@ #define ALWAYS_INLINE inline __attribute__ ((always_inline)) #define __NORETURN __attribute__ ((noreturn)) +#define __MALLOC __attribute__ ((malloc)) #ifdef __cplusplus #define __BEGIN_DECLS extern "C" { diff --git a/Userland/cat.cpp b/Userland/cat.cpp index b127bf2cc9..ab003a2a7d 100644 --- a/Userland/cat.cpp +++ b/Userland/cat.cpp @@ -1,7 +1,9 @@ -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include int main(int argc, char** argv) { diff --git a/Userland/kill.cpp b/Userland/kill.cpp index 573b58fe4c..10cc9c1b62 100644 --- a/Userland/kill.cpp +++ b/Userland/kill.cpp @@ -1,6 +1,7 @@ -#include -#include -#include +#include +#include +#include +#include #include static unsigned parseUInt(const String& str, bool& ok) @@ -18,20 +19,36 @@ static unsigned parseUInt(const String& str, bool& ok) return value; } +static void print_usage_and_exit() +{ + printf("usage: kill [-signal] \n"); + exit(1); +} + int main(int argc, char** argv) { - if (argc < 2) { - printf("usage: kill \n"); - return 1; - } + if (argc != 2 && argc != 3) + print_usage_and_exit(); bool ok; - unsigned value = parseUInt(argv[1], ok); + unsigned signum = SIGTERM; + int pid_argi = 1; + if (argc == 3) { + pid_argi = 2; + if (argv[1][0] != '-') + print_usage_and_exit(); + signum = parseUInt(&argv[1][1], ok); + if (!ok) { + printf("%s is not a valid signal number\n", &argv[1][1]); + return 2; + } + } + unsigned pid = parseUInt(argv[pid_argi], ok); if (!ok) { - printf("%s is not a valid PID\n", argv[1]); - return 2; + printf("%s is not a valid PID\n", argv[pid_argi]); + return 3; } - kill((pid_t)value, SIGKILL); + kill((pid_t)pid, signum); return 0; } diff --git a/Userland/sh.cpp b/Userland/sh.cpp index 1ec61c2ed4..5d8c224b9a 100644 --- a/Userland/sh.cpp +++ b/Userland/sh.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include struct GlobalState { @@ -32,6 +33,29 @@ static int sh_pwd(int, const char**) return 0; } +void did_receive_signal(int signum) +{ + printf("\nMy word, I've received a signal with number %d\n", signum); + //exit(0); +} + +static int sh_busy(int, const char**) +{ + struct sigaction sa; + sa.sa_handler = did_receive_signal; + sa.sa_flags = 0; + sa.sa_mask = 0; + sa.sa_restorer = nullptr; + int rc = sigaction(SIGUSR1, &sa, nullptr); + assert(rc == 0); + printf("listening for SIGUSR1 while looping in userspace...\n"); + for (;;) { + for (volatile int i = 0; i < 100000; ++i) + ; + } + return 0; +} + static int sh_fork(int, const char**) { pid_t pid = fork(); @@ -147,6 +171,10 @@ static bool handle_builtin(int argc, const char** argv, int& retval) retval = sh_fef(argc, argv); return true; } + if (!strcmp(argv[0], "busy")) { + retval = sh_busy(argc, argv); + return true; + } if (!strcmp(argv[0], "wt")) { retval = sh_wt(argc, argv); return true; diff --git a/VirtualFileSystem/UnixTypes.h b/VirtualFileSystem/UnixTypes.h index 4f4a3e10e2..ed29ce03b0 100644 --- a/VirtualFileSystem/UnixTypes.h +++ b/VirtualFileSystem/UnixTypes.h @@ -20,9 +20,54 @@ typedef dword nlink_t; typedef dword uid_t; typedef dword gid_t; +#ifdef SERENITY typedef void (*__sighandler_t)(int); typedef __sighandler_t sighandler_t; +typedef dword sigset_t; +typedef void siginfo_t; + +struct sigaction { + union { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t*, void*); + }; + sigset_t sa_mask; + int sa_flags; + void (*sa_restorer)(void); +}; + +#define SA_NOCLDSTOP 1 +#define SA_NOCLDWAIT 2 +#define SA_SIGINFO 4 + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGCONT 18 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 + +#endif + + #ifdef SERENITY // FIXME: Support 64-bit offsets! typedef signed_dword off_t;