mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Make signal handling work well across platforms
- Fix sigsuspend() on XNU - Fix strsignal() on non-Linux - Add unit tests for strsignal() - Add unit tests for setitimer() - Add unit tests for sigsuspend() - Rewrite setitimer() for New Technology - Rewrite nanosleep() for New Technology - Polyfill SIGALRM on the New Technology - select(0,0,0,0) on NT now calls pause() - Remove some NTDLL calls that aren't needed - Polyfill SA_NOCLDWAIT on the New Technology - Polyfill SA_RESETHAND on the New Technology - Polyfill sigprocmask() on the New Technology - Polyfill SIGCHLD+SIG_IGN on the New Technology - Polyfill SA_RESTART masking on the New Technology - Deliver console signals from main thread on New Technology - Document SA_RESTART behavior w/ @sarestartable / @norestart - System call trace in MODE=dbg now prints inherited FDs and signal mask
This commit is contained in:
parent
3b9e66ecba
commit
072e1d2910
82 changed files with 1388 additions and 450 deletions
|
@ -8,14 +8,24 @@
|
|||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
sigset_t ss;
|
||||
if (argc < 3) {
|
||||
fputs("USAGE: EXEC.COM PROG ARGV₀ [ARGV₁...]\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// block arbitrary signal so __printargs() looks cooler
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGPWR);
|
||||
sigprocmask(SIG_BLOCK, &ss, 0);
|
||||
|
||||
execv(argv[1], argv + 1);
|
||||
return 127;
|
||||
}
|
||||
|
|
72
examples/setitimer.c
Normal file
72
examples/setitimer.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
#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/itimerval.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
volatile bool gotalrm;
|
||||
|
||||
void OnSigAlrm(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||
gotalrm = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
printf("first tutorial: set singleshot alarm for 1.5 seconds from now\n");
|
||||
|
||||
// setup alarm in 1.5 seconds
|
||||
// this timer tears itself down once it's handled
|
||||
struct itimerval shot = {{0, 0}, {1, 500000}};
|
||||
struct sigaction handler = {.sa_sigaction = OnSigAlrm,
|
||||
.sa_flags = SA_RESETHAND | SA_SIGINFO};
|
||||
CHECK_EQ(0, sigaction(SIGALRM, &handler, 0));
|
||||
CHECK_EQ(0, setitimer(ITIMER_REAL, &shot, 0));
|
||||
|
||||
// wait for alarm
|
||||
pause();
|
||||
printf("got singleshot alarm!\n\n");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
printf("second tutorial: interval timer\n");
|
||||
|
||||
// setup timer every 1.5 seconds
|
||||
struct sigaction oldalrm;
|
||||
struct sigaction sigalrm = {.sa_sigaction = OnSigAlrm,
|
||||
.sa_flags = SA_SIGINFO};
|
||||
CHECK_EQ(0, sigaction(SIGALRM, &sigalrm, &oldalrm));
|
||||
struct itimerval oldtimer;
|
||||
struct itimerval timer = {{1, 500000}, {1, 500000}};
|
||||
CHECK_EQ(0, setitimer(ITIMER_REAL, &timer, &oldtimer));
|
||||
|
||||
// wait for three timeouts
|
||||
int i = 0;
|
||||
int n = 3;
|
||||
while (i < n) {
|
||||
pause();
|
||||
if (gotalrm) {
|
||||
++i;
|
||||
printf("got timeout %d out of %d\n", i, n);
|
||||
gotalrm = false;
|
||||
}
|
||||
}
|
||||
|
||||
// teardown timer
|
||||
CHECK_EQ(0, setitimer(ITIMER_REAL, &oldtimer, 0));
|
||||
CHECK_EQ(0, sigaction(SIGALRM, &oldalrm, 0));
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
|
@ -39,9 +40,10 @@
|
|||
* @see strftime(), gettimeofday()
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int clock_gettime(int clockid, struct timespec *ts) {
|
||||
noinstrument int clock_gettime(int clockid, struct timespec *ts) {
|
||||
int rc, e;
|
||||
axdx_t ad;
|
||||
char buf[45];
|
||||
if (!ts) {
|
||||
rc = efault();
|
||||
} else if (IsAsan() && !__asan_is_valid(ts, sizeof(*ts))) {
|
||||
|
@ -64,6 +66,9 @@ int clock_gettime(int clockid, struct timespec *ts) {
|
|||
} else {
|
||||
rc = sys_clock_gettime_nt(clockid, ts);
|
||||
}
|
||||
/* TODO(jart): Add get_clock_gettime() so we can STRACE() */
|
||||
if (!__time_critical) {
|
||||
STRACE("clock_gettime(%d, [%s]) → %d% m", clockid,
|
||||
__strace_timespec(buf, sizeof(buf), rc, ts), rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
@ -47,8 +48,10 @@
|
|||
* @param arg can be FD_CLOEXEC, etc. depending
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int fcntl(int fd, int cmd, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
uintptr_t arg;
|
||||
va_start(va, cmd);
|
||||
|
@ -56,13 +59,15 @@ int fcntl(int fd, int cmd, ...) {
|
|||
va_end(va);
|
||||
if (fd >= 0) {
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
return weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||
rc = weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||
} else if (!IsWindows()) {
|
||||
return sys_fcntl(fd, cmd, arg);
|
||||
rc = sys_fcntl(fd, cmd, arg);
|
||||
} else {
|
||||
return sys_fcntl_nt(fd, cmd, arg);
|
||||
rc = sys_fcntl_nt(fd, cmd, arg);
|
||||
}
|
||||
} else {
|
||||
return einval();
|
||||
rc = einval();
|
||||
}
|
||||
STRACE("fcntl(%d, %d, %p) → %d% m", fd, cmd, arg);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
* @param op can have LOCK_{SH,EX,NB,UN} for shared, exclusive,
|
||||
* non-blocking, and unlocking
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @restartable
|
||||
*/
|
||||
int flock(int fd, int op) {
|
||||
int rc;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
|
@ -16,10 +16,29 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
.text.windows
|
||||
|
||||
__winalarm_nt:
|
||||
ezlea __winalarm,ax
|
||||
jmp __nt2sysv
|
||||
.endfn __winalarm_nt,globl,hidden
|
||||
// fmod [sic] does (𝑥 rem 𝑦) w/ round()-style rounding.
|
||||
//
|
||||
// @param 𝑥 is an 80-bit long double passed on stack in 16-bytes
|
||||
// @param 𝑦 is the power, also pushed on stack, in reverse order
|
||||
// @return remainder ∈ (-|𝑦|,|𝑦|) in %st
|
||||
// @define 𝑥-truncl(𝑥/𝑦)*𝑦
|
||||
// @see emod()
|
||||
fmodl: push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable
|
||||
fldt 32(%rbp)
|
||||
fldt 16(%rbp)
|
||||
1: fprem
|
||||
fnstsw
|
||||
test $FPU_C2>>8,%ah
|
||||
jnz 1b
|
||||
fstp %st(1)
|
||||
pop %rbp
|
||||
ret
|
||||
1: int3
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fmodl,globl
|
|
@ -19,3 +19,4 @@
|
|||
#include "libc/calls/internal.h"
|
||||
|
||||
unsigned __sighandrvas[NSIG];
|
||||
unsigned __sighandflags[NSIG];
|
||||
|
|
|
@ -9,8 +9,6 @@ static inline int GetConsoleCtrlEvent(int sig) {
|
|||
switch (sig) {
|
||||
case SIGINT:
|
||||
return kNtCtrlCEvent;
|
||||
case SIGHUP:
|
||||
return kNtCtrlCloseEvent;
|
||||
case SIGQUIT:
|
||||
return kNtCtrlBreakEvent;
|
||||
default:
|
||||
|
|
|
@ -58,6 +58,7 @@ struct Fd {
|
|||
unsigned flags;
|
||||
int64_t handle;
|
||||
int64_t extra;
|
||||
bool zombie;
|
||||
};
|
||||
|
||||
struct Fds {
|
||||
|
@ -69,9 +70,10 @@ struct Fds {
|
|||
|
||||
extern const struct Fd kEmptyFd;
|
||||
|
||||
hidden extern volatile bool __interrupted;
|
||||
hidden extern int __vforked;
|
||||
hidden extern bool __time_critical;
|
||||
hidden extern unsigned __sighandrvas[NSIG];
|
||||
hidden extern unsigned __sighandflags[NSIG];
|
||||
hidden extern struct Fds g_fds;
|
||||
hidden extern const struct NtSecurityAttributes kNtIsInheritable;
|
||||
|
||||
|
@ -306,8 +308,10 @@ int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden;
|
|||
│ cosmopolitan § syscalls » windows nt » support ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
bool _check_sigchld(void) hidden;
|
||||
int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64]) hidden;
|
||||
bool _check_interrupts(bool, struct Fd *) hidden;
|
||||
void _check_sigchld(void) hidden;
|
||||
void _check_sigalrm(void) hidden;
|
||||
int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64], bool) hidden;
|
||||
bool isdirectory_nt(const char *) hidden;
|
||||
bool isregularfile_nt(const char *) hidden;
|
||||
bool issymlink_nt(const char *) hidden;
|
||||
|
@ -327,7 +331,6 @@ struct NtOverlapped *offset2overlap(int64_t, struct NtOverlapped *) hidden;
|
|||
unsigned __wincrash_nt(struct NtExceptionPointers *);
|
||||
void *GetProcAddressModule(const char *, const char *) hidden;
|
||||
void WinMainForked(void) hidden;
|
||||
void __winalarm(void *, uint32_t, uint32_t) hidden;
|
||||
void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- 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 2020 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2022 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 │
|
||||
|
@ -16,32 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/dce.h"
|
||||
|
||||
TEST(fastdiv, test) {
|
||||
long x = 123000000321;
|
||||
EXPECT_EQ(123, div1000000000int64(x));
|
||||
EXPECT_EQ(321, rem1000000000int64(x));
|
||||
}
|
||||
|
||||
TEST(dsleep, test) {
|
||||
long double t1, t2;
|
||||
sigset_t mask, oldmask;
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_BLOCK, &mask, &oldmask);
|
||||
sched_yield();
|
||||
t1 = dtime(CLOCK_MONOTONIC);
|
||||
dsleep(0.001L);
|
||||
t2 = dtime(CLOCK_MONOTONIC);
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
ASSERT_LDBL_GT(t2 - t1, 0.0005L);
|
||||
textwindows bool _check_interrupts(bool restartable, struct Fd *fd) {
|
||||
if (__time_critical) return false;
|
||||
if (weaken(_check_sigalrm)) weaken(_check_sigalrm)();
|
||||
if (weaken(_check_sigchld)) weaken(_check_sigchld)();
|
||||
if (fd && weaken(_check_sigwinch)) weaken(_check_sigwinch)(fd);
|
||||
return weaken(__sig_check) && weaken(__sig_check)(restartable);
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
/**
|
||||
* Controls settings on device.
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
int(ioctl)(int fd, uint64_t request, ...) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/getconsolectrlevent.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -35,7 +36,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
if (pid) {
|
||||
pid = ABS(pid);
|
||||
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
/* kill(pid, SIGINT|SIGHUP|SIGQUIT) */
|
||||
/* kill(pid, SIGINT|SIGQUIT) */
|
||||
if (__isfdkind(pid, kFdProcess)) {
|
||||
ntpid = GetProcessId(g_fds.p[pid].handle);
|
||||
} else if (!__isfdopen(pid)) {
|
||||
|
|
|
@ -16,33 +16,61 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define ShouldUseMsabiAttribute() 1
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/nt/time.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows noinstrument int sys_nanosleep_nt(const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
/* no function tracing because timing sensitive */
|
||||
int64_t millis, hectonanos, relasleep;
|
||||
if (rem) memcpy(rem, req, sizeof(*rem));
|
||||
hectonanos = req->tv_sec * 10000000 + req->tv_nsec / 100;
|
||||
hectonanos = MAX(1, hectonanos);
|
||||
relasleep = -hectonanos;
|
||||
if (NtError(__imp_NtDelayExecution(true, &relasleep))) {
|
||||
millis = hectonanos / 10000;
|
||||
millis = MAX(1, millis);
|
||||
if (__imp_SleepEx(millis, true) == kNtWaitIoCompletion) {
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
int rc;
|
||||
int64_t sec, nsec;
|
||||
uint64_t ms, slice;
|
||||
if (__builtin_mul_overflow(req->tv_sec, 1000, &ms) ||
|
||||
__builtin_add_overflow(ms, req->tv_nsec / 1000000, &ms)) {
|
||||
ms = -1;
|
||||
}
|
||||
if (!ms && (req->tv_sec || req->tv_nsec)) {
|
||||
ms = 1;
|
||||
}
|
||||
rc = 0;
|
||||
do {
|
||||
if (!__time_critical && _check_interrupts(false, g_fds.p)) {
|
||||
rc = eintr();
|
||||
break;
|
||||
}
|
||||
if (ms > __SIG_POLLING_INTERVAL_MS) {
|
||||
slice = __SIG_POLLING_INTERVAL_MS;
|
||||
} else {
|
||||
slice = ms;
|
||||
}
|
||||
if (!__time_critical) {
|
||||
STRACE("SleepEx(%u, true)", slice);
|
||||
}
|
||||
if (SleepEx(slice, true) == kNtWaitIoCompletion) {
|
||||
rc = eintr();
|
||||
break;
|
||||
}
|
||||
ms -= slice;
|
||||
} while (ms);
|
||||
if (rem) {
|
||||
sec = ms / 1000;
|
||||
nsec = ms % 1000 * 1000000000;
|
||||
rem->tv_nsec -= nsec;
|
||||
if (rem->tv_nsec < 0) {
|
||||
--rem->tv_sec;
|
||||
rem->tv_nsec = 1000000000 - rem->tv_nsec;
|
||||
}
|
||||
rem->tv_sec -= sec;
|
||||
if (rem->tv_sec < 0) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
}
|
||||
}
|
||||
if (rem) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
}
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -18,24 +18,35 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sleeps for a particular amount of time.
|
||||
* @norestart
|
||||
*/
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||
if (!req) return efault();
|
||||
if (req->tv_sec < 0 || !(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
|
||||
return einval();
|
||||
}
|
||||
if (!IsWindows() && !IsMetal() && !IsXnu()) {
|
||||
return sys_nanosleep(req, rem);
|
||||
noinstrument int nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||
int rc;
|
||||
char buf[2][45];
|
||||
if (!req) {
|
||||
rc = efault();
|
||||
} else if (req->tv_sec < 0 ||
|
||||
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
|
||||
rc = einval();
|
||||
} else if (!IsWindows() && !IsMetal() && !IsXnu()) {
|
||||
rc = sys_nanosleep(req, rem);
|
||||
} else if (IsXnu()) {
|
||||
return sys_nanosleep_xnu(req, rem);
|
||||
rc = sys_nanosleep_xnu(req, rem);
|
||||
} else if (IsMetal()) {
|
||||
return enosys(); /* TODO: Sleep on Metal */
|
||||
rc = enosys(); /* TODO: Sleep on Metal */
|
||||
} else {
|
||||
return sys_nanosleep_nt(req, rem);
|
||||
rc = sys_nanosleep_nt(req, rem);
|
||||
}
|
||||
if (!__time_critical) {
|
||||
STRACE("nanosleep(%s, [%s]) → %d% m",
|
||||
__strace_timespec(buf[0], sizeof(buf[0]), rc, req),
|
||||
__strace_timespec(buf[1], sizeof(buf[1]), rc, rem), rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "libc/bits/initializer.internal.h"
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
|
@ -40,17 +42,20 @@ static long double GetTimeSample(void) {
|
|||
sched_yield();
|
||||
time1 = dtime(CLOCK_REALTIME);
|
||||
tick1 = rdtsc();
|
||||
nanosleep(&(struct timespec){0, 100000}, NULL);
|
||||
nanosleep(&(struct timespec){0, 1000000}, NULL);
|
||||
time2 = dtime(CLOCK_REALTIME);
|
||||
tick2 = rdtsc();
|
||||
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
|
||||
}
|
||||
|
||||
static long double MeasureNanosPerCycle(void) {
|
||||
bool tc;
|
||||
int i, n;
|
||||
long double avg, samp;
|
||||
tc = __time_critical;
|
||||
__time_critical = true;
|
||||
if (IsWindows()) {
|
||||
n = 20;
|
||||
n = 10;
|
||||
} else {
|
||||
n = 5;
|
||||
}
|
||||
|
@ -58,6 +63,8 @@ static long double MeasureNanosPerCycle(void) {
|
|||
samp = GetTimeSample();
|
||||
avg += (samp - avg) / i;
|
||||
}
|
||||
__time_critical = tc;
|
||||
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
|
||||
return avg;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,52 +16,25 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/pushpop.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
textwindows bool32 __onntconsoleevent(uint32_t CtrlType) {
|
||||
int sig;
|
||||
unsigned rva;
|
||||
siginfo_t info;
|
||||
switch (CtrlType) {
|
||||
textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case kNtCtrlCEvent:
|
||||
STRACE("kNtCtrlCEvent");
|
||||
sig = pushpop(SIGINT);
|
||||
break;
|
||||
__sig_add(SIGINT, SI_KERNEL);
|
||||
return true;
|
||||
case kNtCtrlBreakEvent:
|
||||
STRACE("kNtCtrlBreakEvent");
|
||||
sig = pushpop(SIGQUIT);
|
||||
break;
|
||||
__sig_add(SIGQUIT, SI_KERNEL);
|
||||
return true;
|
||||
case kNtCtrlCloseEvent:
|
||||
STRACE("kNtCtrlCloseEvent");
|
||||
sig = pushpop(SIGHUP);
|
||||
break;
|
||||
case kNtCtrlLogoffEvent: // only received by services so hack hack hack
|
||||
case kNtCtrlShutdownEvent: // only received by services so hack hack hack
|
||||
STRACE("kNtCtrlLogoffEvent");
|
||||
sig = pushpop(SIGALRM);
|
||||
break;
|
||||
case kNtCtrlLogoffEvent: // only received by services
|
||||
case kNtCtrlShutdownEvent: // only received by services
|
||||
__sig_add(SIGHUP, SI_KERNEL);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch ((rva = __sighandrvas[sig])) {
|
||||
case (uintptr_t)SIG_DFL:
|
||||
_Exit(128 + sig);
|
||||
case (uintptr_t)SIG_IGN:
|
||||
return true;
|
||||
default:
|
||||
bzero(&info, sizeof(info));
|
||||
info.si_signo = sig;
|
||||
((sigaction_f)(_base + rva))(sig, &info, NULL);
|
||||
__interrupted = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
* ignored if O_CREAT or O_TMPFILE weren't passed
|
||||
* @return number needing close(), or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
int open(const char *file, int flags, ...) {
|
||||
|
|
|
@ -20,28 +20,32 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Waits for signal.
|
||||
* Waits for any signal.
|
||||
*
|
||||
* This suspends execution until an unmasked signal is delivered
|
||||
* and its callback function has been called. It's a better idea
|
||||
* to use sigsuspend() w/ sigprocmask() to avoid race conditions
|
||||
* This suspends execution until an unmasked signal is delivered and its
|
||||
* callback function has been called. The current signal mask is used.
|
||||
*
|
||||
* @return should always be -1 w/ EINTR
|
||||
* @see sigsuspend()
|
||||
* @norestart
|
||||
*/
|
||||
int pause(void) {
|
||||
int rc, olderr;
|
||||
sigset_t oldmask;
|
||||
olderr = errno;
|
||||
STRACE("pause()");
|
||||
int e, rc;
|
||||
sigset_t mask;
|
||||
e = errno;
|
||||
STRACE("pause() → [...]");
|
||||
if ((rc = sys_pause()) == -1 && errno == ENOSYS) {
|
||||
errno = olderr;
|
||||
if (sigprocmask(SIG_BLOCK, NULL, &oldmask) == -1) return -1;
|
||||
rc = sigsuspend(&oldmask);
|
||||
errno = e;
|
||||
if (sigprocmask(SIG_BLOCK, 0, &mask) == -1) return -1;
|
||||
rc = sigsuspend(&mask);
|
||||
}
|
||||
STRACE("[...] pause → %d% m", rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/getconsolectrlevent.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
/**
|
||||
|
@ -33,26 +35,28 @@
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
int raise(int sig) {
|
||||
int event;
|
||||
STRACE("raise(%d)", sig);
|
||||
int rc, event;
|
||||
STRACE("raise(%d) → [...]", sig);
|
||||
if (sig == SIGTRAP) {
|
||||
DebugBreak();
|
||||
return 0;
|
||||
}
|
||||
if (sig == SIGFPE) {
|
||||
rc = 0;
|
||||
} else if (sig == SIGFPE) {
|
||||
volatile int x = 0;
|
||||
x = 1 / x;
|
||||
return 0;
|
||||
}
|
||||
if (!IsWindows()) {
|
||||
return sys_kill(getpid(), sig, 1);
|
||||
} else if ((event = GetConsoleCtrlEvent(sig))) {
|
||||
if (GenerateConsoleCtrlEvent(event, 0)) {
|
||||
return 0;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
rc = 0;
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_kill(getpid(), sig, 1);
|
||||
} else {
|
||||
_Exit(128 + sig);
|
||||
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
if (GenerateConsoleCtrlEvent(event, 0)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
} else {
|
||||
rc = __sig_add(sig, SI_USER);
|
||||
}
|
||||
}
|
||||
STRACE("[...] raise → %d% m", rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -51,11 +51,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
|
|||
ssize_t rc;
|
||||
uint32_t size;
|
||||
size_t i, total;
|
||||
if (cmpxchg(&__interrupted, true, false) ||
|
||||
(weaken(_check_sigchld) && weaken(_check_sigchld)()) ||
|
||||
(weaken(_check_sigwinch) && weaken(_check_sigwinch)(fd))) {
|
||||
return eintr();
|
||||
}
|
||||
if (_check_interrupts(true, fd)) return eintr();
|
||||
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
|
||||
if (iovlen) {
|
||||
for (total = i = 0; i < iovlen; ++i) {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
* exception of size==0, in which case return zero means no error
|
||||
* @see write(), pread(), readv()
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
ssize_t read(int fd, void *buf, size_t size) {
|
||||
ssize_t rc;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* Reads data to multiple buffers.
|
||||
*
|
||||
* @return number of bytes actually read, or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
|
||||
ssize_t rc;
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/rand/lcg.internal.h"
|
||||
|
||||
/**
|
||||
|
@ -28,13 +30,15 @@
|
|||
*
|
||||
* @return number of items returned in pids and handles
|
||||
*/
|
||||
int __sample_pids(int pids[hasatleast 64], int64_t handles[hasatleast 64]) {
|
||||
textwindows int __sample_pids(int pids[hasatleast 64],
|
||||
int64_t handles[hasatleast 64],
|
||||
bool exploratory) {
|
||||
static uint64_t rando = 1;
|
||||
uint32_t i, j, base, count;
|
||||
base = KnuthLinearCongruentialGenerator(&rando);
|
||||
for (count = i = 0; i < g_fds.n; ++i) {
|
||||
j = (base + i) % g_fds.n;
|
||||
if (g_fds.p[j].kind == kFdProcess) {
|
||||
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {
|
||||
pids[count] = j;
|
||||
handles[count] = g_fds.p[j].handle;
|
||||
if (++count == 64) {
|
||||
|
|
|
@ -19,11 +19,16 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/ntdll.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
|
||||
textwindows int sys_sched_yield_nt(void) {
|
||||
size_t i;
|
||||
if (NtYieldExecution() == kNtStatusDllNotFound) {
|
||||
for (i = 0; i < 16; ++i) asm("pause");
|
||||
}
|
||||
// A value of zero, together with the bAlertable parameter set to
|
||||
// FALSE, causes the thread to relinquish the remainder of its time
|
||||
// slice to any other thread that is ready to run, if there are no
|
||||
// pending user APCs on the calling thread. If there are no other
|
||||
// threads ready to run and no user APCs are queued, the function
|
||||
// returns immediately, and the thread continues execution.
|
||||
// ──Quoth MSDN
|
||||
SleepEx(0, false);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,20 +16,31 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/itimerval.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/nexgen32e/nt2sysv.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Heartbreaking polyfill for SIGALRM on NT.
|
||||
|
@ -45,59 +56,68 @@
|
|||
* interrupting i/o operations on the standard input handle.
|
||||
*/
|
||||
|
||||
static struct ItimerNt {
|
||||
int64_t ith;
|
||||
uint32_t tid;
|
||||
struct itimerval itv;
|
||||
} g_itimernt;
|
||||
static bool __hastimer;
|
||||
static bool __singleshot;
|
||||
static long double __lastalrm;
|
||||
static long double __interval;
|
||||
|
||||
static uint32_t ItimerWorker(void *arg) {
|
||||
do {
|
||||
if (!WaitForSingleObject(g_itimernt.ith, -1)) {
|
||||
__winalarm(NULL, 0, 0);
|
||||
textwindows void _check_sigalrm(void) {
|
||||
// TODO(jart): use a different timing source
|
||||
// TODO(jart): synchronize across intervals?
|
||||
long double now, elapsed;
|
||||
if (!__hastimer) return;
|
||||
now = nowl();
|
||||
elapsed = now - __lastalrm;
|
||||
if (elapsed > __interval) {
|
||||
__sig_add(SIGALRM, SI_TIMER);
|
||||
if (__singleshot) {
|
||||
__hastimer = false;
|
||||
} else {
|
||||
__lastalrm = now;
|
||||
}
|
||||
} while (g_itimernt.ith && g_itimernt.tid == GetCurrentThreadId());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int sys_setitimer_nt(int which, const struct itimerval *newvalue,
|
||||
struct itimerval *out_opt_oldvalue) {
|
||||
int32_t period;
|
||||
int64_t ith, duetime;
|
||||
long double elapsed, untilnext;
|
||||
if (which != ITIMER_REAL) return einval();
|
||||
if (newvalue) {
|
||||
if (newvalue->it_value.tv_sec && newvalue->it_value.tv_usec) {
|
||||
if (!(ith = CreateWaitableTimer(NULL, false, NULL))) {
|
||||
return __winerr();
|
||||
}
|
||||
duetime = -(newvalue->it_value.tv_sec * HECTONANOSECONDS +
|
||||
newvalue->it_value.tv_usec * 10);
|
||||
period = newvalue->it_value.tv_sec * 1000 +
|
||||
div1000int64(newvalue->it_value.tv_usec);
|
||||
if (!period && newvalue->it_value.tv_usec) period = 1;
|
||||
if (!SetWaitableTimer(ith, &duetime, period, NULL, NULL, false)) {
|
||||
errno = GetLastError();
|
||||
CloseHandle(ith);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ith = 0;
|
||||
}
|
||||
if (g_itimernt.ith) {
|
||||
CloseHandle(g_itimernt.ith);
|
||||
g_itimernt.ith = 0;
|
||||
}
|
||||
} else {
|
||||
ith = 0;
|
||||
}
|
||||
if (out_opt_oldvalue) {
|
||||
memcpy(out_opt_oldvalue, &g_itimernt.itv, sizeof(struct itimerval));
|
||||
if (__hastimer) {
|
||||
elapsed = nowl() - __lastalrm;
|
||||
if (elapsed > __interval) {
|
||||
untilnext = 0;
|
||||
} else {
|
||||
untilnext = __interval - elapsed;
|
||||
}
|
||||
out_opt_oldvalue->it_interval.tv_sec = __interval;
|
||||
out_opt_oldvalue->it_interval.tv_usec = 1 / 1e6 * fmodl(__interval, 1);
|
||||
out_opt_oldvalue->it_value.tv_sec = untilnext;
|
||||
out_opt_oldvalue->it_value.tv_usec = 1 / 1e6 * fmodl(untilnext, 1);
|
||||
} else {
|
||||
out_opt_oldvalue->it_interval.tv_sec = 0;
|
||||
out_opt_oldvalue->it_interval.tv_usec = 0;
|
||||
out_opt_oldvalue->it_value.tv_sec = 0;
|
||||
out_opt_oldvalue->it_value.tv_usec = 0;
|
||||
}
|
||||
}
|
||||
if (ith) {
|
||||
g_itimernt.ith = ith;
|
||||
memcpy(&g_itimernt.itv, newvalue, sizeof(struct itimerval));
|
||||
CloseHandle(
|
||||
CreateThread(NULL, STACKSIZE, ItimerWorker, NULL, 0, &g_itimernt.tid));
|
||||
if (newvalue) {
|
||||
if (newvalue->it_interval.tv_sec || newvalue->it_interval.tv_usec ||
|
||||
newvalue->it_value.tv_sec || newvalue->it_value.tv_usec) {
|
||||
__hastimer = true;
|
||||
if (newvalue->it_interval.tv_sec || newvalue->it_interval.tv_usec) {
|
||||
__singleshot = false;
|
||||
__interval = newvalue->it_interval.tv_sec +
|
||||
1 / 1e6 * newvalue->it_interval.tv_usec;
|
||||
} else {
|
||||
__singleshot = true;
|
||||
__interval =
|
||||
newvalue->it_value.tv_sec + 1 / 1e6 * newvalue->it_value.tv_usec;
|
||||
}
|
||||
__lastalrm = nowl();
|
||||
} else {
|
||||
__hastimer = false;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
220
libc/calls/sig.c
Normal file
220
libc/calls/sig.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*-*- 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 2022 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/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* @fileoverview UNIX signals for the New Technology.
|
||||
* @threadsafe
|
||||
*/
|
||||
|
||||
#define LOCK \
|
||||
for (;;) \
|
||||
if (lockcmpxchg(&__sig.lock, false, true)) { \
|
||||
asm volatile("" ::: "memory")
|
||||
|
||||
#define UNLOCK \
|
||||
asm volatile("" ::: "memory"); \
|
||||
__sig.lock = false; \
|
||||
break; \
|
||||
} \
|
||||
else sched_yield()
|
||||
|
||||
struct Signal {
|
||||
struct Signal *next;
|
||||
bool used;
|
||||
int sig;
|
||||
int si_code;
|
||||
};
|
||||
|
||||
struct Signals {
|
||||
bool lock;
|
||||
sigset_t mask;
|
||||
struct Signal *queue;
|
||||
struct Signal mem[__SIG_QUEUE_LENGTH];
|
||||
};
|
||||
|
||||
struct Signals __sig;
|
||||
|
||||
/**
|
||||
* Allocates piece of memory for storing pending signal.
|
||||
*/
|
||||
static textwindows struct Signal *__sig_alloc(void) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAYLEN(__sig.mem); ++i) {
|
||||
if (!__sig.mem[i].used && lockcmpxchg(&__sig.mem[i].used, false, true)) {
|
||||
return __sig.mem + i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns signal memory to static pool.
|
||||
*/
|
||||
static textwindows void __sig_free(struct Signal *mem) {
|
||||
mem->used = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues signal that isn't masked.
|
||||
* @return signal or null if empty or none unmasked
|
||||
*/
|
||||
static textwindows struct Signal *__sig_remove(void) {
|
||||
struct Signal *prev, *res;
|
||||
if (__sig.queue) {
|
||||
LOCK;
|
||||
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
|
||||
if (!sigismember(&__sig.mask, res->sig)) {
|
||||
if (res == __sig.queue) {
|
||||
__sig.queue = res->next;
|
||||
} else if (prev) {
|
||||
prev->next = res->next;
|
||||
}
|
||||
res->next = 0;
|
||||
break;
|
||||
} else {
|
||||
STRACE("%s is masked", strsignal(res->sig));
|
||||
}
|
||||
}
|
||||
UNLOCK;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers signal.
|
||||
* @note called from main thread
|
||||
* @return true if EINTR should be returned by caller
|
||||
*/
|
||||
static textwindows bool __sig_deliver(bool restartable, struct Signal *sig,
|
||||
unsigned rva) {
|
||||
unsigned flags;
|
||||
siginfo_t info;
|
||||
flags = __sighandflags[sig->sig];
|
||||
// TODO(jart): polyfill prevention of re-entry
|
||||
if (flags & SA_RESETHAND) {
|
||||
__sighandrvas[sig->sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
STRACE("delivering %s", strsignal(sig->sig));
|
||||
bzero(&info, sizeof(info));
|
||||
info.si_signo = sig->sig;
|
||||
info.si_code = sig->si_code;
|
||||
((sigaction_f)(_base + rva))(sig->sig, &info, 0);
|
||||
if (!restartable) {
|
||||
return true; // always send EINTR for wait4(), poll(), etc.
|
||||
} else if (flags & SA_RESTART) {
|
||||
STRACE("restarting syscall on %s", strsignal(sig->sig));
|
||||
return false; // resume syscall for read(), write(), etc.
|
||||
} else {
|
||||
return true; // default course is to raise EINTR
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if signal default action is to end process.
|
||||
*/
|
||||
static textwindows bool __sig_isfatal(int sig) {
|
||||
return sig != SIGCHLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues generic signal for delivery on New Technology.
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows int __sig_add(int sig, int si_code) {
|
||||
struct Signal *mem;
|
||||
if (1 <= sig && sig <= NSIG) {
|
||||
if ((mem = __sig_alloc())) {
|
||||
mem->sig = sig;
|
||||
mem->si_code = si_code;
|
||||
LOCK;
|
||||
mem->next = __sig.queue;
|
||||
__sig.queue = mem;
|
||||
UNLOCK;
|
||||
return 0;
|
||||
} else {
|
||||
return enomem();
|
||||
}
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues generic signal for delivery on New Technology.
|
||||
*
|
||||
* @param restartable is for functions like read() but not poll()
|
||||
* @return true if EINTR should be returned by caller
|
||||
* @note called from main thread
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows bool __sig_check(bool restartable) {
|
||||
unsigned rva;
|
||||
bool delivered;
|
||||
struct Signal *sig;
|
||||
delivered = false;
|
||||
while ((sig = __sig_remove())) {
|
||||
switch ((rva = __sighandrvas[sig->sig])) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
if (__sig_isfatal(sig->sig)) {
|
||||
STRACE("terminating on %s", strsignal(sig->sig));
|
||||
__restorewintty();
|
||||
_Exit(128 + sig->sig);
|
||||
}
|
||||
// fallthrough
|
||||
case (intptr_t)SIG_IGN:
|
||||
STRACE("ignoring %s", strsignal(sig->sig));
|
||||
break;
|
||||
default:
|
||||
delivered = __sig_deliver(restartable, sig, rva);
|
||||
break;
|
||||
}
|
||||
__sig_free(sig);
|
||||
}
|
||||
return delivered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes signal mask for main thread.
|
||||
* @return old mask
|
||||
*/
|
||||
textwindows sigset_t __sig_mask(const sigset_t *neu) {
|
||||
sigset_t old;
|
||||
LOCK;
|
||||
old = __sig.mask;
|
||||
if (neu) __sig.mask = *neu;
|
||||
UNLOCK;
|
||||
return old;
|
||||
}
|
17
libc/calls/sig.internal.h
Normal file
17
libc/calls/sig.internal.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
||||
#define __SIG_QUEUE_LENGTH 8
|
||||
#define __SIG_POLLING_INTERVAL_MS 50
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
bool __sig_check(bool) hidden;
|
||||
int __sig_add(int, int) hidden;
|
||||
sigset_t __sig_mask(const sigset_t *) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */
|
|
@ -219,6 +219,7 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
}
|
||||
if (act) {
|
||||
__sighandrvas[sig] = rva;
|
||||
__sighandflags[sig] = act->sa_flags;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
@ -232,6 +233,7 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
|
||||
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
|
||||
*
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @see xsigaction() for a much better api
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
|
|
|
@ -16,40 +16,46 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
/**
|
||||
* Checks to see if SIGCHLD should be raised on Windows.
|
||||
* @return true if a signal was raised
|
||||
* @note yoinked by fork-nt.c
|
||||
*/
|
||||
bool _check_sigchld(void) {
|
||||
void _check_sigchld(void) {
|
||||
siginfo_t si;
|
||||
int pids[64];
|
||||
uint32_t i, n;
|
||||
int64_t handles[64];
|
||||
if (__sighandrvas[SIGCHLD] < kSigactionMinRva) return false;
|
||||
if (!(n = __sample_pids(pids, handles))) return false;
|
||||
if (!(n = __sample_pids(pids, handles, true))) return;
|
||||
i = WaitForMultipleObjects(n, handles, false, 0);
|
||||
if (i == kNtWaitTimeout) return false;
|
||||
if (i == kNtWaitTimeout) return;
|
||||
if (i == kNtWaitFailed) {
|
||||
STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
STRACE("SIGCHLD fd=%d handle=%ld", pids[i], handles[i]);
|
||||
bzero(&si, sizeof(si));
|
||||
si.si_signo = SIGCHLD;
|
||||
si.si_code = CLD_EXITED;
|
||||
si.si_pid = pids[i];
|
||||
((sigaction_f)(_base + __sighandrvas[SIGCHLD]))(SIGCHLD, &si, 0);
|
||||
return true;
|
||||
if (__sighandrvas[SIGCHLD] == (intptr_t)SIG_IGN) {
|
||||
STRACE("killing zombie fd=%d handle=%ld", pids[i], handles[i]);
|
||||
CloseHandle(handles[i]);
|
||||
__releasefd(pids[i]);
|
||||
return;
|
||||
}
|
||||
if (__sighandflags[SIGCHLD] & SA_NOCLDWAIT) {
|
||||
STRACE("SIGCHILD SA_NOCLDWAIT fd=%d handle=%ld", pids[i], handles[i]);
|
||||
CloseHandle(handles[i]);
|
||||
__releasefd(pids[i]);
|
||||
}
|
||||
g_fds.p[pids[i]].zombie = true;
|
||||
__sig_add(SIGCHLD, CLD_EXITED);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -39,7 +41,7 @@ static const char *DescribeHow(char buf[12], int how) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Changes program signal blocking state, e.g.:
|
||||
* Changes signal blocking state of calling thread, e.g.:
|
||||
*
|
||||
* sigset_t neu,old;
|
||||
* sigfillset(&neu);
|
||||
|
@ -51,56 +53,45 @@ static const char *DescribeHow(char buf[12], int how) {
|
|||
* @param oldset will receive the old mask (optional) and can't overlap
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
||||
int x, rc;
|
||||
sigset_t old;
|
||||
char howbuf[12];
|
||||
char buf[2][41];
|
||||
sigset_t old, *oldp;
|
||||
const sigset_t *arg;
|
||||
if (!(IsAsan() &&
|
||||
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
|
||||
(opt_out_oldset &&
|
||||
!__asan_is_valid(opt_out_oldset, sizeof(*opt_out_oldset)))))) {
|
||||
if (!IsWindows() && !IsOpenbsd()) {
|
||||
if (opt_out_oldset) {
|
||||
bzero(&old, sizeof(old));
|
||||
oldp = &old;
|
||||
} else {
|
||||
oldp = 0;
|
||||
}
|
||||
if (sys_sigprocmask(how, opt_set, oldp, 8) != -1) {
|
||||
if (opt_out_oldset) {
|
||||
memcpy(opt_out_oldset, &old, sizeof(old));
|
||||
}
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else if (IsOpenbsd()) {
|
||||
if (opt_set) {
|
||||
/* openbsd only supports 32 signals so it passses them in a reg */
|
||||
arg = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set);
|
||||
} else {
|
||||
how = 1; /* SIG_BLOCK */
|
||||
arg = 0; /* changes nothing */
|
||||
}
|
||||
if ((x = sys_sigprocmask(how, arg, 0, 0)) != -1) {
|
||||
if (opt_out_oldset) {
|
||||
bzero(opt_out_oldset, sizeof(*opt_out_oldset));
|
||||
memcpy(opt_out_oldset, &x, sizeof(x));
|
||||
}
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
if (opt_out_oldset) bzero(opt_out_oldset, sizeof(*opt_out_oldset));
|
||||
rc = 0; /* TODO(jart): Implement me! */
|
||||
}
|
||||
} else {
|
||||
int res, rc, arg1;
|
||||
const sigset_t *arg2;
|
||||
sigemptyset(&old);
|
||||
if (IsAsan() &&
|
||||
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
|
||||
(opt_out_oldset &&
|
||||
!__asan_is_valid(opt_out_oldset, sizeof(*opt_out_oldset))))) {
|
||||
rc = efault();
|
||||
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsNetbsd()) {
|
||||
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0, 8);
|
||||
} else if (IsOpenbsd()) {
|
||||
if (opt_set) {
|
||||
// openbsd only supports 32 signals so it passses them in a reg
|
||||
arg1 = how;
|
||||
arg2 = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set);
|
||||
} else {
|
||||
arg1 = how; // SIG_BLOCK
|
||||
arg2 = 0; // changes nothing
|
||||
}
|
||||
if ((res = sys_sigprocmask(arg1, arg2, 0, 0)) != -1) {
|
||||
memcpy(&old, &res, sizeof(res));
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else { // windows or metal
|
||||
old = __sig_mask(opt_set);
|
||||
_check_interrupts(false, 0);
|
||||
rc = 0;
|
||||
}
|
||||
if (rc != -1 && opt_out_oldset) {
|
||||
*opt_out_oldset = old;
|
||||
}
|
||||
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
|
||||
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set),
|
||||
|
|
|
@ -18,32 +18,64 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Blocks until SIG ∉ MASK is delivered to process.
|
||||
*
|
||||
* @param ignore is a bitset of signals to block temporarily
|
||||
* @return -1 w/ EINTR
|
||||
* @return -1 w/ EINTR or possibly EFAULT
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
int sigsuspend(const sigset_t *ignore) {
|
||||
int rc;
|
||||
char buf[41];
|
||||
if (!ignore || (IsAsan() && !__asan_is_valid(ignore, sizeof(*ignore)))) {
|
||||
sigset_t save, mask, *arg;
|
||||
STRACE("sigsuspend(%s) → [...]",
|
||||
__strace_sigset(buf, sizeof(buf), 0, ignore));
|
||||
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
STRACE("sigsuspend(%s)", __strace_sigset(buf, sizeof(buf), 0, ignore));
|
||||
if (IsOpenbsd()) ignore = (sigset_t *)(uintptr_t)(*(uint32_t *)ignore);
|
||||
return sys_sigsuspend(ignore, 8);
|
||||
} else if (IsXnu() || IsOpenbsd()) {
|
||||
// openbsd and xnu only support 32 signals
|
||||
// they use a register calling convention for sigsuspend
|
||||
if (ignore) {
|
||||
arg = (sigset_t *)(uintptr_t)(*(uint32_t *)ignore);
|
||||
} else {
|
||||
arg = 0;
|
||||
}
|
||||
rc = sys_sigsuspend(arg, 8);
|
||||
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsWindows()) {
|
||||
if (ignore) {
|
||||
arg = ignore;
|
||||
} else {
|
||||
sigemptyset(&mask);
|
||||
arg = &mask;
|
||||
}
|
||||
if (!IsWindows()) {
|
||||
rc = sys_sigsuspend(arg, 8);
|
||||
} else {
|
||||
save = __sig_mask(arg);
|
||||
do {
|
||||
if (_check_interrupts(false, g_fds.p)) {
|
||||
rc = eintr();
|
||||
break;
|
||||
}
|
||||
SleepEx(__SIG_POLLING_INTERVAL_MS, true);
|
||||
} while (1);
|
||||
__sig_mask(&save);
|
||||
}
|
||||
} else {
|
||||
rc = enosys(); /* TODO(jart): Implement me! */
|
||||
// TODO(jart): sigsuspend metal support
|
||||
rc = enosys();
|
||||
}
|
||||
STRACE("sigsuspend(%s) → %d% m", __strace_sigset(buf, sizeof(buf), 0, ignore),
|
||||
rc);
|
||||
STRACE("[...] sigsuspend → %d% m", rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,18 +16,23 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/struct/consolescreenbufferinfoex.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
static struct winsize __ws;
|
||||
|
||||
textwindows bool _check_sigwinch(struct Fd *fd) {
|
||||
textwindows void _check_sigwinch(struct Fd *fd) {
|
||||
int e;
|
||||
siginfo_t si;
|
||||
struct winsize ws, old;
|
||||
|
@ -39,14 +44,7 @@ textwindows bool _check_sigwinch(struct Fd *fd) {
|
|||
if (old.ws_col != ws.ws_col || old.ws_row != ws.ws_row) {
|
||||
__ws = ws;
|
||||
if (old.ws_col | old.ws_row) {
|
||||
STRACE("SIGWINCH %hhu×%hhu → %hhu×%hhu", old.ws_col, old.ws_row,
|
||||
ws.ws_col, ws.ws_row);
|
||||
if (__sighandrvas[SIGWINCH] >= kSigactionMinRva) {
|
||||
bzero(&si, sizeof(si));
|
||||
si.si_signo = SIGWINCH;
|
||||
((sigaction_f)(_base + __sighandrvas[SIGWINCH]))(SIGWINCH, &si, 0);
|
||||
return true;
|
||||
}
|
||||
__sig_add(SIGWINCH, SI_KERNEL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -56,5 +54,4 @@ textwindows bool _check_sigwinch(struct Fd *fd) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -16,17 +16,17 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/**
|
||||
* Sleeps for a particular amount of time.
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
int sleep(uint32_t seconds) {
|
||||
int rc;
|
||||
rc = nanosleep(&(struct timespec){seconds, 0}, NULL);
|
||||
STRACE("sleep(%u) → %d% m", seconds, rc);
|
||||
return rc;
|
||||
return nanosleep(&(struct timespec){seconds, 0}, NULL);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ const char *__strace_sigaction(char *, size_t, int, const struct sigaction *);
|
|||
const char *__strace_sigset(char[41], size_t, int, const sigset_t *);
|
||||
const char *__strace_rlimit_name(int);
|
||||
const char *__strace_rlimit(char[41], size_t, int, const struct rlimit *);
|
||||
const char *__strace_timespec(char[45], size_t, int, const struct timespec *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
29
libc/calls/strace_timespec.greg.c
Normal file
29
libc/calls/strace_timespec.greg.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- 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 2021 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/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
||||
privileged const char *__strace_timespec(char buf[45], size_t bufsize, int rc,
|
||||
const struct timespec *ts) {
|
||||
if (rc == -1) return "n/a";
|
||||
if (!ts) return "NULL";
|
||||
ksnprintf(buf, bufsize, "{%ld, %ld}", ts->tv_sec, ts->tv_nsec);
|
||||
return buf;
|
||||
}
|
|
@ -24,7 +24,7 @@ COSMOPOLITAN_C_START_
|
|||
|
||||
void _init_onntconsoleevent(void);
|
||||
void _init_wincrash(void);
|
||||
bool _check_sigwinch();
|
||||
void _check_sigwinch();
|
||||
|
||||
#ifndef __SIGACTION_YOINK
|
||||
#define __SIGACTION_YOINK(SIG) \
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- 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 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2022 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 │
|
||||
|
@ -18,4 +18,4 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
|
||||
volatile bool __interrupted;
|
||||
bool __time_critical;
|
|
@ -56,17 +56,33 @@ int uname(struct utsname *lool) {
|
|||
} else {
|
||||
bzero(tmp, sizeof(tmp));
|
||||
if (!IsWindows()) {
|
||||
if ((rc = sys_uname(tmp)) != -1) {
|
||||
out = (char *)lool;
|
||||
for (i = j = 0;;) {
|
||||
len = strlen(&tmp[j]);
|
||||
if (len >= sizeof(struct utsname) - i) break;
|
||||
memcpy(&out[i], &tmp[j], len + 1);
|
||||
i += SYS_NMLN;
|
||||
j += len;
|
||||
while (j < sizeof(tmp) && tmp[j] == '\0') ++j;
|
||||
if (j == sizeof(tmp)) break;
|
||||
if (IsLinux() || IsFreebsd()) {
|
||||
if ((rc = sys_uname(tmp)) != -1) {
|
||||
out = (char *)lool;
|
||||
for (i = j = 0;;) {
|
||||
len = strlen(&tmp[j]);
|
||||
if (len >= sizeof(struct utsname) - i) break;
|
||||
memcpy(&out[i], &tmp[j], len + 1);
|
||||
i += SYS_NMLN;
|
||||
j += len;
|
||||
while (j < sizeof(tmp) && tmp[j] == '\0') ++j;
|
||||
if (j == sizeof(tmp)) break;
|
||||
}
|
||||
}
|
||||
} else if (IsXnu()) {
|
||||
strcpy(lool->sysname, "XNU's Not UNIX!");
|
||||
gethostname_bsd(lool->nodename, sizeof(lool->nodename));
|
||||
rc = 0;
|
||||
} else if (IsOpenbsd()) {
|
||||
strcpy(lool->sysname, "OpenBSD");
|
||||
gethostname_bsd(lool->nodename, sizeof(lool->nodename));
|
||||
rc = 0;
|
||||
} else if (IsNetbsd()) {
|
||||
strcpy(lool->sysname, "NetBSD");
|
||||
gethostname_bsd(lool->nodename, sizeof(lool->nodename));
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
} else {
|
||||
v = NtGetVersion();
|
||||
|
|
|
@ -16,18 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/**
|
||||
* Sleeps for particular amount of microseconds.
|
||||
* @norestart
|
||||
*/
|
||||
int usleep(uint32_t microseconds) {
|
||||
int rc;
|
||||
rc = nanosleep(&(struct timespec){(uint64_t)microseconds / 1000000,
|
||||
(uint64_t)microseconds % 1000000 * 1000},
|
||||
NULL);
|
||||
STRACE("usleep(%'u) → %d% m", microseconds, rc);
|
||||
return rc;
|
||||
return nanosleep(&(struct timespec){(uint64_t)microseconds / 1000000,
|
||||
(uint64_t)microseconds % 1000000 * 1000},
|
||||
NULL);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* may be inspected using WEEXITSTATUS(), etc.
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
int wait(int *opt_out_wstatus) {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
* @param opt_out_rusage optionally returns accounting data
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int wait3(int *opt_out_wstatus, int options, struct rusage *opt_out_rusage) {
|
||||
return wait4(-1, opt_out_wstatus, options, opt_out_rusage);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include "libc/nt/struct/processmemorycounters.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/rand/lcg.internal.h"
|
||||
#include "libc/runtime/ezmap.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
@ -46,9 +48,11 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
|||
int64_t handle;
|
||||
int64_t handles[64];
|
||||
uint32_t dwExitCode;
|
||||
bool shouldinterrupt;
|
||||
uint32_t i, j, base, count, timeout;
|
||||
struct NtProcessMemoryCountersEx memcount;
|
||||
struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime;
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
if (pid != -1 && pid != 0) {
|
||||
if (pid < 0) {
|
||||
/* XXX: this is sloppy */
|
||||
|
@ -75,10 +79,11 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
|||
pids[0] = pid;
|
||||
count = 1;
|
||||
} else {
|
||||
count = __sample_pids(pids, handles);
|
||||
count = __sample_pids(pids, handles, false);
|
||||
if (!count) return echild();
|
||||
}
|
||||
for (;;) {
|
||||
if (_check_interrupts(true, 0)) return eintr();
|
||||
dwExitCode = kNtStillActive;
|
||||
if (options & WNOHANG) {
|
||||
i = WaitForMultipleObjects(count, handles, false, 0);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
* @param opt_out_rusage optionally returns accounting data
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int wait4(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int waitpid(int pid, int *opt_out_wstatus, int options) {
|
||||
return wait4(pid, opt_out_wstatus, options, NULL);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
@ -35,6 +36,7 @@ static textwindows ssize_t sys_write_nt_epipe(int fd) {
|
|||
siginfo_t info;
|
||||
STRACE("WriteFile(%d:%p) → %m", fd, g_fds.p[fd].handle);
|
||||
if (!__sighandrvas[SIGPIPE]) {
|
||||
__restorewintty();
|
||||
_Exit(128 + SIGPIPE);
|
||||
} else if (__sighandrvas[SIGPIPE] >= kSigactionMinRva) {
|
||||
bzero(&info, sizeof(info));
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
* impossible unless size was passed as zero to do an error check
|
||||
* @see read(), pwrite(), writev(), SIGPIPE
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
ssize_t write(int fd, const void *buf, size_t size) {
|
||||
ssize_t rc;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
* call using write().
|
||||
*
|
||||
* @return number of bytes actually handed off, or -1 w/ errno
|
||||
* @restartable
|
||||
*/
|
||||
ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
|
||||
ssize_t rc;
|
||||
|
|
|
@ -29,7 +29,7 @@ privileged const char *strerror_short(int x) {
|
|||
int i;
|
||||
if (x) {
|
||||
for (i = 0; kErrorNames[i].x; ++i) {
|
||||
if (x == *(const *)((uintptr_t)kErrorNames + kErrorNames[i].x)) {
|
||||
if (x == *(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x)) {
|
||||
return (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s);
|
||||
}
|
||||
}
|
||||
|
|
45
libc/intrin/createthread.greg.c
Normal file
45
libc/intrin/createthread.greg.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*-*- 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 2022 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/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/nt/struct/securityattributes.h"
|
||||
#include "libc/nt/thread.h"
|
||||
|
||||
extern typeof(CreateThread) *const __imp_CreateThread __msabi;
|
||||
|
||||
/**
|
||||
* Opens file on the New Technology.
|
||||
*
|
||||
* @return thread handle, or 0 on failure
|
||||
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
|
||||
*/
|
||||
textwindows int64_t CreateThread(
|
||||
struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize,
|
||||
NtThreadStartRoutine lpStartAddress, void *lpParameter,
|
||||
uint32_t dwCreationFlags, uint32_t *opt_lpThreadId) {
|
||||
int64_t hHandle;
|
||||
hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
|
||||
lpParameter, dwCreationFlags, opt_lpThreadId);
|
||||
if (hHandle == -1) __winerr();
|
||||
STRACE("CreateThread(sec=%p, stack=%'zu, start=%p, param=%p, flags=%s, "
|
||||
"id=%p) → %ld% m",
|
||||
lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter,
|
||||
dwCreationFlags, opt_lpThreadId, hHandle);
|
||||
return hHandle;
|
||||
}
|
|
@ -276,37 +276,47 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
switch ((c = *f++)) {
|
||||
default:
|
||||
goto EmitFormatByte;
|
||||
|
||||
case '\0':
|
||||
break;
|
||||
|
||||
case '.':
|
||||
pdot = 1;
|
||||
continue;
|
||||
|
||||
case '-':
|
||||
flip = 1;
|
||||
continue;
|
||||
|
||||
case '#':
|
||||
hash = '0';
|
||||
continue;
|
||||
|
||||
case '_':
|
||||
case ',':
|
||||
case '\'':
|
||||
quot = c;
|
||||
continue;
|
||||
|
||||
case ' ':
|
||||
case '+':
|
||||
sign = c;
|
||||
continue;
|
||||
|
||||
case 'h':
|
||||
--type;
|
||||
continue;
|
||||
|
||||
case 'j':
|
||||
case 'l':
|
||||
case 'z':
|
||||
++type;
|
||||
continue;
|
||||
|
||||
case '!':
|
||||
dang = 1;
|
||||
continue;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
|
@ -321,6 +331,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
si *= 10;
|
||||
si += c - '0';
|
||||
goto UpdateCols;
|
||||
|
||||
case '*':
|
||||
si = va_arg(va, int);
|
||||
UpdateCols:
|
||||
|
@ -339,9 +350,11 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
}
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'T':
|
||||
x = ClocksToNanos(ts.start, ts.birth) % 86400000000000;
|
||||
goto FormatUnsigned;
|
||||
|
||||
case 'P':
|
||||
if (!__vforked) {
|
||||
x = __pid;
|
||||
|
@ -352,6 +365,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
: "rcx", "rdx", "r11", "memory", "cc");
|
||||
}
|
||||
goto FormatUnsigned;
|
||||
|
||||
case 'u':
|
||||
case 'd':
|
||||
if (UNLIKELY(type <= -3)) {
|
||||
|
@ -416,6 +430,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
base = 1;
|
||||
if (hash) hash = '0' | 'b' << 8;
|
||||
|
@ -428,6 +443,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
do z[i++ & 127] = abet[x & m];
|
||||
while ((x >>= base) || (pdot && i < prec));
|
||||
goto EmitNumber;
|
||||
|
||||
case 'X':
|
||||
abet = "0123456789ABCDEF";
|
||||
/* fallthrough */
|
||||
|
@ -435,9 +451,11 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
base = 4;
|
||||
if (hash) hash = '0' | 'x' << 8;
|
||||
goto BinaryNumber;
|
||||
|
||||
case 'o':
|
||||
base = 3;
|
||||
goto BinaryNumber;
|
||||
|
||||
case 'p':
|
||||
x = va_arg(va, intptr_t);
|
||||
if (!x && pdot) pdot = 0;
|
||||
|
@ -448,10 +466,11 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
hash = '0' | 'x' << 8;
|
||||
base = 4;
|
||||
goto FormatNumber;
|
||||
|
||||
case 'C':
|
||||
c = 'c';
|
||||
type = 1;
|
||||
/* fallthrough */
|
||||
// fallthrough
|
||||
case 'c':
|
||||
i = 1;
|
||||
j = 0;
|
||||
|
@ -463,9 +482,9 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
quot = 1;
|
||||
hash = '\'';
|
||||
p = kemitquote(p, e, type, hash);
|
||||
if (cols && type) --cols; /* u/L */
|
||||
if (cols) --cols; /* start quote */
|
||||
if (cols) --cols; /* end quote */
|
||||
if (cols && type) --cols; // u/L
|
||||
if (cols) --cols; // start quote
|
||||
if (cols) --cols; // end quote
|
||||
}
|
||||
goto EmitChar;
|
||||
|
||||
|
@ -506,7 +525,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
case 'S':
|
||||
c = 's';
|
||||
type = 1;
|
||||
/* fallthrough */
|
||||
// fallthrough
|
||||
case 's':
|
||||
if (!(s = va_arg(va, const void *))) {
|
||||
s = sign != ' ' ? "NULL" : "";
|
||||
|
@ -525,9 +544,9 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
|
|||
} else if (hash) {
|
||||
quot = 1;
|
||||
hash = '"';
|
||||
if (cols && type) --cols; /* u/L */
|
||||
if (cols) --cols; /* start quote */
|
||||
if (cols) --cols; /* end quote */
|
||||
if (cols && type) --cols; // u/L
|
||||
if (cols) --cols; // start quote
|
||||
if (cols) --cols; // end quote
|
||||
p = kemitquote(p, e, type, hash);
|
||||
}
|
||||
if (sign == ' ' && (!pdot || prec) && *s) {
|
||||
|
|
|
@ -307,7 +307,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
|||
DebugBreak();
|
||||
} else if (__nocolor || g_isrunningundermake) {
|
||||
gdbpid = -1;
|
||||
} else if (IsLinux() && FindDebugBinary()) {
|
||||
} else if (!IsTiny() && IsLinux() && FindDebugBinary()) {
|
||||
RestoreDefaultCrashSignalHandlers();
|
||||
gdbpid = AttachDebugger(
|
||||
((sig == SIGTRAP || sig == SIGQUIT) &&
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
.imp kernel32,__imp_CreateThread,CreateThread,0
|
||||
|
||||
.text.windows
|
||||
CreateThread:
|
||||
__CreateThread:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable
|
||||
mov __imp_CreateThread(%rip),%rax
|
||||
jmp __sysv2nt6
|
||||
.endfn CreateThread,globl
|
||||
.endfn __CreateThread,globl
|
||||
.previous
|
||||
|
|
|
@ -685,7 +685,7 @@ imp 'CreateSymbolicLinkTransactedA' CreateSymbolicLinkTransactedA kernel32
|
|||
imp 'CreateSymbolicLinkTransacted' CreateSymbolicLinkTransactedW kernel32 238
|
||||
imp 'CreateSystemThreads' CreateSystemThreads user32 1623
|
||||
imp 'CreateTapePartition' CreateTapePartition kernel32 240
|
||||
imp 'CreateThread' CreateThread kernel32 0 6 # KernelBase
|
||||
imp '__CreateThread' CreateThread kernel32 0 6 # KernelBase
|
||||
imp 'CreateThreadpool' CreateThreadpool kernel32 0 # KernelBase
|
||||
imp 'CreateThreadpoolCleanupGroup' CreateThreadpoolCleanupGroup kernel32 0 # KernelBase
|
||||
imp 'CreateThreadpoolIo' CreateThreadpoolIo kernel32 0 # KernelBase
|
||||
|
|
|
@ -62,6 +62,7 @@ static bool have_getrandom;
|
|||
* close any file descriptor it ends up needing before it returns.
|
||||
*
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t getrandom(void *p, size_t n, unsigned f) {
|
||||
|
|
|
@ -96,7 +96,7 @@ privileged int clone(int (*f)(void *), void *stack, int flags, void *arg, ...) {
|
|||
CloseHandle(h);
|
||||
return tid;
|
||||
} else {
|
||||
return __winerr();
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return einval();
|
||||
|
|
|
@ -314,24 +314,13 @@ textwindows int sys_fork_nt(void) {
|
|||
}
|
||||
}
|
||||
if (ok) {
|
||||
if (!weaken(__sighandrvas) ||
|
||||
weaken(__sighandrvas)[SIGCHLD] != SIG_IGN) {
|
||||
g_fds.p[pid].kind = kFdProcess;
|
||||
g_fds.p[pid].handle = procinfo.hProcess;
|
||||
g_fds.p[pid].flags = O_CLOEXEC;
|
||||
untrackpid = -1;
|
||||
rc = pid;
|
||||
} else {
|
||||
/*
|
||||
* XXX: Ignoring SIGCHLD should track the process information.
|
||||
* What we need to do instead, is periodically check if a
|
||||
* process has exited and remove it automatically via i/o
|
||||
* functions like poll() so it doesn't get zombdied.
|
||||
*/
|
||||
STRACE("fork() parent closing process handle b/c SIGCHLD=SIG_IGN");
|
||||
rc = GetProcessId(procinfo.hProcess);
|
||||
CloseHandle(procinfo.hProcess);
|
||||
}
|
||||
// XXX: this should be tracked in a separate data structure
|
||||
g_fds.p[pid].kind = kFdProcess;
|
||||
g_fds.p[pid].handle = procinfo.hProcess;
|
||||
g_fds.p[pid].flags = O_CLOEXEC;
|
||||
g_fds.p[pid].zombie = false;
|
||||
untrackpid = -1;
|
||||
rc = pid;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
TerminateProcess(procinfo.hProcess, 127);
|
||||
|
|
|
@ -16,12 +16,22 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
#define PRINT(FMT, ...) kprintf(STRACE_PROLOGUE FMT "%n", ##__VA_ARGS__)
|
||||
|
||||
static const struct AuxiliaryValue {
|
||||
const char *fmt;
|
||||
|
@ -77,40 +87,75 @@ static const struct AuxiliaryValue *DescribeAuxv(unsigned long x) {
|
|||
|
||||
textstartup void __printargs(void) {
|
||||
#ifdef SYSDEBUG
|
||||
int st;
|
||||
long key;
|
||||
char **env;
|
||||
unsigned i;
|
||||
sigset_t ss;
|
||||
uintptr_t *auxp;
|
||||
char path[PATH_MAX];
|
||||
struct pollfd pfds[128];
|
||||
struct AuxiliaryValue *auxinfo;
|
||||
STRACE("ARGUMENTS (%p)", __argv);
|
||||
if (__strace <= 0) return;
|
||||
st = __strace;
|
||||
__strace = 0;
|
||||
|
||||
PRINT("ARGUMENTS (%p)", __argv);
|
||||
for (i = 0; i < __argc; ++i) {
|
||||
STRACE(" ☼ %s", __argv[i]);
|
||||
PRINT(" ☼ %s", __argv[i]);
|
||||
}
|
||||
STRACE("ENVIRONMENT (%p)", __envp);
|
||||
|
||||
PRINT("ENVIRONMENT (%p)", __envp);
|
||||
for (env = __envp; *env; ++env) {
|
||||
STRACE(" ☼ %s", *env);
|
||||
PRINT(" ☼ %s", *env);
|
||||
}
|
||||
STRACE("AUXILIARY (%p)", __auxv);
|
||||
|
||||
PRINT("AUXILIARY (%p)", __auxv);
|
||||
for (auxp = __auxv; *auxp; auxp += 2) {
|
||||
if ((auxinfo = DescribeAuxv(auxp[0]))) {
|
||||
ksnprintf(path, sizeof(path), auxinfo->fmt, auxp[1]);
|
||||
STRACE(" ☼ %16s[%4ld] = %s", auxinfo->name, auxp[0], path);
|
||||
PRINT(" ☼ %16s[%4ld] = %s", auxinfo->name, auxp[0], path);
|
||||
} else {
|
||||
STRACE(" ☼ %16s[%4ld] = %014p", "unknown", auxp[0], auxp[1]);
|
||||
PRINT(" ☼ %16s[%4ld] = %014p", "unknown", auxp[0], auxp[1]);
|
||||
}
|
||||
}
|
||||
STRACE("SPECIALS");
|
||||
STRACE(" ☼ %30s = %#s", "kTmpPath", kTmpPath);
|
||||
STRACE(" ☼ %30s = %#s", "kNtSystemDirectory", kNtSystemDirectory);
|
||||
STRACE(" ☼ %30s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory);
|
||||
STRACE(" ☼ %30s = %#s", "program_executable_name",
|
||||
GetProgramExecutableName());
|
||||
STRACE(" ☼ %30s = %#s", "GetInterpreterExecutableName()",
|
||||
GetInterpreterExecutableName(path, sizeof(path)));
|
||||
STRACE(" ☼ %30s = %p", "RSP", __builtin_frame_address(0));
|
||||
STRACE(" ☼ %30s = %p", "GetStackAddr()", GetStackAddr(0));
|
||||
STRACE(" ☼ %30s = %p", "GetStaticStackAddr(0)", GetStaticStackAddr(0));
|
||||
STRACE(" ☼ %30s = %p", "GetStackSize()", GetStackSize());
|
||||
|
||||
PRINT("SPECIALS");
|
||||
PRINT(" ☼ %30s = %#s", "kTmpPath", kTmpPath);
|
||||
PRINT(" ☼ %30s = %#s", "kNtSystemDirectory", kNtSystemDirectory);
|
||||
PRINT(" ☼ %30s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory);
|
||||
PRINT(" ☼ %30s = %#s", "program_executable_name", GetProgramExecutableName());
|
||||
PRINT(" ☼ %30s = %#s", "GetInterpreterExecutableName()",
|
||||
GetInterpreterExecutableName(path, sizeof(path)));
|
||||
PRINT(" ☼ %30s = %p", "RSP", __builtin_frame_address(0));
|
||||
PRINT(" ☼ %30s = %p", "GetStackAddr()", GetStackAddr(0));
|
||||
PRINT(" ☼ %30s = %p", "GetStaticStackAddr(0)", GetStaticStackAddr(0));
|
||||
PRINT(" ☼ %30s = %p", "GetStackSize()", GetStackSize());
|
||||
|
||||
if (!IsWindows()) {
|
||||
PRINT("OPEN FILE DESCRIPTORS");
|
||||
for (i = 0; i < ARRAYLEN(pfds); ++i) {
|
||||
pfds[i].fd = i;
|
||||
pfds[i].events = 0;
|
||||
}
|
||||
if (sys_poll(pfds, ARRAYLEN(pfds), 0) != -1) {
|
||||
for (i = 0; i < ARRAYLEN(pfds); ++i) {
|
||||
if (~pfds[i].revents & POLLNVAL) {
|
||||
PRINT(" ☼ %d (F_GETFL=%#x)", i, fcntl(i, F_GETFL));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sigprocmask(SIG_BLOCK, 0, &ss) && (ss.__bits[0] || ss.__bits[1])) {
|
||||
PRINT("BLOCKED SIGNALS {%#lx, %#lx}", ss.__bits[0], ss.__bits[1]);
|
||||
for (i = 0; i < 32; ++i) {
|
||||
if (ss.__bits[0] & (1u << i)) {
|
||||
PRINT(" ☼ %s (%d)", strsignal(i + 1), i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__strace = st;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* @param inout_addrsize provides and receives addr's byte length
|
||||
* @return client fd which needs close(), or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
int accept(int fd, void *out_addr, uint32_t *inout_addrsize) {
|
||||
return accept4(fd, out_addr, inout_addrsize, 0);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
*
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
int connect(int fd, const void *addr, uint32_t addrsize) {
|
||||
int rc;
|
||||
|
|
|
@ -1345,7 +1345,7 @@ static textwindows dontinline int sys_epoll_create1_nt(uint32_t flags) {
|
|||
}
|
||||
|
||||
static textwindows dontinline int sys_epoll_ctl_nt(int epfd, int op, int fd,
|
||||
struct epoll_event *ev) {
|
||||
struct epoll_event *ev) {
|
||||
int r;
|
||||
struct PortState *port_state;
|
||||
struct TsTreeNode *tree_node;
|
||||
|
@ -1375,9 +1375,9 @@ static textwindows dontinline int sys_epoll_ctl_nt(int epfd, int op, int fd,
|
|||
}
|
||||
|
||||
static textwindows dontinline int sys_epoll_wait_nt(int epfd,
|
||||
struct epoll_event *events,
|
||||
int maxevents,
|
||||
int timeoutms) {
|
||||
struct epoll_event *events,
|
||||
int maxevents,
|
||||
int timeoutms) {
|
||||
int num_events;
|
||||
struct PortState *port_state;
|
||||
struct TsTreeNode *tree_node;
|
||||
|
@ -1493,6 +1493,7 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev) {
|
|||
* @param maxevents is array length of events
|
||||
* @param timeoutms is milliseconds, 0 to not block, or -1 for forever
|
||||
* @return number of events stored, 0 on timeout, or -1 w/ errno
|
||||
* @norestart
|
||||
*/
|
||||
int epoll_wait(int epfd, struct epoll_event *events, int maxevents,
|
||||
int timeoutms) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/struct/pollfd.h"
|
||||
|
@ -43,12 +44,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t ms) {
|
|||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (cmpxchg(&__interrupted, true, false) ||
|
||||
(weaken(_check_sigchld) && weaken(_check_sigchld)()) ||
|
||||
(weaken(_check_sigwinch) && weaken(_check_sigwinch)(g_fds.p + 0))) {
|
||||
return eintr();
|
||||
}
|
||||
waitfor = MIN(1000, ms); /* for ctrl+c */
|
||||
if (_check_interrupts(false, g_fds.p)) return eintr();
|
||||
waitfor = MIN(__SIG_POLLING_INTERVAL_MS, ms); /* for ctrl+c */
|
||||
if ((got = WSAPoll(ntfds, nfds, waitfor)) != -1) {
|
||||
if (!got && (ms -= waitfor) > 0) continue;
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
* @return fds[𝑖].revents flags can have:
|
||||
* (fds[𝑖].events & POLL{IN,OUT,PRI,HUP,ERR,NVAL})
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
int poll(struct pollfd *fds, uint64_t nfds, int32_t timeout_ms) {
|
||||
int rc;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t recv(int fd, void *buf, size_t size, int flags) {
|
||||
return recvfrom(fd, buf, size, flags, NULL, 0);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
|
@ -40,36 +41,41 @@
|
|||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags,
|
||||
void *opt_out_srcaddr, uint32_t *opt_inout_srcaddrsize) {
|
||||
ssize_t got;
|
||||
ssize_t rc, got;
|
||||
if (IsAsan() &&
|
||||
(!__asan_is_valid(buf, size) ||
|
||||
(opt_out_srcaddr &&
|
||||
!__asan_is_valid(opt_out_srcaddr, *opt_inout_srcaddrsize)))) {
|
||||
return efault();
|
||||
}
|
||||
if (!IsWindows()) {
|
||||
rc = efault();
|
||||
} else if (IsWindows() && _check_interrupts(false, g_fds.p)) {
|
||||
rc = eintr();
|
||||
} else if (!IsWindows()) {
|
||||
got = sys_recvfrom(fd, buf, size, flags, opt_out_srcaddr,
|
||||
opt_inout_srcaddrsize);
|
||||
if (opt_out_srcaddr && IsBsd() && got != -1) {
|
||||
sockaddr2linux(opt_out_srcaddr);
|
||||
}
|
||||
return got;
|
||||
rc = got;
|
||||
} else {
|
||||
if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
return sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1,
|
||||
flags, opt_out_srcaddr, opt_inout_srcaddrsize);
|
||||
rc = sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1,
|
||||
flags, opt_out_srcaddr, opt_inout_srcaddrsize);
|
||||
} else if (__isfdkind(fd, kFdFile) && !opt_out_srcaddr) { /* socketpair */
|
||||
if (flags) return einval();
|
||||
return sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1);
|
||||
if (flags) rc = einval();
|
||||
rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1);
|
||||
} else {
|
||||
return enotsock();
|
||||
rc = enotsock();
|
||||
}
|
||||
} else {
|
||||
return ebadf();
|
||||
rc = ebadf();
|
||||
}
|
||||
}
|
||||
STRACE("recvfrom(%d, %#.*hhs, %'zu, %#x) → %'ld% m", fd, size, buf, size,
|
||||
flags, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
|
||||
ssize_t got;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/popcnt.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -119,12 +120,13 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
|||
} else if (timeout) {
|
||||
req.tv_sec = timeout->tv_sec;
|
||||
req.tv_nsec = timeout->tv_usec * 1000;
|
||||
if ((rc = sys_nanosleep_nt(&req, &rem)) != -1) {
|
||||
timeout->tv_sec = rem.tv_sec;
|
||||
timeout->tv_usec = rem.tv_nsec / 1000;
|
||||
}
|
||||
rem.tv_sec = 0;
|
||||
rem.tv_nsec = 0;
|
||||
rc = sys_nanosleep_nt(&req, &rem);
|
||||
timeout->tv_sec = rem.tv_sec;
|
||||
timeout->tv_usec = rem.tv_nsec / 1000;
|
||||
} else {
|
||||
rc = einval();
|
||||
rc = pause();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t send(int fd, const void *buf, size_t size, int flags) {
|
||||
return sendto(fd, buf, size, flags, NULL, 0);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
|
||||
if (!IsWindows()) {
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @asyncsignalsafe
|
||||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags,
|
||||
const void *opt_addr, uint32_t addrsize) {
|
||||
|
|
69
libc/str/kstrsignal.S
Normal file
69
libc/str/kstrsignal.S
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 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/macros.internal.h"
|
||||
|
||||
.macro .e e s
|
||||
.long \e - kStrSignal
|
||||
.long 1f - kStrSignal
|
||||
.rodata.str1.1
|
||||
1: .string "\s"
|
||||
.previous
|
||||
.endm
|
||||
|
||||
.section .rodata
|
||||
.align 4
|
||||
.underrun
|
||||
kStrSignal:
|
||||
.e SIGHUP,"HUP"
|
||||
.e SIGINT,"INT"
|
||||
.e SIGQUIT,"QUIT"
|
||||
.e SIGILL,"ILL"
|
||||
.e SIGTRAP,"TRAP"
|
||||
.e SIGABRT,"ABRT"
|
||||
.e SIGBUS,"BUS"
|
||||
.e SIGFPE,"FPE"
|
||||
.e SIGKILL,"KILL"
|
||||
.e SIGUSR1,"USR1"
|
||||
.e SIGSEGV,"SEGV"
|
||||
.e SIGUSR2,"USR2"
|
||||
.e SIGPIPE,"PIPE"
|
||||
.e SIGALRM,"ALRM"
|
||||
.e SIGTERM,"TERM"
|
||||
.e SIGSTKFLT,"STKFLT"
|
||||
.e SIGCHLD,"CHLD"
|
||||
.e SIGCONT,"CONT"
|
||||
.e SIGSTOP,"STOP"
|
||||
.e SIGTSTP,"TSTP"
|
||||
.e SIGTTIN,"TTIN"
|
||||
.e SIGTTOU,"TTOU"
|
||||
.e SIGURG,"URG"
|
||||
.e SIGXCPU,"XCPU"
|
||||
.e SIGXFSZ,"XFSZ"
|
||||
.e SIGVTALRM,"VTALRM"
|
||||
.e SIGPROF,"PROF"
|
||||
.e SIGWINCH,"WINCH"
|
||||
.e SIGIO,"IO"
|
||||
.e SIGSYS,"SYS"
|
||||
.e SIGINFO,"INFO"
|
||||
.e SIGRTMAX,"RTMAX"
|
||||
.e SIGRTMIN,"RTMIN"
|
||||
.e SIGEMT,"EMT"
|
||||
.long 0
|
||||
.endobj kStrSignal,globl,hidden
|
||||
.overrun
|
|
@ -20,28 +20,42 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static const char kSig[4] = "SIG";
|
||||
static const char kUnknown[8] = "UNKNOWN";
|
||||
extern const struct { int x, s; } kStrSignal[];
|
||||
|
||||
_Alignas(char) static const char kStrSignals[][8] = {
|
||||
"EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS",
|
||||
"FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM",
|
||||
"STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG",
|
||||
"XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "IO", "PWR", "SYS",
|
||||
};
|
||||
|
||||
static char g_strsignal[4 + 8];
|
||||
static char g_strsignal[12];
|
||||
|
||||
/**
|
||||
* Returns name associated with signal code.
|
||||
* Returns string describing signal code.
|
||||
*
|
||||
* This returns SIGUNKNOWN for 0 which is the empty value. Textual names
|
||||
* should be available for signals 1 through 32. Signals in the range 33
|
||||
* and 128 are returned as a `SIG%03d` string. Everything else is SIGWUT
|
||||
*
|
||||
* @param sig is signal number which should be in range 1 through 128
|
||||
* @return pointer to static memory that mutates on subsequent calls
|
||||
* @see sigaction()
|
||||
*/
|
||||
char *strsignal(int sig) {
|
||||
if (0 <= sig && sig < ARRAYLEN(kStrSignals)) {
|
||||
memcpy(g_strsignal, kSig, 4);
|
||||
memcpy(&g_strsignal[3], kStrSignals[sig], 8);
|
||||
int i;
|
||||
strcpy(g_strsignal, "SIG");
|
||||
if (sig) {
|
||||
for (i = 0; kStrSignal[i].x; ++i) {
|
||||
if (sig == *(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x)) {
|
||||
strcpy(g_strsignal + 3,
|
||||
(const char *)((uintptr_t)kStrSignal + kStrSignal[i].s));
|
||||
return g_strsignal;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sig) {
|
||||
strcpy(g_strsignal + 3, "UNKNOWN");
|
||||
} else if (1 <= sig && sig <= 128) {
|
||||
g_strsignal[3] = '0' + sig / 100;
|
||||
g_strsignal[4] = '0' + sig / 10 % 10;
|
||||
g_strsignal[5] = '0' + sig % 10;
|
||||
g_strsignal[6] = 0;
|
||||
} else {
|
||||
memcpy(g_strsignal, &kUnknown, 8);
|
||||
strcpy(g_strsignal + 3, "WUT");
|
||||
}
|
||||
return g_strsignal;
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_sigprocmask,0x125030154203000e,globl,hidden
|
||||
.scall sys_sigprocmask,0x125030154214900e,globl,hidden
|
||||
|
|
|
@ -2015,7 +2015,7 @@ syscon nr __NR_msync 0x001a 0x2000041 0x0041 0x0100 0x115 0xfff
|
|||
syscon nr __NR_mprotect 0x000a 0x200004a 0x004a 0x004a 0x04a 0xfff
|
||||
syscon nr __NR_munmap 0x000b 0x2000049 0x0049 0x0049 0x049 0xfff
|
||||
syscon nr __NR_sigaction 0x000d 0x200002e 0x01a0 0x002e 0x154 0xfff
|
||||
syscon nr __NR_sigprocmask 0x000e 0x2000030 0x0154 0x0030 0x125 0xfff
|
||||
syscon nr __NR_sigprocmask 0x000e 0x2000149 0x0154 0x0030 0x125 0xfff
|
||||
syscon nr __NR_ioctl 0x0010 0x2000036 0x0036 0x0036 0x036 0xfff
|
||||
syscon nr __NR_pread 0x0011 0x2000099 0x01db 0x00ad 0x0ad 0xfff
|
||||
syscon nr __NR_pwrite 0x0012 0x200009a 0x01dc 0x00ae 0x0ae 0xfff
|
||||
|
|
|
@ -53,43 +53,44 @@ COSMOPOLITAN_C_END_
|
|||
|
||||
#define SIGABRT LITERALLY(6)
|
||||
#define SIGALRM LITERALLY(14)
|
||||
#define SIGBUS SYMBOLIC(SIGBUS)
|
||||
#define SIGCHLD SYMBOLIC(SIGCHLD)
|
||||
#define SIGCONT SYMBOLIC(SIGCONT)
|
||||
#define SIGEMT SYMBOLIC(SIGEMT)
|
||||
#define SIGFPE LITERALLY(8)
|
||||
#define SIGHUP LITERALLY(1)
|
||||
#define SIGILL LITERALLY(4)
|
||||
#define SIGINFO SYMBOLIC(SIGINFO)
|
||||
#define SIGINT LITERALLY(2)
|
||||
#define SIGIO SYMBOLIC(SIGIO)
|
||||
#define SIGIOT LITERALLY(6)
|
||||
#define SIGKILL LITERALLY(9)
|
||||
#define SIGPIPE LITERALLY(13)
|
||||
#define SIGPOLL SYMBOLIC(SIGPOLL)
|
||||
#define SIGPROF LITERALLY(27)
|
||||
#define SIGPWR SYMBOLIC(SIGPWR)
|
||||
#define SIGQUIT LITERALLY(3)
|
||||
#define SIGRTMAX SYMBOLIC(SIGRTMAX)
|
||||
#define SIGRTMIN SYMBOLIC(SIGRTMIN)
|
||||
#define SIGSEGV LITERALLY(11)
|
||||
#define SIGSTKFLT SYMBOLIC(SIGSTKFLT)
|
||||
#define SIGSTOP SYMBOLIC(SIGSTOP)
|
||||
#define SIGSYS SYMBOLIC(SIGSYS)
|
||||
#define SIGTERM LITERALLY(15)
|
||||
#define SIGTRAP LITERALLY(5)
|
||||
#define SIGTSTP SYMBOLIC(SIGTSTP)
|
||||
#define SIGTTIN LITERALLY(21)
|
||||
#define SIGTTOU LITERALLY(22)
|
||||
#define SIGUNUSED SYMBOLIC(SIGUNUSED)
|
||||
#define SIGURG SYMBOLIC(SIGURG)
|
||||
#define SIGUSR1 SYMBOLIC(SIGUSR1)
|
||||
#define SIGUSR2 SYMBOLIC(SIGUSR2)
|
||||
#define SIGVTALRM LITERALLY(26)
|
||||
#define SIGWINCH LITERALLY(28)
|
||||
#define SIGXCPU LITERALLY(24)
|
||||
#define SIGXFSZ LITERALLY(25)
|
||||
|
||||
#define SIGBUS SYMBOLIC(SIGBUS)
|
||||
#define SIGCHLD SYMBOLIC(SIGCHLD)
|
||||
#define SIGCONT SYMBOLIC(SIGCONT)
|
||||
#define SIGEMT SYMBOLIC(SIGEMT)
|
||||
#define SIGINFO SYMBOLIC(SIGINFO)
|
||||
#define SIGIO SYMBOLIC(SIGIO)
|
||||
#define SIGPOLL SYMBOLIC(SIGPOLL)
|
||||
#define SIGPWR SYMBOLIC(SIGPWR)
|
||||
#define SIGRTMAX SYMBOLIC(SIGRTMAX)
|
||||
#define SIGRTMIN SYMBOLIC(SIGRTMIN)
|
||||
#define SIGSTKFLT SYMBOLIC(SIGSTKFLT)
|
||||
#define SIGSTOP SYMBOLIC(SIGSTOP)
|
||||
#define SIGSYS SYMBOLIC(SIGSYS)
|
||||
#define SIGTSTP SYMBOLIC(SIGTSTP)
|
||||
#define SIGUNUSED SYMBOLIC(SIGUNUSED)
|
||||
#define SIGURG SYMBOLIC(SIGURG)
|
||||
#define SIGUSR1 SYMBOLIC(SIGUSR1)
|
||||
#define SIGUSR2 SYMBOLIC(SIGUSR2)
|
||||
|
||||
#define SIG_ATOMIC_MIN SYMBOLIC(SIG_ATOMIC_MIN)
|
||||
#define SIG_BLOCK SYMBOLIC(SIG_BLOCK)
|
||||
#define SIG_SETMASK SYMBOLIC(SIG_SETMASK)
|
||||
|
|
|
@ -49,7 +49,7 @@ scall sys_msync 0x115100041204101a globl hidden
|
|||
scall sys_mprotect 0x04a04a04a204a00a globl hidden
|
||||
scall __sys_munmap 0x049049049204900b globl hidden
|
||||
scall sys_sigaction 0x15402e1a0202e00d globl hidden # rt_sigaction on Lunix; it's complicated on NetBSD
|
||||
scall sys_sigprocmask 0x125030154203000e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue
|
||||
scall sys_sigprocmask 0x125030154214900e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue, a.k.a. pthread_sigmask
|
||||
scall sys_ioctl 0x0360360362036010 globl hidden
|
||||
scall sys_pread 0x0ad0ad1db2099011 globl hidden # a.k.a. pread64; netbsd+openbsd:pad
|
||||
scall sys_pwrite 0x0ae0ae1dc209a012 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad
|
||||
|
@ -162,8 +162,8 @@ scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu
|
|||
scall sys_setresgid 0xfff11c138ffff077 globl hidden # polyfilled for xnu
|
||||
scall getresuid 0xfff119168ffff076 globl # semantics aren't well-defined
|
||||
scall getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined
|
||||
scall sigpending 0x124034034203407f globl # rt_sigpending on linux
|
||||
scall sys_sigsuspend 0x12606f155206f082 globl hidden # openbsd:byvalue
|
||||
scall sigpending 0x124034034203407f globl # a.k.a. rt_sigpending on linux
|
||||
scall sys_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU
|
||||
scall sys_sigaltstack 0x1191200352035083 globl hidden
|
||||
scall sys_mknod 0x1c200e00e200e085 globl hidden
|
||||
scall mknodat 0x1cc14022fffff103 globl # FreeBSD 12+
|
||||
|
@ -193,7 +193,7 @@ scall capget 0xfffffffffffff07d globl
|
|||
scall capset 0xfffffffffffff07e globl
|
||||
scall sigtimedwait 0xffffff159ffff080 globl
|
||||
scall sys_sigqueue 0xffffff1c8fffffff globl
|
||||
scall sys_sigqueueinfo 0x0f5ffffffffff081 globl # rt_sigqueueinfo on linux
|
||||
scall sys_sigqueueinfo 0x0f5ffffffffff081 globl # a.k.a. rt_sigqueueinfo on linux
|
||||
scall personality 0xfffffffffffff087 globl
|
||||
scall ustat 0xfffffffffffff088 globl
|
||||
scall sysfs 0xfffffffffffff08b globl
|
||||
|
@ -246,7 +246,7 @@ scall lookup_dcookie 0xfffffffffffff0d4 globl
|
|||
scall sys_epoll_create 0xfffffffffffff0d5 globl
|
||||
scall sys_epoll_wait 0xfffffffffffff0e8 globl
|
||||
scall sys_epoll_ctl 0xfffffffffffff0e9 globl
|
||||
scall getdents 0x18606311020c40d9 globl hidden # four args b/c xnu, getdirentries on xnu, 32-bit on xnu/freebsd, getdents64 on linux, 64-bit on openbsd
|
||||
scall getdents 0x18606311020c40d9 globl hidden # four args b/c xnu, getdirentries on xnu, 32-bit on xnu/freebsd, a.k.a. getdents64 on linux, 64-bit on openbsd
|
||||
scall set_tid_address 0xfffffffffffff0da globl
|
||||
scall restart_syscall 0xfffffffffffff0db globl
|
||||
scall semtimedop 0xfffffffffffff0dc globl
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
// todo(jart): delete
|
||||
|
||||
/**
|
||||
* Sleeps w/ higher precision.
|
||||
*/
|
||||
|
|
57
test/libc/calls/setitimer_test.c
Normal file
57
test/libc/calls/setitimer_test.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*-*- 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 2022 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/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
bool gotsig;
|
||||
|
||||
void OnSigAlrm(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||
EXPECT_EQ(SIGALRM, sig);
|
||||
EXPECT_EQ(SIGALRM, si->si_signo);
|
||||
gotsig = true;
|
||||
}
|
||||
|
||||
TEST(setitimer, testSingleShot) {
|
||||
sigset_t block, oldmask;
|
||||
struct sigaction oldalrm;
|
||||
struct itimerval it = {{0, 0}, {0, 10000}};
|
||||
struct sigaction sa = {.sa_sigaction = OnSigAlrm,
|
||||
.sa_flags = SA_RESETHAND | SA_SIGINFO};
|
||||
gotsig = false;
|
||||
sigemptyset(&block);
|
||||
sigaddset(&block, SIGALRM);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &block, &oldmask));
|
||||
ASSERT_EQ(0, sigaction(SIGALRM, &sa, &oldalrm));
|
||||
ASSERT_EQ(0, setitimer(ITIMER_REAL, &it, 0));
|
||||
sigdelset(&block, SIGALRM);
|
||||
EXPECT_EQ(-1, sigsuspend(&block));
|
||||
EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &oldmask, 0));
|
||||
EXPECT_EQ(0, sigaction(SIGUSR1, &oldalrm, 0));
|
||||
EXPECT_EQ(true, gotsig);
|
||||
}
|
77
test/libc/calls/sigprocmask_test.c
Normal file
77
test/libc/calls/sigprocmask_test.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*-*- 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 2022 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/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
int n;
|
||||
|
||||
void OnSig(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||
++n;
|
||||
}
|
||||
|
||||
TEST(sigprocmask, testMultipleBlockedDeliveries) {
|
||||
int pid, ws;
|
||||
sigset_t neu, old;
|
||||
struct sigaction oldusr1, oldusr2;
|
||||
struct sigaction sa = {.sa_sigaction = OnSig, .sa_flags = SA_SIGINFO};
|
||||
n = 0;
|
||||
sigemptyset(&neu);
|
||||
sigaddset(&neu, SIGUSR1);
|
||||
sigaddset(&neu, SIGUSR2);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &neu, &old));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &sa, &oldusr1));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR2, &sa, &oldusr2));
|
||||
ASSERT_EQ(0, raise(SIGUSR1));
|
||||
ASSERT_EQ(0, raise(SIGUSR2));
|
||||
EXPECT_EQ(0, n);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &old, NULL));
|
||||
EXPECT_EQ(0, sigaction(SIGUSR1, &oldusr1, 0));
|
||||
EXPECT_EQ(0, sigaction(SIGUSR2, &oldusr2, 0));
|
||||
ASSERT_EQ(2, n);
|
||||
}
|
||||
|
||||
TEST(sigprocmask, testMultipleBlockedDeliveriesOfSameSignal) {
|
||||
int pid, ws;
|
||||
sigset_t neu, old;
|
||||
struct sigaction oldusr2;
|
||||
struct sigaction sa = {.sa_sigaction = OnSig, .sa_flags = SA_SIGINFO};
|
||||
n = 0;
|
||||
sigemptyset(&neu);
|
||||
sigaddset(&neu, SIGUSR2);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &neu, &old));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR2, &sa, &oldusr2));
|
||||
ASSERT_EQ(0, raise(SIGUSR2));
|
||||
ASSERT_EQ(0, raise(SIGUSR2));
|
||||
EXPECT_EQ(0, n);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &old, NULL));
|
||||
EXPECT_EQ(0, sigaction(SIGUSR2, &oldusr2, 0));
|
||||
if (IsFreebsd() || IsWindows()) {
|
||||
EXPECT_EQ(2, n);
|
||||
} else {
|
||||
EXPECT_EQ(1, n);
|
||||
}
|
||||
}
|
120
test/libc/calls/sigsuspend_test.c
Normal file
120
test/libc/calls/sigsuspend_test.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*-*- 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 2022 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/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
volatile bool gotsig1;
|
||||
volatile bool gotsig2;
|
||||
volatile bool finished;
|
||||
|
||||
void OnSigQueuing(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||
if (!finished) {
|
||||
EXPECT_EQ(SIGUSR2, sig);
|
||||
EXPECT_EQ(SIGUSR2, si->si_signo);
|
||||
gotsig2 = true;
|
||||
} else {
|
||||
EXPECT_EQ(SIGUSR1, sig);
|
||||
EXPECT_EQ(SIGUSR1, si->si_signo);
|
||||
gotsig1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(sigsuspend, testSignalQueuingSelf) {
|
||||
if (IsWindows()) {
|
||||
// xxx: probably need a signal server to do this kind of signalling
|
||||
return;
|
||||
}
|
||||
sigset_t neu, old, bits;
|
||||
struct sigaction oldusr1, oldusr2;
|
||||
struct sigaction sa = {.sa_sigaction = OnSigQueuing, .sa_flags = SA_SIGINFO};
|
||||
gotsig1 = false;
|
||||
gotsig2 = false;
|
||||
finished = false;
|
||||
sigemptyset(&neu);
|
||||
sigaddset(&neu, SIGUSR1);
|
||||
sigaddset(&neu, SIGUSR2);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &neu, &old));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &sa, &oldusr1));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR2, &sa, &oldusr2));
|
||||
raise(SIGUSR1);
|
||||
raise(SIGUSR2);
|
||||
sigdelset(&neu, SIGUSR2);
|
||||
EXPECT_EQ(false, gotsig2);
|
||||
memcpy(&bits, &neu, sizeof(bits));
|
||||
ASSERT_EQ(-1, sigsuspend(&neu)); // raises SIGUSR2
|
||||
EXPECT_EQ(0, memcmp(&bits, &neu, sizeof(bits))); // constness respected
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
EXPECT_EQ(true, gotsig2);
|
||||
EXPECT_EQ(false, gotsig1);
|
||||
finished = true;
|
||||
EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &old, NULL)); // raises SIGUSR1
|
||||
EXPECT_EQ(true, gotsig1);
|
||||
EXPECT_EQ(0, sigaction(SIGUSR1, &oldusr1, 0));
|
||||
EXPECT_EQ(0, sigaction(SIGUSR2, &oldusr2, 0));
|
||||
}
|
||||
|
||||
TEST(sigsuspend, testSignalQueuingIpc) {
|
||||
if (IsWindows()) {
|
||||
// xxx: probably need a signal server to do this kind of signalling
|
||||
return;
|
||||
}
|
||||
int pid, ws;
|
||||
sigset_t neu, old, bits;
|
||||
struct sigaction oldusr1, oldusr2;
|
||||
struct sigaction sa = {.sa_sigaction = OnSigQueuing, .sa_flags = SA_SIGINFO};
|
||||
gotsig1 = false;
|
||||
gotsig2 = false;
|
||||
finished = false;
|
||||
sigemptyset(&neu);
|
||||
sigaddset(&neu, SIGUSR1);
|
||||
sigaddset(&neu, SIGUSR2);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &neu, &old));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &sa, &oldusr1));
|
||||
ASSERT_EQ(0, sigaction(SIGUSR2, &sa, &oldusr2));
|
||||
pid = getpid();
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
if (ws == -2) {
|
||||
kill(pid, SIGUSR1);
|
||||
kill(pid, SIGUSR2);
|
||||
_exit(0);
|
||||
}
|
||||
sigdelset(&neu, SIGUSR2);
|
||||
EXPECT_EQ(false, gotsig2);
|
||||
memcpy(&bits, &neu, sizeof(bits));
|
||||
ASSERT_EQ(-1, sigsuspend(&neu)); // raises SIGUSR2
|
||||
EXPECT_EQ(0, memcmp(&bits, &neu, sizeof(bits))); // constness respected
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
EXPECT_EQ(true, gotsig2);
|
||||
EXPECT_EQ(false, gotsig1);
|
||||
finished = true;
|
||||
EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &old, NULL)); // raises SIGUSR1
|
||||
EXPECT_EQ(true, gotsig1);
|
||||
EXPECT_EQ(0, sigaction(SIGUSR1, &oldusr1, 0));
|
||||
EXPECT_EQ(0, sigaction(SIGUSR2, &oldusr2, 0));
|
||||
}
|
|
@ -23,8 +23,8 @@
|
|||
#include "libc/time/time.h"
|
||||
|
||||
TEST(select, allZero) {
|
||||
/* blocks indefinitely not worth supporting */
|
||||
/* EXPECT_SYS(0, 0, select(0, 0, 0, 0, 0)); */
|
||||
// todo: figure out how to test block until signal w/ select
|
||||
// EXPECT_SYS(0, 0, select(0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(select, testSleep) {
|
||||
|
@ -35,8 +35,7 @@ TEST(select, testSleep) {
|
|||
EXPECT_SYS(0, 0, select(0, 0, 0, 0, &t));
|
||||
e = (nowl() - n) * 1e6;
|
||||
EXPECT_GT(e, 1000);
|
||||
if (!IsBsd()) {
|
||||
/* maybe we should polyfill */
|
||||
if (IsLinux()) {
|
||||
EXPECT_EQ(0, t.tv_sec);
|
||||
EXPECT_EQ(0, t.tv_usec);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- 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 2020 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2022 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 │
|
||||
|
@ -16,20 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
void __winalarm(void *lpArgToCompletionRoutine, uint32_t dwTimerLowValue,
|
||||
uint32_t dwTimerHighValue) {
|
||||
int rva;
|
||||
siginfo_t info;
|
||||
bzero(&info, sizeof(info));
|
||||
info.si_signo = SIGALRM;
|
||||
rva = __sighandrvas[SIGALRM];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
((sigaction_f)(_base + rva))(SIGALRM, &info, NULL);
|
||||
}
|
||||
TEST(strsignal, test) {
|
||||
EXPECT_STREQ("SIGUNKNOWN", strsignal(0));
|
||||
EXPECT_STREQ("SIGINT", strsignal(SIGINT));
|
||||
EXPECT_STREQ("SIGQUIT", strsignal(SIGQUIT));
|
||||
EXPECT_STREQ("SIGALRM", strsignal(SIGALRM));
|
||||
EXPECT_STREQ("SIGUSR1", strsignal(SIGUSR1));
|
||||
EXPECT_STREQ("SIGSTOP", strsignal(SIGSTOP));
|
||||
EXPECT_STREQ("SIG099", strsignal(99));
|
||||
EXPECT_STREQ("SIG100", strsignal(100));
|
||||
EXPECT_STREQ("SIGWUT", strsignal(-1));
|
||||
EXPECT_STREQ("SIGWUT", strsignal(9001));
|
||||
}
|
|
@ -61,6 +61,7 @@
|
|||
#define INTPTR 7
|
||||
#define STAT 8
|
||||
#define SIG 9
|
||||
#define SIGSET 10
|
||||
#define OCTAL 0x80
|
||||
|
||||
static const struct Syscall {
|
||||
|
@ -87,15 +88,19 @@ static const struct Syscall {
|
|||
{&__NR_msync, "msync", 3, INT, {PTR, ULONG, INT}},
|
||||
{&__NR_mprotect, "mprotect", 3, INT, {PTR, ULONG, INT}},
|
||||
{&__NR_munmap, "munmap", 2, INT, {PTR, ULONG}},
|
||||
{&__NR_sigaction, "sigaction", 4, INT, {SIG}},
|
||||
{&__NR_sigprocmask, "sigprocmask", 3, INT},
|
||||
{&__NR_sigaction, "rt_sigaction", 4, INT, {SIG}},
|
||||
{&__NR_sigprocmask, "rt_sigprocmask", 4, INT, {INT, SIGSET, SIGSET, LONG}},
|
||||
{&__NR_sigpending, "rt_sigpending", 2, INT, {SIGSET, LONG}},
|
||||
{&__NR_sigsuspend, "rt_sigsuspend", 2, INT, {SIGSET, LONG}},
|
||||
{&__NR_rt_sigqueueinfo, "rt_sigqueueinfo", 6},
|
||||
{&__NR_ioctl, "ioctl", 3, INT, {INT, ULONG, ULONG}},
|
||||
{&__NR_pread, "pread", 4, LONG, {INT, BUF, ULONG, ULONG}},
|
||||
{&__NR_pwrite, "pwrite", 4, LONG, {INT, BUF, ULONG, ULONG}},
|
||||
{&__NR_pread, "pread64", 4, LONG, {INT, BUF, ULONG, ULONG}},
|
||||
{&__NR_pwrite, "pwrite64", 4, LONG, {INT, BUF, ULONG, ULONG}},
|
||||
{&__NR_readv, "readv", 3, LONG, {INT, IOV, INT}},
|
||||
{&__NR_writev, "writev", 3, LONG, {INT, IOV, INT}},
|
||||
{&__NR_access, "access", 2, INT, {STR, OCTAL|INT}},
|
||||
{&__NR_pipe, "pipe", 1, INT},
|
||||
{&__NR_pipe2, "pipe2", 2, INT},
|
||||
{&__NR_select, "select", 5},
|
||||
{&__NR_pselect, "pselect", 6},
|
||||
{&__NR_pselect6, "pselect6", 6},
|
||||
|
@ -201,8 +206,6 @@ static const struct Syscall {
|
|||
{&__NR_setresgid, "setresgid", 6},
|
||||
{&__NR_getresuid, "getresuid", 6},
|
||||
{&__NR_getresgid, "getresgid", 6},
|
||||
{&__NR_sigpending, "sigpending", 6},
|
||||
{&__NR_sigsuspend, "sigsuspend", 6},
|
||||
{&__NR_sigaltstack, "sigaltstack", 6},
|
||||
{&__NR_mknod, "mknod", 6},
|
||||
{&__NR_mknodat, "mknodat", 6},
|
||||
|
@ -229,7 +232,6 @@ static const struct Syscall {
|
|||
{&__NR_capget, "capget", 6},
|
||||
{&__NR_capset, "capset", 6},
|
||||
{&__NR_sigtimedwait, "sigtimedwait", 6},
|
||||
{&__NR_rt_sigqueueinfo, "rt_sigqueueinfo", 6},
|
||||
{&__NR_personality, "personality", 6},
|
||||
{&__NR_ustat, "ustat", 6},
|
||||
{&__NR_sysfs, "sysfs", 6},
|
||||
|
@ -283,7 +285,7 @@ static const struct Syscall {
|
|||
{&__NR_epoll_create, "epoll_create", 6},
|
||||
{&__NR_epoll_wait, "epoll_wait", 6},
|
||||
{&__NR_epoll_ctl, "epoll_ctl", 6},
|
||||
{&__NR_getdents, "getdents", 6},
|
||||
{&__NR_getdents, "getdents64", 6},
|
||||
{&__NR_set_tid_address, "set_tid_address", 1},
|
||||
{&__NR_restart_syscall, "restart_syscall", 6},
|
||||
{&__NR_semtimedop, "semtimedop", 6},
|
||||
|
@ -325,7 +327,7 @@ static const struct Syscall {
|
|||
{&__NR_futimesat, "futimesat", 6},
|
||||
{&__NR_futimes, "futimes", 6},
|
||||
{&__NR_futimens, "futimens", 6},
|
||||
{&__NR_fstatat, "fstatat", 4, INT, {INT, STR, STAT, INT}},
|
||||
{&__NR_fstatat, "newfstatat", 4, INT, {INT, STR, STAT, INT}},
|
||||
{&__NR_unlinkat, "unlinkat", 3, INT, {INT, STR, INT}},
|
||||
{&__NR_renameat, "renameat", 4, INT, {INT, STR, INT, STR}},
|
||||
{&__NR_linkat, "linkat", 6},
|
||||
|
@ -347,7 +349,6 @@ static const struct Syscall {
|
|||
{&__NR_posix_fallocate, "posix_fallocate", 6},
|
||||
{&__NR_accept4, "accept4", 4},
|
||||
{&__NR_dup3, "dup3", 3, INT},
|
||||
{&__NR_pipe2, "pipe2", 2},
|
||||
{&__NR_epoll_pwait, "epoll_pwait", 6},
|
||||
{&__NR_epoll_create1, "epoll_create1", 6},
|
||||
{&__NR_perf_event_open, "perf_event_open", 6},
|
||||
|
@ -613,7 +614,7 @@ static char *PrintString(char *s) {
|
|||
return s;
|
||||
}
|
||||
|
||||
static char *PeekData(unsigned long x, size_t size) {
|
||||
static void *PeekData(unsigned long x, size_t size) {
|
||||
union {
|
||||
char buf[8];
|
||||
long word;
|
||||
|
@ -760,6 +761,10 @@ static struct stat *PrintStat(struct stat *st) {
|
|||
return st;
|
||||
}
|
||||
|
||||
static void PrintSigset(unsigned long p) {
|
||||
kappendf(&ob, "{%#lx}", ptrace(PTRACE_PEEKTEXT, sp->pid, p));
|
||||
}
|
||||
|
||||
static void PrintSyscallArg(int type, unsigned long x, unsigned long y) {
|
||||
char *s;
|
||||
switch (type & 31) {
|
||||
|
@ -791,6 +796,9 @@ static void PrintSyscallArg(int type, unsigned long x, unsigned long y) {
|
|||
case SIG:
|
||||
appends(&ob, strsignal(x));
|
||||
break;
|
||||
case SIGSET:
|
||||
PrintSigset(x);
|
||||
break;
|
||||
case STRLIST:
|
||||
FreeStringList(PrintStringList(PeekStringList(x)));
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue