From d4586427909a367e6f94e4c365e56ae1836e0ada Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 13 Oct 2023 02:22:48 -0700 Subject: [PATCH] Write more tests and improve kill() on Windows --- examples/pause.c | 36 ++++++++++++ libc/intrin/exit1.greg.c | 2 +- libc/log/minicrash.c | 8 ++- libc/proc/kill-nt.c | 22 +++++-- test/libc/calls/sig_test.c | 115 +++++++++++++++++++++++++++++++++++++ tool/build/runit.c | 13 ++--- 6 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 examples/pause.c create mode 100644 test/libc/calls/sig_test.c diff --git a/examples/pause.c b/examples/pause.c new file mode 100644 index 000000000..be13f5f45 --- /dev/null +++ b/examples/pause.c @@ -0,0 +1,36 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/fmt/itoa.h" +#include "libc/str/str.h" + +volatile int g_sig; + +void OnSig(int sig) { + g_sig = sig; +} + +int main(int argc, char *argv[]) { + + // listen for all signals + for (int sig = 1; sig <= NSIG; ++sig) { + signal(sig, OnSig); + } + + // wait for a signal + char ibuf[12]; + FormatInt32(ibuf, getpid()); + tinyprint(2, "waiting for signal to be sent to ", ibuf, "\n", NULL); + pause(); + + // report the signal + tinyprint(1, "got ", strsignal(g_sig), "\n", NULL); +} diff --git a/libc/intrin/exit1.greg.c b/libc/intrin/exit1.greg.c index cb90fbbfd..efaae0f7d 100644 --- a/libc/intrin/exit1.greg.c +++ b/libc/intrin/exit1.greg.c @@ -42,7 +42,7 @@ __msabi extern typeof(ExitThread) *const __imp_ExitThread; * @see cthread_exit() * @noreturn */ -privileged wontreturn void _Exit1(int rc) { +wontreturn void _Exit1(int rc) { #ifdef __x86_64__ char cf; int ax, dx, di, si; diff --git a/libc/log/minicrash.c b/libc/log/minicrash.c index 3602b2a10..585fe5a63 100644 --- a/libc/log/minicrash.c +++ b/libc/log/minicrash.c @@ -29,7 +29,13 @@ #include "libc/str/str.h" /** - * Prints miniature crash report. + * Prints miniature crash report, e.g. + * + * struct sigaction sa = { + * .sa_sigaction = __minicrash, + * .sa_flags = SA_SIGINFO | SA_RESETHAND, + * }; + * sigaction(SIGSEGV, &sa, 0); * * This function may be called from a signal handler to print vital * information about the cause of a crash. Only vital number values diff --git a/libc/proc/kill-nt.c b/libc/proc/kill-nt.c index b906af3fc..38fbc4269 100644 --- a/libc/proc/kill-nt.c +++ b/libc/proc/kill-nt.c @@ -20,9 +20,14 @@ #include "libc/calls/syscall-nt.internal.h" #include "libc/errno.h" #include "libc/intrin/strace.internal.h" +#include "libc/nt/console.h" +#include "libc/nt/enum/ctrlevent.h" +#include "libc/nt/enum/processaccess.h" #include "libc/nt/errors.h" +#include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/proc/proc.internal.h" +#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #ifdef __x86_64__ @@ -55,17 +60,26 @@ textwindows int sys_kill_nt(int pid, int sig) { } // find existing handle we own for process - int64_t handle; + int64_t handle, closeme = 0; if (!(handle = __proc_handle(pid))) { - return esrch(); + if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) { + closeme = handle; + } else { + goto OnError; + } } // perform actual kill // process will report WIFSIGNALED with WTERMSIG(sig) - if (TerminateProcess(handle, sig)) return 0; - STRACE("TerminateProcess() failed with %d", GetLastError()); + bool32 ok = TerminateProcess(handle, sig); + if (closeme) CloseHandle(closeme); + if (ok) return 0; + + // handle error +OnError: switch (GetLastError()) { case kNtErrorInvalidHandle: + case kNtErrorInvalidParameter: return esrch(); default: return eperm(); diff --git a/test/libc/calls/sig_test.c b/test/libc/calls/sig_test.c new file mode 100644 index 000000000..d1a0f0241 --- /dev/null +++ b/test/libc/calls/sig_test.c @@ -0,0 +1,115 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/sysv/consts/sig.h" +#include "libc/atomic.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/nt/enum/context.h" +#include "libc/nt/struct/context.h" +#include "libc/nt/thread.h" +#include "libc/sock/struct/pollfd.h" +#include "libc/sysv/consts/poll.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +atomic_bool ready; +atomic_bool didit; +atomic_bool isdone; +atomic_bool gotsig; + +void SetUp(void) { + ready = false; + didit = false; + isdone = false; + gotsig = false; +} + +void Teleport(long rdi, long rsi, long rdx, long rcx) { + ASSERT_EQ(0x1111111111111111, rdi); + ASSERT_EQ(0x2222222222222222, rsi); + ASSERT_EQ(0x3333333333333333, rdx); + ASSERT_EQ(0x4444444444444444, rcx); + didit = true; + pthread_exit(0); +} + +void *Worker(void *arg) { + for (;;) { + ready = true; + } +} + +TEST(SetThreadContext, test) { + pthread_t th; + if (!IsWindows()) return; + ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); + while (!ready) donothing; + int64_t hand = _pthread_syshand((struct PosixThread *)th); + ASSERT_EQ(0, SuspendThread(hand)); + struct NtContext nc; + nc.ContextFlags = kNtContextFull; + ASSERT_NE(0, GetThreadContext(hand, &nc)); + nc.Rsp = (nc.Rsp - 128) & -16; + *(long *)(nc.Rsp -= 8) = nc.Rip; + nc.Rip = (long)Teleport; + nc.Rdi = 0x1111111111111111; + nc.Rsi = 0x2222222222222222; + nc.Rdx = 0x3333333333333333; + nc.Rcx = 0x4444444444444444; + ASSERT_NE(0, SetThreadContext(hand, &nc)); + ASSERT_EQ(1, ResumeThread(hand)); + ASSERT_EQ(0, pthread_join(th, 0)); + ASSERT_TRUE(didit); +} + +int pfds[2]; + +void OnSig(int sig) { + gotsig = true; +} + +void *Worker2(void *arg) { + do { + ASSERT_SYS(EINTR, -1, poll(&(struct pollfd){pfds[0], POLLIN}, 1, -1u)); + } while (!isdone); + didit = true; + return 0; +} + +TEST(poll, interrupt) { + pthread_t th; + signal(SIGUSR1, OnSig); + ASSERT_SYS(0, 0, pipe(pfds)); + ASSERT_EQ(0, pthread_create(&th, 0, Worker2, 0)); + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(0, pthread_kill(th, SIGUSR1)); + usleep(1000); + } + isdone = true; + ASSERT_EQ(0, pthread_kill(th, SIGUSR1)); + ASSERT_EQ(0, pthread_join(th, 0)); + ASSERT_SYS(0, 0, close(pfds[1])); + ASSERT_SYS(0, 0, close(pfds[0])); + ASSERT_TRUE(gotsig); + ASSERT_TRUE(didit); +} diff --git a/tool/build/runit.c b/tool/build/runit.c index 33380a125..13b12ffc1 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -48,6 +48,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sock.h" +#include "libc/temp.h" #include "libc/x/xasprintf.h" #include "net/https/https.h" #include "third_party/mbedtls/net_sockets.h" @@ -399,18 +400,16 @@ bool ShouldRunInParallel(void) { } int SpawnSubprocesses(int argc, char *argv[]) { - const char *tpath; sigset_t chldmask, savemask; - int i, ws, pid, tmpfd, *pids, exitcode; + int i, ws, pid, *pids, exitcode; struct sigaction ignore, saveint, savequit; char *args[5] = {argv[0], argv[1], argv[2]}; // create compressed network request ahead of time - CHECK_NE(-1, (tmpfd = open( - (tpath = _gc(xasprintf( - "%s/runit.%d", firstnonnull(getenv("TMPDIR"), "/tmp"), - getpid()))), - O_WRONLY | O_CREAT | O_TRUNC, 0755))); + const char *tmpdir = firstnonnull(getenv("TMPDIR"), "/tmp"); + char *tpath = _gc(xasprintf("%s/runit.XXXXXX", tmpdir)); + int tmpfd = mkstemp(tpath); + CHECK_NE(-1, tmpfd); CHECK(SendRequest(tmpfd)); CHECK_NE(-1, close(tmpfd));