mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-01 03:53:33 +00:00
221 lines
6.7 KiB
C
221 lines
6.7 KiB
C
|
/*-*- 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;
|
||
|
}
|