mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-04-23 15:24:43 +00:00
Implement signal handler tail recursion
GNU Make on Windows now appears to be working reliably. This change also fixes a bug where, after fork the Windows thread handle wasn't reset and that caused undefined behavior using SetThreadContext() with our signals
This commit is contained in:
parent
a657f3e878
commit
cdf556e7d2
15 changed files with 627 additions and 77 deletions
167
libc/calls/sig.c
167
libc/calls/sig.c
|
@ -97,6 +97,32 @@ textwindows void __sig_delete(int sig) {
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static textwindows int __sig_getter(struct CosmoTib *tib, atomic_ulong *sigs) {
|
||||||
|
int sig;
|
||||||
|
sigset_t bit, pending, masked, deliverable;
|
||||||
|
for (;;) {
|
||||||
|
pending = atomic_load_explicit(sigs, memory_order_acquire);
|
||||||
|
masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire);
|
||||||
|
if ((deliverable = pending & ~masked)) {
|
||||||
|
sig = _bsf(deliverable) + 1;
|
||||||
|
bit = 1ull << (sig - 1);
|
||||||
|
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit) {
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static textwindows int __sig_get(struct CosmoTib *tib) {
|
||||||
|
int sig;
|
||||||
|
if (!(sig = __sig_getter(tib, &tib->tib_sigpending))) {
|
||||||
|
sig = __sig_getter(tib, &__sig.pending);
|
||||||
|
}
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||||
struct CosmoTib *tib) {
|
struct CosmoTib *tib) {
|
||||||
if (!(flags & SA_ONSTACK)) {
|
if (!(flags & SA_ONSTACK)) {
|
||||||
|
@ -146,34 +172,49 @@ static textwindows sigaction_f __sig_handler(unsigned rva) {
|
||||||
}
|
}
|
||||||
|
|
||||||
textwindows int __sig_raise(int sig, int sic) {
|
textwindows int __sig_raise(int sig, int sic) {
|
||||||
unsigned rva, flags;
|
|
||||||
|
// create machine context object
|
||||||
struct PosixThread *pt = _pthread_self();
|
struct PosixThread *pt = _pthread_self();
|
||||||
ucontext_t ctx = {.uc_sigmask = pt->tib->tib_sigmask};
|
ucontext_t ctx = {.uc_sigmask = pt->tib->tib_sigmask};
|
||||||
if (!__sig_start(pt, sig, &rva, &flags)) return 0;
|
|
||||||
if (flags & SA_RESETHAND) {
|
|
||||||
STRACE("resetting %G handler", sig);
|
|
||||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
|
||||||
}
|
|
||||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
|
||||||
struct NtContext nc;
|
struct NtContext nc;
|
||||||
nc.ContextFlags = kNtContextFull;
|
nc.ContextFlags = kNtContextFull;
|
||||||
GetThreadContext(GetCurrentThread(), &nc);
|
GetThreadContext(GetCurrentThread(), &nc);
|
||||||
_ntcontext2linux(&ctx, &nc);
|
_ntcontext2linux(&ctx, &nc);
|
||||||
pt->tib->tib_sigmask |= __sighandmask[sig];
|
|
||||||
if (!(flags & SA_NODEFER)) {
|
// process signal(s)
|
||||||
pt->tib->tib_sigmask |= 1ull << (sig - 1);
|
int handler_was_called = 0;
|
||||||
}
|
do {
|
||||||
NTTRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
// start the signal
|
||||||
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
unsigned rva, flags;
|
||||||
DescribeSigset(0, (sigset_t *)&pt->tib->tib_sigmask));
|
if (__sig_start(pt, sig, &rva, &flags)) {
|
||||||
__sig_handler(rva)(sig, &si, &ctx);
|
if (flags & SA_RESETHAND) {
|
||||||
NTTRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
STRACE("resetting %G handler", sig);
|
||||||
__sig_handler(rva),
|
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||||
DescribeSigset(0, (sigset_t *)&pt->tib->tib_sigmask),
|
}
|
||||||
DescribeSigset(0, &ctx.uc_sigmask));
|
|
||||||
atomic_store_explicit(&pt->tib->tib_sigmask, ctx.uc_sigmask,
|
// update the signal mask in preparation for signal handller
|
||||||
memory_order_release);
|
sigset_t blocksigs = __sighandmask[sig];
|
||||||
return (flags & SA_RESTART) ? 2 : 1;
|
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||||
|
ctx.uc_sigmask = atomic_fetch_or_explicit(
|
||||||
|
&pt->tib->tib_sigmask, blocksigs, memory_order_acq_rel);
|
||||||
|
|
||||||
|
// call the user's signal handler
|
||||||
|
char ssbuf[2][128];
|
||||||
|
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||||
|
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
|
||||||
|
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
|
||||||
|
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||||
|
__sig_handler(rva)(sig, &si, &ctx);
|
||||||
|
(void)ssbuf;
|
||||||
|
|
||||||
|
// restore the original signal mask
|
||||||
|
atomic_store_explicit(&pt->tib->tib_sigmask, ctx.uc_sigmask,
|
||||||
|
memory_order_release);
|
||||||
|
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
|
||||||
|
}
|
||||||
|
sic = SI_KERNEL;
|
||||||
|
} while ((sig = __sig_get(pt->tib)));
|
||||||
|
return handler_was_called;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancels blocking operations being performed by signaled thread
|
// cancels blocking operations being performed by signaled thread
|
||||||
|
@ -220,17 +261,48 @@ textwindows void __sig_cancel(struct PosixThread *pt, int sig, unsigned flags) {
|
||||||
WakeByAddressSingle(blocker);
|
WakeByAddressSingle(blocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the user's signal handler callback is composed with this trampoline
|
||||||
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||||
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
|
|
||||||
int sig = sf->si.si_signo;
|
int sig = sf->si.si_signo;
|
||||||
sigset_t blocksigs = __sighandmask[sig];
|
struct CosmoTib *tib = __get_tls();
|
||||||
if (!(sf->flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
|
||||||
sf->ctx.uc_sigmask = atomic_fetch_or_explicit(
|
for (;;) {
|
||||||
&__get_tls()->tib_sigmask, blocksigs, memory_order_acq_rel);
|
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
|
||||||
__sig_handler(sf->rva)(sig, &sf->si, &sf->ctx);
|
|
||||||
atomic_store_explicit(&__get_tls()->tib_sigmask, sf->ctx.uc_sigmask,
|
// update the signal mask in preparation for signal handller
|
||||||
memory_order_release);
|
sigset_t blocksigs = __sighandmask[sig];
|
||||||
__sig_restore(&sf->ctx);
|
if (!(sf->flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||||
|
sf->ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
|
||||||
|
memory_order_acq_rel);
|
||||||
|
|
||||||
|
// call the user's signal handler
|
||||||
|
char ssbuf[2][128];
|
||||||
|
STRACE("__sig_tramp(%G, %t) mask %s → %s", sig, __sig_handler(sf->rva),
|
||||||
|
(DescribeSigset)(ssbuf[0], 0, &sf->ctx.uc_sigmask),
|
||||||
|
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&tib->tib_sigmask));
|
||||||
|
__sig_handler(sf->rva)(sig, &sf->si, &sf->ctx);
|
||||||
|
(void)ssbuf;
|
||||||
|
|
||||||
|
// restore the signal mask that was used by the interrupted code
|
||||||
|
// this may have been modified by the signal handler in the callback
|
||||||
|
atomic_store_explicit(&tib->tib_sigmask, sf->ctx.uc_sigmask,
|
||||||
|
memory_order_release);
|
||||||
|
|
||||||
|
// jump back into original code if there aren't any pending signals
|
||||||
|
do {
|
||||||
|
if (!(sig = __sig_get(tib))) {
|
||||||
|
__sig_restore(&sf->ctx);
|
||||||
|
}
|
||||||
|
} while (!__sig_start(pt, sig, &sf->rva, &sf->flags));
|
||||||
|
|
||||||
|
// tail recurse into another signal handler
|
||||||
|
sf->si.si_signo = sig;
|
||||||
|
sf->si.si_code = SI_KERNEL;
|
||||||
|
if (sf->flags & SA_RESETHAND) {
|
||||||
|
STRACE("resetting %G handler", sig);
|
||||||
|
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||||
|
@ -266,7 +338,7 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||||
if ((pt->tib->tib_sigmask & (1ull << (sig - 1))) ||
|
if ((pt->tib->tib_sigmask & (1ull << (sig - 1))) ||
|
||||||
!((uintptr_t)__executable_start <= nc.Rip &&
|
!((uintptr_t)__executable_start <= nc.Rip &&
|
||||||
nc.Rip < (uintptr_t)__privileged_start)) {
|
nc.Rip < (uintptr_t)__privileged_start)) {
|
||||||
STRACE("enqueing %G on %d", sig, _pthread_tid(pt));
|
STRACE("enqueing %G on %d rip %p", sig, _pthread_tid(pt), nc.Rip);
|
||||||
pt->tib->tib_sigpending |= 1ull << (sig - 1);
|
pt->tib->tib_sigpending |= 1ull << (sig - 1);
|
||||||
ResumeThread(th);
|
ResumeThread(th);
|
||||||
__sig_cancel(pt, sig, flags);
|
__sig_cancel(pt, sig, flags);
|
||||||
|
@ -345,11 +417,11 @@ textwindows void __sig_generate(int sig, int sic) {
|
||||||
if (mark) {
|
if (mark) {
|
||||||
STRACE("generating %G by killing %d", sig, _pthread_tid(mark));
|
STRACE("generating %G by killing %d", sig, _pthread_tid(mark));
|
||||||
__sig_killer(mark, sig, sic);
|
__sig_killer(mark, sig, sic);
|
||||||
|
_pthread_unref(mark);
|
||||||
} else {
|
} else {
|
||||||
STRACE("all threads block %G so adding to pending signals of process", sig);
|
STRACE("all threads block %G so adding to pending signals of process", sig);
|
||||||
__sig.pending |= 1ull << (sig - 1);
|
__sig.pending |= 1ull << (sig - 1);
|
||||||
}
|
}
|
||||||
_pthread_unref(mark);
|
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,34 +611,17 @@ __msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows int __sig_checker(atomic_ulong *sigs, struct CosmoTib *tib) {
|
|
||||||
int sig, handler_was_called = 0;
|
|
||||||
sigset_t bit, pending, masked, deliverable;
|
|
||||||
pending = atomic_load_explicit(sigs, memory_order_acquire);
|
|
||||||
masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire);
|
|
||||||
if ((deliverable = pending & ~masked)) {
|
|
||||||
do {
|
|
||||||
sig = _bsf(deliverable) + 1;
|
|
||||||
bit = 1ull << (sig - 1);
|
|
||||||
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit) {
|
|
||||||
STRACE("found pending %G we can raise now", sig);
|
|
||||||
handler_was_called |= __sig_raise(sig, SI_KERNEL);
|
|
||||||
}
|
|
||||||
} while ((deliverable &= ~bit));
|
|
||||||
}
|
|
||||||
return handler_was_called;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns 0 if no signal handlers were called, otherwise a bitmask
|
// returns 0 if no signal handlers were called, otherwise a bitmask
|
||||||
// consisting of `1` which means a signal handler was invoked which
|
// consisting of `1` which means a signal handler was invoked which
|
||||||
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
|
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
|
||||||
// handlers were called (or `3` if both were the case).
|
// handlers were called (or `3` if both were the case).
|
||||||
textwindows int __sig_check(void) {
|
textwindows int __sig_check(void) {
|
||||||
int handler_was_called = false;
|
int sig;
|
||||||
struct CosmoTib *tib = __get_tls();
|
if ((sig = __sig_get(__get_tls()))) {
|
||||||
handler_was_called |= __sig_checker(&tib->tib_sigpending, tib);
|
return __sig_raise(sig, SI_KERNEL);
|
||||||
handler_was_called |= __sig_checker(&__sig.pending, tib);
|
} else {
|
||||||
return handler_was_called;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textstartup void __sig_init(void) {
|
textstartup void __sig_init(void) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct Signals __sig;
|
||||||
sigset_t __sig_block(void) {
|
sigset_t __sig_block(void) {
|
||||||
if (IsWindows()) {
|
if (IsWindows()) {
|
||||||
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, -1,
|
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, -1,
|
||||||
memory_order_acq_rel);
|
memory_order_acquire);
|
||||||
} else {
|
} else {
|
||||||
sigset_t res, neu = -1;
|
sigset_t res, neu = -1;
|
||||||
sys_sigprocmask(SIG_SETMASK, &neu, &res);
|
sys_sigprocmask(SIG_SETMASK, &neu, &res);
|
||||||
|
@ -55,7 +55,7 @@ textwindows int __sig_enqueue(int sig) {
|
||||||
|
|
||||||
textwindows sigset_t __sig_beginwait(sigset_t waitmask) {
|
textwindows sigset_t __sig_beginwait(sigset_t waitmask) {
|
||||||
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
|
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
|
||||||
memory_order_acq_rel);
|
memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
textwindows void __sig_finishwait(sigset_t m) {
|
textwindows void __sig_finishwait(sigset_t m) {
|
||||||
|
|
|
@ -598,6 +598,10 @@ imp 'PdhOpenQuery' PdhOpenQueryW pdh 3 # Creates a new query that is
|
||||||
# PSAPI.DLL
|
# PSAPI.DLL
|
||||||
#
|
#
|
||||||
# Name Actual DLL Arity
|
# Name Actual DLL Arity
|
||||||
|
imp 'EnumProcessModules' EnumProcessModules psapi 4
|
||||||
|
imp 'EnumProcessModulesEx' EnumProcessModulesEx psapi 5
|
||||||
|
imp 'EnumProcesses' EnumProcesses psapi 3
|
||||||
|
imp 'GetModuleBaseName' GetModuleBaseNameW psapi 4
|
||||||
imp 'GetProcessImageFileName' GetProcessImageFileNameW psapi 3
|
imp 'GetProcessImageFileName' GetProcessImageFileNameW psapi 3
|
||||||
imp 'GetProcessMemoryInfo' GetProcessMemoryInfo psapi 3
|
imp 'GetProcessMemoryInfo' GetProcessMemoryInfo psapi 3
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,17 @@ int64_t CreateToolhelp32Snapshot(uint32_t dwFlags, uint32_t th32ProcessID);
|
||||||
bool32 Process32First(int64_t hSnapshot, struct NtProcessEntry32 *in_out_lppe);
|
bool32 Process32First(int64_t hSnapshot, struct NtProcessEntry32 *in_out_lppe);
|
||||||
bool32 Process32Next(int64_t hSnapshot, struct NtProcessEntry32 *out_lppe);
|
bool32 Process32Next(int64_t hSnapshot, struct NtProcessEntry32 *out_lppe);
|
||||||
|
|
||||||
|
bool32 EnumProcesses(uint32_t *out_lpidProcess, uint32_t cb,
|
||||||
|
uint32_t *out_lpcbNeeded) paramsnonnull();
|
||||||
|
bool32 EnumProcessModules(int64_t hProcess, int64_t *out_lphModule, uint32_t cb,
|
||||||
|
uint32_t *out_lpcbNeeded) paramsnonnull();
|
||||||
|
bool32 EnumProcessModulesEx(int64_t hProcess, int64_t *out_lphModule,
|
||||||
|
uint32_t cb, uint32_t *out_lpcbNeeded,
|
||||||
|
uint32_t dwFilterFlag) paramsnonnull();
|
||||||
|
uint32_t GetModuleBaseName(int64_t hProcess, int64_t opt_hModule,
|
||||||
|
char16_t *out_lpBaseName, uint32_t nSize)
|
||||||
|
paramsnonnull();
|
||||||
|
|
||||||
#if ShouldUseMsabiAttribute()
|
#if ShouldUseMsabiAttribute()
|
||||||
#include "libc/nt/thunk/process.inc"
|
#include "libc/nt/thunk/process.inc"
|
||||||
#endif /* ShouldUseMsabiAttribute() */
|
#endif /* ShouldUseMsabiAttribute() */
|
||||||
|
|
18
libc/nt/psapi/EnumProcessModules.S
Normal file
18
libc/nt/psapi/EnumProcessModules.S
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "libc/nt/codegen.h"
|
||||||
|
.imp psapi,__imp_EnumProcessModules,EnumProcessModules
|
||||||
|
|
||||||
|
.text.windows
|
||||||
|
.ftrace1
|
||||||
|
EnumProcessModules:
|
||||||
|
.ftrace2
|
||||||
|
#ifdef __x86_64__
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
mov __imp_EnumProcessModules(%rip),%rax
|
||||||
|
jmp __sysv2nt
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
mov x0,#0
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
.endfn EnumProcessModules,globl
|
||||||
|
.previous
|
18
libc/nt/psapi/EnumProcessModulesEx.S
Normal file
18
libc/nt/psapi/EnumProcessModulesEx.S
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "libc/nt/codegen.h"
|
||||||
|
.imp psapi,__imp_EnumProcessModulesEx,EnumProcessModulesEx
|
||||||
|
|
||||||
|
.text.windows
|
||||||
|
.ftrace1
|
||||||
|
EnumProcessModulesEx:
|
||||||
|
.ftrace2
|
||||||
|
#ifdef __x86_64__
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
mov __imp_EnumProcessModulesEx(%rip),%rax
|
||||||
|
jmp __sysv2nt6
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
mov x0,#0
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
.endfn EnumProcessModulesEx,globl
|
||||||
|
.previous
|
18
libc/nt/psapi/EnumProcesses.S
Normal file
18
libc/nt/psapi/EnumProcesses.S
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "libc/nt/codegen.h"
|
||||||
|
.imp psapi,__imp_EnumProcesses,EnumProcesses
|
||||||
|
|
||||||
|
.text.windows
|
||||||
|
.ftrace1
|
||||||
|
EnumProcesses:
|
||||||
|
.ftrace2
|
||||||
|
#ifdef __x86_64__
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
mov __imp_EnumProcesses(%rip),%rax
|
||||||
|
jmp __sysv2nt
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
mov x0,#0
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
.endfn EnumProcesses,globl
|
||||||
|
.previous
|
18
libc/nt/psapi/GetModuleBaseNameW.S
Normal file
18
libc/nt/psapi/GetModuleBaseNameW.S
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "libc/nt/codegen.h"
|
||||||
|
.imp psapi,__imp_GetModuleBaseNameW,GetModuleBaseNameW
|
||||||
|
|
||||||
|
.text.windows
|
||||||
|
.ftrace1
|
||||||
|
GetModuleBaseName:
|
||||||
|
.ftrace2
|
||||||
|
#ifdef __x86_64__
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
mov __imp_GetModuleBaseNameW(%rip),%rax
|
||||||
|
jmp __sysv2nt
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
mov x0,#0
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
.endfn GetModuleBaseName,globl
|
||||||
|
.previous
|
|
@ -395,6 +395,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
||||||
__set_tls(tib);
|
__set_tls(tib);
|
||||||
__morph_tls();
|
__morph_tls();
|
||||||
__tls_enabled_set(true);
|
__tls_enabled_set(true);
|
||||||
|
// get new main thread handle
|
||||||
// clear pending signals
|
// clear pending signals
|
||||||
tib->tib_sigpending = 0;
|
tib->tib_sigpending = 0;
|
||||||
atomic_store_explicit(&__sig.pending, 0, memory_order_relaxed);
|
atomic_store_explicit(&__sig.pending, 0, memory_order_relaxed);
|
||||||
|
@ -404,9 +405,9 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
||||||
if (ftrace_stackdigs) {
|
if (ftrace_stackdigs) {
|
||||||
_weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)());
|
_weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)());
|
||||||
}
|
}
|
||||||
// reset console
|
// reset core runtime services
|
||||||
|
__proc_wipe();
|
||||||
__keystroke_wipe();
|
__keystroke_wipe();
|
||||||
// reset alarms
|
|
||||||
if (_weaken(__itimer_wipe)) {
|
if (_weaken(__itimer_wipe)) {
|
||||||
_weaken(__itimer_wipe)();
|
_weaken(__itimer_wipe)();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,21 +27,22 @@
|
||||||
#include "libc/intrin/dll.h"
|
#include "libc/intrin/dll.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
|
#include "libc/nt/files.h"
|
||||||
#include "libc/nt/process.h"
|
#include "libc/nt/process.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
|
#include "libc/nt/thread.h"
|
||||||
#include "libc/proc/proc.internal.h"
|
#include "libc/proc/proc.internal.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/runtime/syslib.internal.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
int _fork(uint32_t dwCreationFlags) {
|
int _fork(uint32_t dwCreationFlags) {
|
||||||
struct Dll *e;
|
struct Dll *e;
|
||||||
struct CosmoTib *tib;
|
|
||||||
int ax, dx, tid, parent;
|
int ax, dx, tid, parent;
|
||||||
struct PosixThread *me, *other;
|
|
||||||
parent = __pid;
|
parent = __pid;
|
||||||
(void)parent;
|
(void)parent;
|
||||||
BLOCK_SIGNALS;
|
BLOCK_SIGNALS;
|
||||||
|
@ -55,33 +56,55 @@ int _fork(uint32_t dwCreationFlags) {
|
||||||
ax = sys_fork_nt(dwCreationFlags);
|
ax = sys_fork_nt(dwCreationFlags);
|
||||||
}
|
}
|
||||||
if (!ax) {
|
if (!ax) {
|
||||||
|
|
||||||
|
// get new process id
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
dx = sys_getpid().ax;
|
dx = sys_getpid().ax;
|
||||||
} else {
|
} else {
|
||||||
dx = GetCurrentProcessId();
|
dx = GetCurrentProcessId();
|
||||||
}
|
}
|
||||||
__pid = dx;
|
__pid = dx;
|
||||||
tib = __get_tls();
|
|
||||||
me = (struct PosixThread *)tib->tib_pthread;
|
// turn other threads into zombies
|
||||||
dll_remove(&_pthread_list, &me->list);
|
// we can't free() them since we're monopolizing all locks
|
||||||
|
// we assume the operating system already reclaimed system handles
|
||||||
|
struct CosmoTib *tib = __get_tls();
|
||||||
|
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
|
||||||
|
dll_remove(&_pthread_list, &pt->list);
|
||||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||||
other = POSIXTHREAD_CONTAINER(e);
|
atomic_store_explicit(&POSIXTHREAD_CONTAINER(e)->pt_status,
|
||||||
atomic_store_explicit(&other->pt_status, kPosixThreadZombie,
|
kPosixThreadZombie, memory_order_relaxed);
|
||||||
|
atomic_store_explicit(&POSIXTHREAD_CONTAINER(e)->tib->tib_syshand, 0,
|
||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
}
|
}
|
||||||
dll_make_first(&_pthread_list, &me->list);
|
dll_make_first(&_pthread_list, &pt->list);
|
||||||
|
|
||||||
|
// get new main thread id
|
||||||
tid = IsLinux() || IsXnuSilicon() ? dx : sys_gettid();
|
tid = IsLinux() || IsXnuSilicon() ? dx : sys_gettid();
|
||||||
atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed);
|
atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed);
|
||||||
atomic_store_explicit(&me->ptid, tid, memory_order_relaxed);
|
atomic_store_explicit(&pt->ptid, tid, memory_order_relaxed);
|
||||||
atomic_store_explicit(&me->pt_canceled, false, memory_order_relaxed);
|
|
||||||
|
// get new system thread handle
|
||||||
|
intptr_t syshand = 0;
|
||||||
|
if (IsXnuSilicon()) {
|
||||||
|
syshand = __syslib->__pthread_self();
|
||||||
|
} else if (IsWindows()) {
|
||||||
|
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
|
||||||
|
GetCurrentProcess(), &syshand, 0, false,
|
||||||
|
kNtDuplicateSameAccess);
|
||||||
|
}
|
||||||
|
atomic_store_explicit(&tib->tib_syshand, syshand, memory_order_relaxed);
|
||||||
|
|
||||||
|
// we can't be canceled if the canceler no longer exists
|
||||||
|
atomic_store_explicit(&pt->pt_canceled, false, memory_order_relaxed);
|
||||||
|
|
||||||
|
// run user fork callbacks
|
||||||
if (__threaded && _weaken(_pthread_onfork_child)) {
|
if (__threaded && _weaken(_pthread_onfork_child)) {
|
||||||
_weaken(_pthread_onfork_child)();
|
_weaken(_pthread_onfork_child)();
|
||||||
}
|
}
|
||||||
if (IsWindows()) {
|
|
||||||
__proc_wipe();
|
|
||||||
}
|
|
||||||
STRACE("fork() → 0 (child of %d)", parent);
|
STRACE("fork() → 0 (child of %d)", parent);
|
||||||
} else {
|
} else {
|
||||||
|
// this is the parent process
|
||||||
if (__threaded && _weaken(_pthread_onfork_parent)) {
|
if (__threaded && _weaken(_pthread_onfork_parent)) {
|
||||||
_weaken(_pthread_onfork_parent)();
|
_weaken(_pthread_onfork_parent)();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/nt/files.h"
|
#include "libc/nt/files.h"
|
||||||
|
#include "libc/nt/process.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
|
@ -191,10 +192,11 @@ textstartup void __enable_tls(void) {
|
||||||
tib->tib_locale = (intptr_t)&__c_dot_utf8_locale;
|
tib->tib_locale = (intptr_t)&__c_dot_utf8_locale;
|
||||||
tib->tib_pthread = (pthread_t)&_pthread_static;
|
tib->tib_pthread = (pthread_t)&_pthread_static;
|
||||||
if (IsWindows()) {
|
if (IsWindows()) {
|
||||||
intptr_t threadhand, pseudo = GetCurrentThread();
|
intptr_t hThread;
|
||||||
DuplicateHandle(GetCurrentProcess(), pseudo, GetCurrentProcess(),
|
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
|
||||||
&threadhand, 0, true, kNtDuplicateSameAccess);
|
GetCurrentProcess(), &hThread, 0, false,
|
||||||
atomic_store_explicit(&tib->tib_syshand, threadhand, memory_order_relaxed);
|
kNtDuplicateSameAccess);
|
||||||
|
atomic_store_explicit(&tib->tib_syshand, hThread, memory_order_relaxed);
|
||||||
} else if (IsXnuSilicon()) {
|
} else if (IsXnuSilicon()) {
|
||||||
tib->tib_syshand = __syslib->__pthread_self();
|
tib->tib_syshand = __syslib->__pthread_self();
|
||||||
}
|
}
|
||||||
|
|
151
test/libc/proc/handkill_test.c
Normal file
151
test/libc/proc/handkill_test.c
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/atomic.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/struct/sigaction.h"
|
||||||
|
#include "libc/calls/struct/sigset.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/nt/process.h"
|
||||||
|
#include "libc/nt/thread.h"
|
||||||
|
#include "libc/runtime/clktck.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/testlib/subprocess.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
// test we can:
|
||||||
|
// 1. raise the same signal inside the signal handler
|
||||||
|
// 2. that the signal will trigger immediately after handling
|
||||||
|
|
||||||
|
struct SharedMemory {
|
||||||
|
pthread_t target;
|
||||||
|
atomic_bool ready;
|
||||||
|
atomic_bool got_signal;
|
||||||
|
atomic_bool handler_returned;
|
||||||
|
} * shm;
|
||||||
|
|
||||||
|
void OnSig(int sig) {
|
||||||
|
signal(SIGUSR1, SIG_DFL);
|
||||||
|
raise(SIGUSR1);
|
||||||
|
shm->got_signal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUpOnce(void) {
|
||||||
|
signal(SIGUSR1, OnSig);
|
||||||
|
shm = _mapshared(sizeof(*shm));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDownOnce(void) {
|
||||||
|
munmap(shm, sizeof(*shm));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp(void) {
|
||||||
|
bzero(shm, sizeof(*shm));
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Killer(void *arg) {
|
||||||
|
ASSERT_EQ(0, pthread_kill(shm->target, SIGUSR1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Killed(void *arg) {
|
||||||
|
shm->ready = true;
|
||||||
|
while (!shm->got_signal) donothing;
|
||||||
|
shm->handler_returned = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(handkill, raise) {
|
||||||
|
SPAWN(fork);
|
||||||
|
raise(SIGUSR1);
|
||||||
|
shm->handler_returned = true;
|
||||||
|
TERMS(SIGUSR1);
|
||||||
|
EXPECT_TRUE(shm->got_signal);
|
||||||
|
EXPECT_FALSE(shm->handler_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(handkill, main2thread_async) {
|
||||||
|
SPAWN(fork);
|
||||||
|
pthread_t th;
|
||||||
|
shm->target = pthread_self();
|
||||||
|
pthread_create(&th, 0, Killed, 0);
|
||||||
|
while (!shm->ready) donothing;
|
||||||
|
ASSERT_EQ(0, pthread_kill(th, SIGUSR1));
|
||||||
|
ASSERT_EQ(0, pthread_join(th, 0));
|
||||||
|
TERMS(SIGUSR1);
|
||||||
|
EXPECT_TRUE(shm->got_signal);
|
||||||
|
EXPECT_FALSE(shm->handler_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(handkill, thread2main_async) {
|
||||||
|
SPAWN(fork);
|
||||||
|
pthread_t th;
|
||||||
|
shm->target = pthread_self();
|
||||||
|
pthread_create(&th, 0, Killer, 0);
|
||||||
|
while (!shm->got_signal) donothing;
|
||||||
|
shm->handler_returned = true;
|
||||||
|
pthread_join(th, 0);
|
||||||
|
TERMS(SIGUSR1);
|
||||||
|
EXPECT_TRUE(shm->got_signal);
|
||||||
|
EXPECT_FALSE(shm->handler_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(handkill, thread2thread_async) {
|
||||||
|
SPAWN(fork);
|
||||||
|
pthread_t th;
|
||||||
|
pthread_create(&shm->target, 0, Killed, 0);
|
||||||
|
pthread_create(&th, 0, Killer, 0);
|
||||||
|
pthread_join(shm->target, 0);
|
||||||
|
pthread_join(th, 0);
|
||||||
|
TERMS(SIGUSR1);
|
||||||
|
EXPECT_TRUE(shm->got_signal);
|
||||||
|
EXPECT_FALSE(shm->handler_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(handkill, process_async) {
|
||||||
|
if (IsWindows()) return;
|
||||||
|
SPAWN(fork);
|
||||||
|
shm->ready = true;
|
||||||
|
while (!shm->got_signal) donothing;
|
||||||
|
shm->handler_returned = true;
|
||||||
|
PARENT();
|
||||||
|
while (!shm->ready) donothing;
|
||||||
|
ASSERT_SYS(0, 0, kill(child, SIGUSR1));
|
||||||
|
WAIT(term, SIGUSR1);
|
||||||
|
EXPECT_TRUE(shm->got_signal);
|
||||||
|
EXPECT_FALSE(shm->handler_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(handkill, process_pause) {
|
||||||
|
if (IsWindows()) return;
|
||||||
|
SPAWN(fork);
|
||||||
|
shm->ready = true;
|
||||||
|
pause();
|
||||||
|
shm->handler_returned = true;
|
||||||
|
PARENT();
|
||||||
|
while (!shm->ready) donothing;
|
||||||
|
usleep(1e6 / CLK_TCK * 2);
|
||||||
|
ASSERT_SYS(0, 0, kill(child, SIGUSR1));
|
||||||
|
WAIT(term, SIGUSR1);
|
||||||
|
EXPECT_TRUE(shm->got_signal);
|
||||||
|
EXPECT_FALSE(shm->handler_returned);
|
||||||
|
}
|
|
@ -22,6 +22,7 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/runtime/clktck.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
#include "libc/sock/struct/sockaddr.h"
|
#include "libc/sock/struct/sockaddr.h"
|
||||||
|
|
|
@ -34,6 +34,7 @@ TOOL_BUILD_DIRECTDEPS = \
|
||||||
LIBC_MEM \
|
LIBC_MEM \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_NT_KERNEL32 \
|
LIBC_NT_KERNEL32 \
|
||||||
|
LIBC_NT_PSAPI \
|
||||||
LIBC_NT_USER32 \
|
LIBC_NT_USER32 \
|
||||||
LIBC_NT_WS2_32 \
|
LIBC_NT_WS2_32 \
|
||||||
LIBC_PROC \
|
LIBC_PROC \
|
||||||
|
|
229
tool/build/killall.c
Normal file
229
tool/build/killall.c
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/nt/enum/formatmessageflags.h"
|
||||||
|
#include "libc/nt/enum/lang.h"
|
||||||
|
#include "libc/nt/enum/processaccess.h"
|
||||||
|
#include "libc/nt/process.h"
|
||||||
|
#include "libc/nt/runtime.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/stdio/internal.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/x/x.h"
|
||||||
|
#include "third_party/getopt/getopt.internal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview Mass Process Killer for Windows.
|
||||||
|
*
|
||||||
|
* Bad things sometimes happen during development, such as fork bombs.
|
||||||
|
* Under such circumstances, the Windows operating system itself remains
|
||||||
|
* remarkably stable (much more so than Linux would in these cases) but
|
||||||
|
* the tools on Windows for managing processes do not scale gracefully;
|
||||||
|
* GUIs like the Task Manager and Process Explorer become unresponsive
|
||||||
|
* when the system has a nontrivial number of processes, leaving no way
|
||||||
|
* to kill the processes. This tool is designed to make it easy to kill
|
||||||
|
* processes, particularly in large numbers, via a simple CLI interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *prog;
|
||||||
|
static char16_t **filters;
|
||||||
|
static uint32_t pids[10000];
|
||||||
|
|
||||||
|
static wontreturn void PrintUsage(int rc, FILE *f) {
|
||||||
|
fprintf(f,
|
||||||
|
"Usage: %s [-nshv] NAME...\n"
|
||||||
|
"\tNAME\tcase-insensitive process name substring filter\n"
|
||||||
|
"\t-n\tdry run (print matching processes but do not kill)\n"
|
||||||
|
"\t-v\tverbose mode\n"
|
||||||
|
"\t-s\tsilent mode\n"
|
||||||
|
"\t-h\tshow help\n",
|
||||||
|
prog);
|
||||||
|
exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static wontreturn void OutOfMemory(void) {
|
||||||
|
fprintf(stderr, "%s: out of memory\n", prog);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *Calloc(size_t n, size_t z) {
|
||||||
|
void *p;
|
||||||
|
if (!(p = calloc(n, z))) OutOfMemory();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ConvertStringToLowercase16(char16_t *s) {
|
||||||
|
while (*s) {
|
||||||
|
*s = towlower(*s);
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ShouldKillProcess(char16_t *name) {
|
||||||
|
if (!*filters) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (char16_t **f = filters; *f; ++f) {
|
||||||
|
if (strstr16(name, *f)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t MyOpenProcess(uint32_t pid) {
|
||||||
|
return OpenProcess(
|
||||||
|
kNtProcessTerminate | kNtProcessQueryInformation | kNtProcessVmRead,
|
||||||
|
false, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetProcessName(int64_t hand, char16_t name[hasatleast PATH_MAX]) {
|
||||||
|
int64_t hMod;
|
||||||
|
uint32_t cbNeeded;
|
||||||
|
*name = 0;
|
||||||
|
if (EnumProcessModules(hand, &hMod, sizeof(hMod), &cbNeeded)) {
|
||||||
|
GetModuleBaseName(hand, hMod, name, PATH_MAX);
|
||||||
|
}
|
||||||
|
return !!name[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static char16_t *DescribeError(int err) {
|
||||||
|
static char16_t msg[256];
|
||||||
|
FormatMessage(kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0,
|
||||||
|
err, MAKELANGID(kNtLangNeutral, kNtSublangDefault), msg,
|
||||||
|
ARRAYLEN(msg), 0);
|
||||||
|
return chomp16(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
// get program name
|
||||||
|
prog = argv[0] ? argv[0] : "killall";
|
||||||
|
|
||||||
|
// sanity check environment
|
||||||
|
if (!IsWindows()) {
|
||||||
|
fprintf(stderr, "%s: this program is intended for windows\n", prog);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to minimize terminal writes slowing us down
|
||||||
|
setvbuf(stdin, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
// get flags
|
||||||
|
int opt;
|
||||||
|
bool dryrun = false;
|
||||||
|
bool silent = false;
|
||||||
|
bool verbose = false;
|
||||||
|
while ((opt = getopt(argc, argv, "nhsv")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'n':
|
||||||
|
dryrun = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
silent = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
PrintUsage(0, stdout);
|
||||||
|
default:
|
||||||
|
PrintUsage(1, stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get names of things to kill
|
||||||
|
filters = Calloc(argc, sizeof(char16_t *));
|
||||||
|
for (j = 0, i = optind; i < argc; ++i) {
|
||||||
|
char16_t *filter;
|
||||||
|
if ((filter = utf8to16(argv[i], -1, 0)) && *filter) {
|
||||||
|
ConvertStringToLowercase16(filter);
|
||||||
|
filters[j++] = filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!j && !dryrun) {
|
||||||
|
fprintf(stderr, "%s: missing operand\n", prog);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// outer loop
|
||||||
|
int count = 0;
|
||||||
|
int subcount;
|
||||||
|
do {
|
||||||
|
|
||||||
|
// get pids of all processes on system
|
||||||
|
uint32_t n;
|
||||||
|
if (!EnumProcesses(pids, sizeof(pids), &n)) {
|
||||||
|
fprintf(stderr, "%s: EnumProcesses() failed: %s\n", prog,
|
||||||
|
DescribeError(GetLastError()));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
n /= sizeof(uint32_t);
|
||||||
|
|
||||||
|
// kill matching processes
|
||||||
|
int64_t hProcess;
|
||||||
|
char16_t name[PATH_MAX];
|
||||||
|
for (subcount = i = 0; i < n; i++) {
|
||||||
|
if (!pids[i]) continue;
|
||||||
|
if ((hProcess = MyOpenProcess(pids[i]))) {
|
||||||
|
if (GetProcessName(hProcess, name)) {
|
||||||
|
ConvertStringToLowercase16(name);
|
||||||
|
if (ShouldKillProcess(name)) {
|
||||||
|
if (dryrun) {
|
||||||
|
if (!silent) {
|
||||||
|
printf("%5u %hs\n", pids[i], name);
|
||||||
|
}
|
||||||
|
++subcount;
|
||||||
|
} else if (TerminateProcess(hProcess, SIGKILL)) {
|
||||||
|
if (!silent) {
|
||||||
|
printf("%5u %hs killed\n", pids[i], name);
|
||||||
|
}
|
||||||
|
++subcount;
|
||||||
|
} else {
|
||||||
|
printf("%5u %hs %hs\n", pids[i], name,
|
||||||
|
DescribeError(GetLastError()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (verbose) {
|
||||||
|
fprintf(stderr, "%s: GetProcessName(%u) failed: %hs\n", prog, pids[i],
|
||||||
|
DescribeError(GetLastError()));
|
||||||
|
}
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
} else if (verbose) {
|
||||||
|
fprintf(stderr, "%s: OpenProcess(%u) failed: %hs\n", prog, pids[i],
|
||||||
|
DescribeError(GetLastError()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't stop until we've confirmed they're all gone
|
||||||
|
count += subcount;
|
||||||
|
} while (!dryrun && subcount);
|
||||||
|
|
||||||
|
if (!silent && !count) {
|
||||||
|
fprintf(stderr, "%s: no processes found\n", prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue