mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Spoof PID across execve() on Windows
It's now possible with cosmo and redbean, to deliver a signal to a child process after it has called execve(). However the executed program needs to be compiled using cosmocc. The cosmo runtime WinMain() implementation now intercepts a _COSMO_PID environment variable that's set by execve(). It ensures the child process will use the same C:\ProgramData\cosmo\sigs file, which is where kill() will place the delivered signal. We are able to do this on Windows even better than NetBSD, which has a bug with this Fixes #1334
This commit is contained in:
parent
9cc1bd04b2
commit
26c051c297
8 changed files with 187 additions and 21 deletions
|
@ -667,6 +667,9 @@ textwindows int __sig_check(void) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this mutex is needed so execve() can shut down the signal worker
|
||||||
|
pthread_mutex_t __sig_worker_lock;
|
||||||
|
|
||||||
// background thread for delivering inter-process signals asynchronously
|
// background thread for delivering inter-process signals asynchronously
|
||||||
// this checks for undelivered process-wide signals, once per scheduling
|
// this checks for undelivered process-wide signals, once per scheduling
|
||||||
// quantum, which on windows should be every ~15ms or so, unless somehow
|
// quantum, which on windows should be every ~15ms or so, unless somehow
|
||||||
|
@ -680,6 +683,7 @@ textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
|
||||||
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
|
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
|
||||||
STKSZ);
|
STKSZ);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
pthread_mutex_lock(&__sig_worker_lock);
|
||||||
|
|
||||||
// dequeue all pending signals and fire them off. if there's no
|
// dequeue all pending signals and fire them off. if there's no
|
||||||
// thread that can handle them then __sig_generate will requeue
|
// thread that can handle them then __sig_generate will requeue
|
||||||
|
@ -724,6 +728,7 @@ textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
|
||||||
_pthread_unlock();
|
_pthread_unlock();
|
||||||
|
|
||||||
// wait until next scheduler quantum
|
// wait until next scheduler quantum
|
||||||
|
pthread_mutex_unlock(&__sig_worker_lock);
|
||||||
Sleep(POLL_INTERVAL_MS);
|
Sleep(POLL_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/atomic.h"
|
#include "libc/atomic.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/sig.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/nt/files.h"
|
#include "libc/nt/files.h"
|
||||||
#include "libc/nt/memory.h"
|
#include "libc/nt/memory.h"
|
||||||
|
|
|
@ -17,13 +17,14 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/sig.internal.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/fmt/itoa.h"
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/intrin/fds.h"
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nt/enum/processaccess.h"
|
#include "libc/nt/enum/processaccess.h"
|
||||||
#include "libc/nt/enum/startf.h"
|
#include "libc/nt/enum/startf.h"
|
||||||
|
@ -33,8 +34,10 @@
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/struct/processinformation.h"
|
#include "libc/nt/struct/processinformation.h"
|
||||||
#include "libc/nt/struct/startupinfo.h"
|
#include "libc/nt/struct/startupinfo.h"
|
||||||
|
#include "libc/nt/thunk/msabi.h"
|
||||||
#include "libc/proc/describefds.internal.h"
|
#include "libc/proc/describefds.internal.h"
|
||||||
#include "libc/proc/ntspawn.h"
|
#include "libc/proc/ntspawn.h"
|
||||||
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/at.h"
|
#include "libc/sysv/consts/at.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
@ -43,23 +46,37 @@
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess;
|
||||||
|
|
||||||
|
extern pthread_mutex_t __sig_worker_lock;
|
||||||
|
|
||||||
|
static void sys_execve_nt_abort(sigset_t sigmask) {
|
||||||
|
_pthread_unlock();
|
||||||
|
pthread_mutex_unlock(&__sig_worker_lock);
|
||||||
|
__sig_unblock(sigmask);
|
||||||
|
}
|
||||||
|
|
||||||
textwindows int sys_execve_nt(const char *program, char *const argv[],
|
textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||||
char *const envp[]) {
|
char *const envp[]) {
|
||||||
|
|
||||||
// execve() needs to be @asyncsignalsafe
|
// execve() needs to be @asyncsignalsafe
|
||||||
sigset_t sigmask = __sig_block();
|
sigset_t sigmask = __sig_block();
|
||||||
_pthread_lock();
|
pthread_mutex_lock(&__sig_worker_lock); // order matters
|
||||||
|
_pthread_lock(); // order matters
|
||||||
|
|
||||||
// new process should be a child of our parent
|
// new process should be a child of our parent
|
||||||
int64_t hParentProcess;
|
int64_t hParentProcess;
|
||||||
int ppid = sys_getppid_nt();
|
int ppid = sys_getppid_nt();
|
||||||
if (!(hParentProcess = OpenProcess(
|
if (!(hParentProcess = OpenProcess(
|
||||||
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
|
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
|
||||||
_pthread_unlock();
|
sys_execve_nt_abort(sigmask);
|
||||||
__sig_unblock(sigmask);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inherit pid
|
||||||
|
char pidvar[11 + 21];
|
||||||
|
FormatUint64(stpcpy(pidvar, "_COSMO_PID="), __pid);
|
||||||
|
|
||||||
// inherit signal mask
|
// inherit signal mask
|
||||||
char maskvar[6 + 21];
|
char maskvar[6 + 21];
|
||||||
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
|
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
|
||||||
|
@ -84,22 +101,26 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||||
if (!(fdspec = __describe_fds(g_fds.p, g_fds.n, &si, hParentProcess,
|
if (!(fdspec = __describe_fds(g_fds.p, g_fds.n, &si, hParentProcess,
|
||||||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
||||||
CloseHandle(hParentProcess);
|
CloseHandle(hParentProcess);
|
||||||
_pthread_unlock();
|
sys_execve_nt_abort(sigmask);
|
||||||
__sig_unblock(sigmask);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inherit pending signals
|
||||||
|
atomic_fetch_or_explicit(
|
||||||
|
__sig.process,
|
||||||
|
atomic_load_explicit(&__get_tls()->tib_sigpending, memory_order_acquire),
|
||||||
|
memory_order_release);
|
||||||
|
|
||||||
// launch the process
|
// launch the process
|
||||||
struct NtProcessInformation pi;
|
struct NtProcessInformation pi;
|
||||||
int rc = ntspawn(&(struct NtSpawnArgs){
|
int rc = ntspawn(&(struct NtSpawnArgs){
|
||||||
AT_FDCWD, program, argv, envp, (char *[]){fdspec, maskvar, 0}, 0, 0,
|
AT_FDCWD, program, argv, envp, (char *[]){fdspec, maskvar, pidvar, 0}, 0,
|
||||||
hParentProcess, lpExplicitHandles, dwExplicitHandleCount, &si, &pi});
|
0, hParentProcess, lpExplicitHandles, dwExplicitHandleCount, &si, &pi});
|
||||||
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
free(fdspec);
|
free(fdspec);
|
||||||
CloseHandle(hParentProcess);
|
CloseHandle(hParentProcess);
|
||||||
_pthread_unlock();
|
sys_execve_nt_abort(sigmask);
|
||||||
__sig_unblock(sigmask);
|
|
||||||
if (GetLastError() == kNtErrorSharingViolation) {
|
if (GetLastError() == kNtErrorSharingViolation) {
|
||||||
return etxtbsy();
|
return etxtbsy();
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,12 +133,13 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||||
if (DuplicateHandle(GetCurrentProcess(), pi.hProcess, hParentProcess, &handle,
|
if (DuplicateHandle(GetCurrentProcess(), pi.hProcess, hParentProcess, &handle,
|
||||||
0, false, kNtDuplicateSameAccess)) {
|
0, false, kNtDuplicateSameAccess)) {
|
||||||
unassert(!(handle & 0xFFFFFFFFFF000000));
|
unassert(!(handle & 0xFFFFFFFFFF000000));
|
||||||
TerminateThisProcess(0x23000000u | handle);
|
__imp_TerminateProcess(-1, 0x23000000u | handle);
|
||||||
} else {
|
} else {
|
||||||
// TODO(jart): Why does `make loc` print this?
|
// TODO(jart): Why does `make loc` print this?
|
||||||
// kprintf("DuplicateHandle failed w/ %d\n", GetLastError());
|
// kprintf("DuplicateHandle failed w/ %d\n", GetLastError());
|
||||||
TerminateThisProcess(ECHILD);
|
__imp_TerminateProcess(-1, ECHILD);
|
||||||
}
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -36,14 +36,55 @@
|
||||||
/**
|
/**
|
||||||
* Replaces current process with program.
|
* Replaces current process with program.
|
||||||
*
|
*
|
||||||
|
* Your `prog` may be an actually portable executable or a platform
|
||||||
|
* native binary (e.g. ELF, Mach-O, PE). On UNIX systems, your execve
|
||||||
|
* implementation will try to find where the `ape` interpreter program
|
||||||
|
* is installed on your system. The preferred location is `/usr/bin/ape`
|
||||||
|
* except on Apple Silicon where it's `/usr/local/bin/ape`. The $TMPDIR
|
||||||
|
* and $HOME locations that the APE shell script extracts the versioned
|
||||||
|
* ape binaries to will also be checked as a fallback path. Finally, if
|
||||||
|
* `prog` isn't an executable in any recognizable format, cosmo assumes
|
||||||
|
* it's a bourne shell script and launches it under /bin/sh.
|
||||||
|
*
|
||||||
|
* The signal mask and pending signals are inherited by the new process.
|
||||||
|
* Note the NetBSD kernel has a bug where pending signals are cleared.
|
||||||
|
*
|
||||||
|
* File descriptors that haven't been marked `O_CLOEXEC` through various
|
||||||
|
* devices such as open() and fcntl() will be inherited by the executed
|
||||||
|
* subprocess. The current file position of the duplicated descriptors
|
||||||
|
* is shared across processes. On Windows, `prog` needs to be built by
|
||||||
|
* cosmocc in order to properly inherit file descriptors. If a program
|
||||||
|
* compiled by MSVC or Cygwin is launched instead, then only the stdio
|
||||||
|
* file descriptors can be passed along.
|
||||||
|
*
|
||||||
* On Windows, `argv` and `envp` can't contain binary strings. They need
|
* On Windows, `argv` and `envp` can't contain binary strings. They need
|
||||||
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
|
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
|
||||||
* corrupted.
|
* corrupted.
|
||||||
*
|
*
|
||||||
* On Windows, only file descriptors 0, 1 and 2 can be passed to a child
|
* On Windows, cosmo execve uses parent spoofing to implement the UNIX
|
||||||
* process in such a way that allows them to be automatically discovered
|
* behavior of replacing the current process. Since POSIX.1 also needs
|
||||||
* when the child process initializes. Cosmpolitan currently treats your
|
* us to maintain the same PID number too, the _COSMO_PID environemnt
|
||||||
* other file descriptors as implicitly O_CLOEXEC.
|
* variable is passed to the child process which specifies a spoofed
|
||||||
|
* PID. Whatever is in that variable will be reported by getpid() and
|
||||||
|
* other cosmo processes will be able to send signals to the process
|
||||||
|
* using that pid, via kill(). These synthetic PIDs which are only
|
||||||
|
* created by execve could potentially overlap with OS assignments if
|
||||||
|
* Windows recycles them. Cosmo avoids that by tracking handles of
|
||||||
|
* subprocesses. Each process has its own process manager thread, to
|
||||||
|
* associate pids with win32 handles, and execve will tell the parent
|
||||||
|
* process its new handle when it changes. However it's not perfect.
|
||||||
|
* There's still situations where processes created by execve() can
|
||||||
|
* cause surprising things to happen. For an alternative, consider
|
||||||
|
* posix_spawn() which is fastest and awesomest across all OSes.
|
||||||
|
*
|
||||||
|
* On Windows, support is currently not implemented for inheriting
|
||||||
|
* setitimer() and alarm() into an executed process.
|
||||||
|
*
|
||||||
|
* On Windows, support is currently not implemented for inheriting
|
||||||
|
* getrusage() statistics into an executed process.
|
||||||
|
*
|
||||||
|
* The executed process will share the same terminal and current
|
||||||
|
* directory.
|
||||||
*
|
*
|
||||||
* @param program will not be PATH searched, see commandv()
|
* @param program will not be PATH searched, see commandv()
|
||||||
* @param argv[0] is the name of the program to run
|
* @param argv[0] is the name of the program to run
|
||||||
|
|
|
@ -92,6 +92,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
||||||
int64_t handle, closeme = 0;
|
int64_t handle, closeme = 0;
|
||||||
if (!(handle = __proc_handle(pid))) {
|
if (!(handle = __proc_handle(pid))) {
|
||||||
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||||
|
STRACE("warning: kill() using raw win32 pid");
|
||||||
closeme = handle;
|
closeme = handle;
|
||||||
} else {
|
} else {
|
||||||
goto OnError;
|
goto OnError;
|
||||||
|
@ -103,7 +104,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
||||||
// now that we know the process exists, if it has a shared memory file
|
// now that we know the process exists, if it has a shared memory file
|
||||||
// then we can be reasonably certain it's a cosmo process which should
|
// then we can be reasonably certain it's a cosmo process which should
|
||||||
// be trusted to deliver its signal, unless it's a nine exterminations
|
// be trusted to deliver its signal, unless it's a nine exterminations
|
||||||
if (pid > 0 && sig != 9) {
|
if (pid > 0) {
|
||||||
atomic_ulong *sigproc;
|
atomic_ulong *sigproc;
|
||||||
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
|
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
|
||||||
if (sig > 0)
|
if (sig > 0)
|
||||||
|
@ -112,12 +113,15 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
||||||
UnmapViewOfFile(sigproc);
|
UnmapViewOfFile(sigproc);
|
||||||
if (closeme)
|
if (closeme)
|
||||||
CloseHandle(closeme);
|
CloseHandle(closeme);
|
||||||
return 0;
|
if (sig != 9)
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform actual kill
|
// perform actual kill
|
||||||
// process will report WIFSIGNALED with WTERMSIG(sig)
|
// process will report WIFSIGNALED with WTERMSIG(sig)
|
||||||
|
if (sig != 9)
|
||||||
|
STRACE("warning: kill() sending %G via terminate", sig);
|
||||||
bool32 ok = TerminateProcess(handle, sig);
|
bool32 ok = TerminateProcess(handle, sig);
|
||||||
if (closeme)
|
if (closeme)
|
||||||
CloseHandle(closeme);
|
CloseHandle(closeme);
|
||||||
|
|
|
@ -35,6 +35,9 @@
|
||||||
* signal a cosmo process. The targeting process will then notice that a
|
* signal a cosmo process. The targeting process will then notice that a
|
||||||
* signal has been added and delivers to any thread as soon as possible.
|
* signal has been added and delivers to any thread as soon as possible.
|
||||||
*
|
*
|
||||||
|
* On Windows, the only signal that's guaranteed to work on non-cosmocc
|
||||||
|
* processes is SIGKILL.
|
||||||
|
*
|
||||||
* On Windows, the concept of a process group isn't fully implemented.
|
* On Windows, the concept of a process group isn't fully implemented.
|
||||||
* Saying `kill(0, sig)` will deliver `sig` to all direct descendent
|
* Saying `kill(0, sig)` will deliver `sig` to all direct descendent
|
||||||
* processes. Saying `kill(-pid, sig)` will be the same as saying
|
* processes. Saying `kill(-pid, sig)` will be the same as saying
|
||||||
|
|
|
@ -300,6 +300,37 @@ static abi wontreturn void WinInit(const char16_t *cmdline) {
|
||||||
(uintptr_t)(stackaddr + (stacksize - sizeof(struct WinArgs))));
|
(uintptr_t)(stackaddr + (stacksize - sizeof(struct WinArgs))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int Atoi(const char16_t *str) {
|
||||||
|
int c;
|
||||||
|
unsigned x = 0;
|
||||||
|
while ((c = *str++)) {
|
||||||
|
if ('0' <= c && c <= '9') {
|
||||||
|
x *= 10;
|
||||||
|
x += c - '0';
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static abi int WinGetPid(const char16_t *var, bool *out_is_inherited) {
|
||||||
|
uint32_t len;
|
||||||
|
char16_t val[12];
|
||||||
|
if ((len = __imp_GetEnvironmentVariableW(var, val, ARRAYLEN(val)))) {
|
||||||
|
int pid = -1;
|
||||||
|
if (len < ARRAYLEN(val))
|
||||||
|
pid = Atoi(val);
|
||||||
|
__imp_SetEnvironmentVariableW(var, NULL);
|
||||||
|
if (pid > 0) {
|
||||||
|
*out_is_inherited = true;
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out_is_inherited = false;
|
||||||
|
return __imp_GetCurrentProcessId();
|
||||||
|
}
|
||||||
|
|
||||||
abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
||||||
const char *lpCmdLine, int64_t nCmdShow) {
|
const char *lpCmdLine, int64_t nCmdShow) {
|
||||||
static atomic_ulong fake_process_signals;
|
static atomic_ulong fake_process_signals;
|
||||||
|
@ -316,10 +347,12 @@ abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
||||||
__imp_GetSystemInfo(&si);
|
__imp_GetSystemInfo(&si);
|
||||||
__pagesize = si.dwPageSize;
|
__pagesize = si.dwPageSize;
|
||||||
__gransize = si.dwAllocationGranularity;
|
__gransize = si.dwAllocationGranularity;
|
||||||
__pid = __imp_GetCurrentProcessId();
|
bool pid_is_inherited;
|
||||||
|
__pid = WinGetPid(u"_COSMO_PID", &pid_is_inherited);
|
||||||
if (!(__sig.process = __sig_map_process(__pid, kNtOpenAlways)))
|
if (!(__sig.process = __sig_map_process(__pid, kNtOpenAlways)))
|
||||||
__sig.process = &fake_process_signals;
|
__sig.process = &fake_process_signals;
|
||||||
atomic_store_explicit(__sig.process, 0, memory_order_release);
|
if (!pid_is_inherited)
|
||||||
|
atomic_store_explicit(__sig.process, 0, memory_order_release);
|
||||||
cmdline = __imp_GetCommandLineW();
|
cmdline = __imp_GetCommandLineW();
|
||||||
#if SYSDEBUG
|
#if SYSDEBUG
|
||||||
// sloppy flag-only check for early initialization
|
// sloppy flag-only check for early initialization
|
||||||
|
|
59
test/posix/pending_signal_execve_test.c
Normal file
59
test/posix/pending_signal_execve_test.c
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2024 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 <cosmo.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
sig_atomic_t gotsig;
|
||||||
|
|
||||||
|
void onsig(int sig) {
|
||||||
|
gotsig = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
sigset_t ss;
|
||||||
|
sigfillset(&ss);
|
||||||
|
sigprocmask(SIG_BLOCK, &ss, 0);
|
||||||
|
if (argc >= 2 && !strcmp(argv[1], "childe")) {
|
||||||
|
signal(SIGUSR1, onsig);
|
||||||
|
sigemptyset(&ss);
|
||||||
|
sigsuspend(&ss);
|
||||||
|
if (gotsig != SIGUSR1)
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
int child;
|
||||||
|
if ((child = fork()) == -1)
|
||||||
|
return 2;
|
||||||
|
if (!child) {
|
||||||
|
execlp(argv[0], argv[0], "childe", NULL);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
if (IsNetbsd()) {
|
||||||
|
// NetBSD has a bug where pending signals don't inherit across
|
||||||
|
// execve, even though POSIX.1 literally says you must do this
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
if (kill(child, SIGUSR1))
|
||||||
|
return 3;
|
||||||
|
int ws;
|
||||||
|
if (wait(&ws) != child)
|
||||||
|
return 4;
|
||||||
|
if (ws)
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue