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:
Justine Tunney 2022-03-25 07:11:44 -07:00
parent 3b9e66ecba
commit 072e1d2910
82 changed files with 1388 additions and 450 deletions

View file

@ -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
View 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));
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -19,3 +19,4 @@
#include "libc/calls/internal.h"
unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG];

View file

@ -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:

View file

@ -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;
/*───────────────────────────────────────────────────────────────────────────│─╗

View file

@ -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);
}

View file

@ -25,6 +25,7 @@
/**
* Controls settings on device.
* @restartable
* @vforksafe
*/
int(ioctl)(int fd, uint64_t request, ...) {

View file

@ -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)) {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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, ...) {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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;
}

View file

@ -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
View 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
View 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_ */

View file

@ -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

View file

@ -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);
}

View file

@ -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),

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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) */

View 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;
}

View file

@ -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) \

View file

@ -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;

View file

@ -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();

View file

@ -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);
}

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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));

View file

@ -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;

View file

@ -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;

View file

@ -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);
}
}

View 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;
}

View file

@ -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) {

View file

@ -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) &&

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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();

View file

@ -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);

View file

@ -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
}

View file

@ -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);

View file

@ -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;

View file

@ -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) {

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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()) {

View file

@ -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
View 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

View file

@ -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;
}

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_sigprocmask,0x125030154203000e,globl,hidden
.scall sys_sigprocmask,0x125030154214900e,globl,hidden

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -19,6 +19,8 @@
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/time/time.h"
// todo(jart): delete
/**
* Sleeps w/ higher precision.
*/

View 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);
}

View 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);
}
}

View 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));
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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;