move POSIX spawn, exec and child tracking into posixsrv, keep process transactions alive across reconnects and preserve stable PIDs across image replacement, flesh out spawn with ordered file actions, process-group selection and signal inheritance, implement execve with bootstrap validation and rollback before retiring the old process, add vfork children that share the native process while keeping separate POSIX descriptor and signal state, replace local spawn bookkeeping with child reaping, nonblocking and process-group waits and orphan cleanup, add signal dispositions, per-thread masks, pending delivery, process/group signalling and alarms, translate native exceptions into POSIX signals and inherit masks when creating threads, make pipe waits interruptible and raise SIGPIPE for broken pipes, pass arguments and environment into main and add getenv/environ, add direct user APC notifications that can run without an alertable wait, add current-thread capabilities and resume-and-wait, let accepted IPC endpoints open restricted peer process capabilities and preserve caller security during spawn and exec, make process preparation reusable and improve DLL lookup across current, executable and fallback directories, move keyboard repeat into RSSRV so PS/2 and USB share one repeat policy, fix video presentation, report launch failure stages and status codes, mooaaar tests.
This commit is contained in:
1 parent
73abd6f90d
commit
899225f551
57 files changed
+8817
-1216
No files matched your search
@@ -0,0 +1,459 @@
|
||||
// Runtime coverage for P6 POSIX signal delivery, waits, spawn state, and exceptions.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <spawn.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <ntlite/posix_extensions.h>
|
||||
|
||||
#define GROUP_READ_FD 50
|
||||
#define GROUP_WRITE_FD 51
|
||||
|
||||
static volatile sig_atomic_t gHandlerCount;
|
||||
static volatile uint32_t gHandlerTid;
|
||||
static volatile int gWorkerReady;
|
||||
static volatile int gWorkerInheritedBlock;
|
||||
static volatile int gWorkerStop;
|
||||
static volatile sig_atomic_t gNestedCount;
|
||||
static volatile sig_atomic_t gAlarmCount;
|
||||
static volatile sig_atomic_t gPipeCount;
|
||||
|
||||
// Writes one test diagnostic line to the native console.
|
||||
static void print(const char* text) {
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
|
||||
(void)SysWriteConsole(&request);
|
||||
}
|
||||
|
||||
// Exits immediately after one failed assertion.
|
||||
static void fail(const char* message) {
|
||||
print("posix_signal_smoke: FAIL: ");
|
||||
print(message);
|
||||
print("\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Returns whether the compatibility command line contains one child marker.
|
||||
static int child_mode(const char* marker) {
|
||||
const char* command = posix_get_command_line();
|
||||
return command && marker && strstr(command, marker) != (char*)0;
|
||||
}
|
||||
|
||||
// Resolves the current executable path from the compatibility command line.
|
||||
static void get_self_path(char* path, size_t capacity) {
|
||||
const char* command = posix_get_command_line();
|
||||
size_t length = 0;
|
||||
while (command && command[length] && command[length] != ' ' && command[length] != '\t' &&
|
||||
length + 1u < capacity) {
|
||||
path[length] = command[length];
|
||||
++length;
|
||||
}
|
||||
path[length] = '\0';
|
||||
if (length == 0 || (command[length] && command[length] != ' ' && command[length] != '\t'))
|
||||
_exit(90);
|
||||
}
|
||||
|
||||
// Moves one descriptor to an exact number and closes the source alias.
|
||||
static void move_fd(int source, int target) {
|
||||
if (source < 0 || dup2(source, target) != target)
|
||||
fail("control dup2 failed");
|
||||
if (source != target && close(source) != 0)
|
||||
fail("control source close failed");
|
||||
}
|
||||
|
||||
// Records the native thread selected for one caught user signal.
|
||||
static void signal_handler(int signalNumber) {
|
||||
if (signalNumber != SIGUSR1) return;
|
||||
gHandlerTid = SysGetCurrentTid();
|
||||
++gHandlerCount;
|
||||
}
|
||||
|
||||
// Queues the same signal once while its first handler activation masks it.
|
||||
static void nested_handler(int signalNumber) {
|
||||
if (signalNumber != SIGUSR1) return;
|
||||
++gNestedCount;
|
||||
if (gNestedCount == 1 && raise(SIGUSR1) != 0)
|
||||
_exit(91);
|
||||
}
|
||||
|
||||
// Records one SIGALRM used to interrupt alertable POSIX waits.
|
||||
static void alarm_handler(int signalNumber) {
|
||||
if (signalNumber == SIGALRM) ++gAlarmCount;
|
||||
}
|
||||
|
||||
// Records SIGPIPE generation while allowing write() to return EPIPE.
|
||||
static void pipe_handler(int signalNumber) {
|
||||
if (signalNumber == SIGPIPE) ++gPipeCount;
|
||||
}
|
||||
|
||||
// Terminates the process-group child from asynchronous SIGUSR1 delivery.
|
||||
static void group_handler(int signalNumber) {
|
||||
if (signalNumber == SIGUSR1) _exit(61);
|
||||
}
|
||||
|
||||
// Terminates the exception child after native access-fault translation.
|
||||
static void fault_handler(int signalNumber) {
|
||||
if (signalNumber == SIGSEGV) _exit(62);
|
||||
}
|
||||
|
||||
// Keeps one registered unblocked POSIX thread available for process-directed delivery.
|
||||
static void* signal_worker(void* argument) {
|
||||
(void)argument;
|
||||
sigset_t current = 0;
|
||||
if (sigprocmask(SIG_SETMASK, (const sigset_t*)0, ¤t) != 0)
|
||||
return (void*)(uintptr_t)1u;
|
||||
gWorkerInheritedBlock = sigismember(¤t, SIGUSR1) == 1;
|
||||
|
||||
sigset_t unblock = 0;
|
||||
if (sigemptyset(&unblock) != 0 || sigaddset(&unblock, SIGUSR1) != 0 ||
|
||||
sigprocmask(SIG_UNBLOCK, &unblock, (sigset_t*)0) != 0)
|
||||
return (void*)(uintptr_t)1u;
|
||||
|
||||
gWorkerReady = 1;
|
||||
while (!gWorkerStop && gHandlerCount < 3) (void)SysYield();
|
||||
return (void*)0;
|
||||
}
|
||||
|
||||
// Validates signal mask/default attributes before ordinary child entry proceeds.
|
||||
static void run_spawn_attribute_child(void) {
|
||||
sigset_t current = 0;
|
||||
if (sigprocmask(SIG_SETMASK, (const sigset_t*)0, ¤t) != 0 ||
|
||||
sigismember(¤t, SIGUSR1) != 1)
|
||||
_exit(70);
|
||||
struct sigaction action;
|
||||
if (sigaction(SIGUSR2, (const struct sigaction*)0, &action) != 0 ||
|
||||
action.sa_handler != SIG_DFL)
|
||||
_exit(71);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
// Announces readiness, then waits for one process-group-directed SIGUSR1.
|
||||
static void run_group_child(void) {
|
||||
struct sigaction action;
|
||||
action.sa_handler = group_handler;
|
||||
action.sa_mask = 0;
|
||||
action.sa_flags = 0;
|
||||
if (sigaction(SIGUSR1, &action, (struct sigaction*)0) != 0)
|
||||
_exit(72);
|
||||
if (write(GROUP_WRITE_FD, "R", 1) != 1)
|
||||
_exit(73);
|
||||
(void)close(GROUP_WRITE_FD);
|
||||
for (;;) (void)pause();
|
||||
}
|
||||
|
||||
// Waits for the parent to exercise one ordinary default-terminate disposition.
|
||||
static void run_default_child(void) {
|
||||
for (;;) (void)pause();
|
||||
}
|
||||
|
||||
// Triggers one native access fault after installing a POSIX SIGSEGV handler.
|
||||
static void run_exception_child(void) {
|
||||
struct sigaction action;
|
||||
action.sa_handler = fault_handler;
|
||||
action.sa_mask = 0;
|
||||
action.sa_flags = 0;
|
||||
if (sigaction(SIGSEGV, &action, (struct sigaction*)0) != 0)
|
||||
_exit(74);
|
||||
*(volatile uint64_t*)(uintptr_t)1u = 0x1234u;
|
||||
_exit(75);
|
||||
}
|
||||
|
||||
// Spawns one child and requires one exact ordinary exit result.
|
||||
static void wait_for_child(pid_t child, int expectedExit) {
|
||||
int status = -1;
|
||||
if (waitpid(child, &status, 0) != child || !WIFEXITED(status) ||
|
||||
WEXITSTATUS(status) != expectedExit)
|
||||
fail("child exit result");
|
||||
}
|
||||
|
||||
// Verifies spawn-time signal masks and default-disposition reset policy.
|
||||
static void test_spawn_signal_attributes(const char* self) {
|
||||
struct sigaction ignore;
|
||||
ignore.sa_handler = SIG_IGN;
|
||||
ignore.sa_mask = 0;
|
||||
ignore.sa_flags = 0;
|
||||
if (sigaction(SIGUSR2, &ignore, (struct sigaction*)0) != 0)
|
||||
fail("install inherited ignore");
|
||||
|
||||
sigset_t mask = 0;
|
||||
sigset_t defaults = 0;
|
||||
if (sigemptyset(&mask) != 0 || sigaddset(&mask, SIGUSR1) != 0 ||
|
||||
sigemptyset(&defaults) != 0 || sigaddset(&defaults, SIGUSR2) != 0)
|
||||
fail("spawn signal sets");
|
||||
|
||||
posix_spawnattr_t attributes;
|
||||
if (posix_spawnattr_init(&attributes) != 0 ||
|
||||
posix_spawnattr_setsigmask(&attributes, &mask) != 0 ||
|
||||
posix_spawnattr_setsigdefault(&attributes, &defaults) != 0 ||
|
||||
posix_spawnattr_setflags(&attributes, POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF) != 0)
|
||||
fail("spawn signal attributes");
|
||||
sigset_t check = 0;
|
||||
if (posix_spawnattr_getsigmask(&attributes, &check) != 0 || check != mask ||
|
||||
posix_spawnattr_getsigdefault(&attributes, &check) != 0 || check != defaults)
|
||||
fail("spawn signal attribute getters");
|
||||
|
||||
char* argv[] = { (char*)self, "--p6-spawn-attrs", (char*)0 };
|
||||
pid_t child = (pid_t)-1;
|
||||
if (posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
|
||||
&attributes, argv, (char* const*)0) != 0 || child <= 0)
|
||||
fail("spawn signal child");
|
||||
if (posix_spawnattr_destroy(&attributes) != 0)
|
||||
fail("spawn attribute destroy");
|
||||
wait_for_child(child, 0);
|
||||
|
||||
struct sigaction reset;
|
||||
reset.sa_handler = SIG_DFL;
|
||||
reset.sa_mask = 0;
|
||||
reset.sa_flags = 0;
|
||||
if (sigaction(SIGUSR2, &reset, (struct sigaction*)0) != 0)
|
||||
fail("restore SIGUSR2 default");
|
||||
}
|
||||
|
||||
// Verifies actual process-group routing to a child in a newly-created group.
|
||||
static void test_process_group_delivery(const char* self) {
|
||||
int descriptors[2];
|
||||
if (pipe(descriptors) != 0)
|
||||
fail("group pipe");
|
||||
move_fd(descriptors[0], GROUP_READ_FD);
|
||||
move_fd(descriptors[1], GROUP_WRITE_FD);
|
||||
if (fcntl(GROUP_READ_FD, F_SETFD, FD_CLOEXEC) != 0)
|
||||
fail("group read CLOEXEC");
|
||||
|
||||
posix_spawnattr_t attributes;
|
||||
if (posix_spawnattr_init(&attributes) != 0 ||
|
||||
posix_spawnattr_setpgroup(&attributes, 0) != 0 ||
|
||||
posix_spawnattr_setflags(&attributes, POSIX_SPAWN_SETPGROUP) != 0)
|
||||
fail("group spawn attributes");
|
||||
char* argv[] = { (char*)self, "--p6-group-child", (char*)0 };
|
||||
pid_t child = (pid_t)-1;
|
||||
if (posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
|
||||
&attributes, argv, (char* const*)0) != 0 || child <= 0)
|
||||
fail("group child spawn");
|
||||
if (posix_spawnattr_destroy(&attributes) != 0)
|
||||
fail("group spawn attribute destroy");
|
||||
if (close(GROUP_WRITE_FD) != 0)
|
||||
fail("group parent write close");
|
||||
|
||||
char ready = 0;
|
||||
if (read(GROUP_READ_FD, &ready, 1) != 1 || ready != 'R')
|
||||
fail("group child readiness");
|
||||
if (close(GROUP_READ_FD) != 0)
|
||||
fail("group parent read close");
|
||||
if (kill(-child, SIGUSR1) != 0)
|
||||
fail("process-group signal delivery");
|
||||
wait_for_child(child, 61);
|
||||
}
|
||||
|
||||
// Verifies an ordinary default disposition terminates the target process.
|
||||
static void test_default_disposition(const char* self) {
|
||||
char* argv[] = { (char*)self, "--p6-default", (char*)0 };
|
||||
pid_t child = (pid_t)-1;
|
||||
if (posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
|
||||
(const posix_spawnattr_t*)0, argv, (char* const*)0) != 0 || child <= 0)
|
||||
fail("default-disposition child spawn");
|
||||
if (kill(child, SIGTERM) != 0)
|
||||
fail("default-disposition signal");
|
||||
wait_for_child(child, 128 + SIGTERM);
|
||||
}
|
||||
|
||||
// Verifies selected native exceptions enter the POSIX signal adapter.
|
||||
static void test_exception_translation(const char* self) {
|
||||
char* argv[] = { (char*)self, "--p6-exception", (char*)0 };
|
||||
pid_t child = (pid_t)-1;
|
||||
if (posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
|
||||
(const posix_spawnattr_t*)0, argv, (char* const*)0) != 0 || child <= 0)
|
||||
fail("exception child spawn");
|
||||
wait_for_child(child, 62);
|
||||
}
|
||||
|
||||
// Verifies alarm-backed waits and EINTR behavior for a blocked pipe read.
|
||||
static void test_signal_waits(void) {
|
||||
struct sigaction action;
|
||||
action.sa_handler = alarm_handler;
|
||||
action.sa_mask = 0;
|
||||
action.sa_flags = 0;
|
||||
if (sigaction(SIGALRM, &action, (struct sigaction*)0) != 0)
|
||||
fail("install SIGALRM handler");
|
||||
|
||||
if (alarm(3) != 0)
|
||||
fail("initial alarm remaining");
|
||||
unsigned int remaining = alarm(0);
|
||||
if (remaining < 2u || remaining > 3u)
|
||||
fail("alarm replacement remaining");
|
||||
|
||||
sigset_t alarmSet = 0;
|
||||
sigset_t previous = 0;
|
||||
if (sigemptyset(&alarmSet) != 0 || sigaddset(&alarmSet, SIGALRM) != 0 ||
|
||||
sigprocmask(SIG_BLOCK, &alarmSet, &previous) != 0)
|
||||
fail("block SIGALRM");
|
||||
sigset_t temporary = previous;
|
||||
if (sigdelset(&temporary, SIGALRM) != 0)
|
||||
fail("sigsuspend mask");
|
||||
sig_atomic_t before = gAlarmCount;
|
||||
(void)alarm(1);
|
||||
errno = 0;
|
||||
if (sigsuspend(&temporary) != -1 || errno != EINTR || gAlarmCount != before + 1)
|
||||
fail("sigsuspend signal wake");
|
||||
sigset_t current = 0;
|
||||
if (sigprocmask(SIG_SETMASK, (const sigset_t*)0, ¤t) != 0 ||
|
||||
sigismember(¤t, SIGALRM) != 1)
|
||||
fail("sigsuspend mask restoration");
|
||||
if (sigprocmask(SIG_SETMASK, &previous, (sigset_t*)0) != 0)
|
||||
fail("restore SIGALRM mask");
|
||||
|
||||
before = gAlarmCount;
|
||||
(void)alarm(1);
|
||||
errno = 0;
|
||||
if (pause() != -1 || errno != EINTR || gAlarmCount != before + 1)
|
||||
fail("pause signal wake");
|
||||
|
||||
before = gAlarmCount;
|
||||
(void)alarm(1);
|
||||
remaining = sleep(3);
|
||||
if (gAlarmCount != before + 1 || remaining < 1u || remaining > 2u)
|
||||
fail("sleep interruption");
|
||||
|
||||
int descriptors[2];
|
||||
if (pipe(descriptors) != 0)
|
||||
fail("EINTR pipe create");
|
||||
before = gAlarmCount;
|
||||
(void)alarm(1);
|
||||
char byte = 0;
|
||||
errno = 0;
|
||||
if (read(descriptors[0], &byte, 1) != -1 || errno != EINTR || gAlarmCount != before + 1)
|
||||
fail("blocked pipe read EINTR");
|
||||
(void)alarm(0);
|
||||
if (close(descriptors[0]) != 0 || close(descriptors[1]) != 0)
|
||||
fail("EINTR pipe cleanup");
|
||||
|
||||
action.sa_handler = pipe_handler;
|
||||
action.sa_mask = 0;
|
||||
action.sa_flags = 0;
|
||||
if (sigaction(SIGPIPE, &action, (struct sigaction*)0) != 0)
|
||||
fail("install SIGPIPE handler");
|
||||
if (pipe(descriptors) != 0 || close(descriptors[0]) != 0)
|
||||
fail("SIGPIPE pipe setup");
|
||||
sig_atomic_t pipeBefore = gPipeCount;
|
||||
errno = 0;
|
||||
if (write(descriptors[1], "P", 1) != -1 || errno != EPIPE ||
|
||||
gPipeCount != pipeBefore + 1)
|
||||
fail("SIGPIPE broken-pipe delivery");
|
||||
if (close(descriptors[1]) != 0)
|
||||
fail("SIGPIPE pipe cleanup");
|
||||
}
|
||||
|
||||
// Exercises the complete P6 signal contract used by later exec/vfork stages.
|
||||
int main(void) {
|
||||
if (child_mode("--p6-spawn-attrs")) run_spawn_attribute_child();
|
||||
if (child_mode("--p6-group-child")) run_group_child();
|
||||
if (child_mode("--p6-default")) run_default_child();
|
||||
if (child_mode("--p6-exception")) run_exception_child();
|
||||
|
||||
print("posix_signal_smoke: ");
|
||||
|
||||
sigset_t set;
|
||||
if (sigemptyset(&set) != 0 || set != 0) fail("sigemptyset");
|
||||
if (sigaddset(&set, SIGUSR1) != 0 || sigismember(&set, SIGUSR1) != 1)
|
||||
fail("sigaddset/sigismember");
|
||||
if (sigdelset(&set, SIGUSR1) != 0 || sigismember(&set, SIGUSR1) != 0)
|
||||
fail("sigdelset");
|
||||
if (sigfillset(&set) != 0 || sigismember(&set, SIGKILL) != 1)
|
||||
fail("sigfillset");
|
||||
errno = 0;
|
||||
if (sigaddset(&set, 0) != -1 || errno != EINVAL) fail("invalid signal set member");
|
||||
|
||||
struct sigaction action;
|
||||
action.sa_handler = signal_handler;
|
||||
action.sa_mask = 0;
|
||||
action.sa_flags = 0;
|
||||
if (sigaction(SIGUSR1, &action, (struct sigaction*)0) != 0)
|
||||
fail("install handler");
|
||||
|
||||
if (raise(SIGUSR1) != 0 || gHandlerCount != 1 || gHandlerTid != SysGetCurrentTid())
|
||||
fail("raise delivery");
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &set, (sigset_t*)0) != 0) fail("block signal");
|
||||
if (raise(SIGUSR1) != 0 || gHandlerCount != 1) fail("blocked raise delivered");
|
||||
sigset_t pending = 0;
|
||||
if (sigpending(&pending) != 0 || sigismember(&pending, SIGUSR1) != 1)
|
||||
fail("thread pending signal");
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, (sigset_t*)0) != 0 || gHandlerCount != 2)
|
||||
fail("pending delivery after unblock");
|
||||
if (sigpending(&pending) != 0 || sigismember(&pending, SIGUSR1) != 0)
|
||||
fail("pending signal not cleared");
|
||||
|
||||
struct sigaction nested;
|
||||
nested.sa_handler = nested_handler;
|
||||
nested.sa_mask = 0;
|
||||
nested.sa_flags = 0;
|
||||
if (sigaction(SIGUSR1, &nested, (struct sigaction*)0) != 0 ||
|
||||
raise(SIGUSR1) != 0 || gNestedCount != 2)
|
||||
fail("nested pending delivery");
|
||||
if (sigaction(SIGUSR1, &action, (struct sigaction*)0) != 0)
|
||||
fail("restore handler after nested delivery");
|
||||
|
||||
struct sigaction ignore;
|
||||
ignore.sa_handler = SIG_IGN;
|
||||
ignore.sa_mask = 0;
|
||||
ignore.sa_flags = 0;
|
||||
if (sigaction(SIGUSR1, &ignore, (struct sigaction*)0) != 0) fail("ignore disposition");
|
||||
if (kill(getpid(), SIGUSR1) != 0) fail("ignored kill");
|
||||
for (uint32_t i = 0; i < 32u; ++i) (void)SysYield();
|
||||
if (gHandlerCount != 2) fail("ignored signal reached handler");
|
||||
if (sigaction(SIGUSR1, &action, (struct sigaction*)0) != 0) fail("restore handler");
|
||||
|
||||
if (sigprocmask(SIG_BLOCK, &set, (sigset_t*)0) != 0) fail("block main for routing");
|
||||
pthread_t worker;
|
||||
if (pthread_create(&worker, (const pthread_attr_t*)0, signal_worker, (void*)0) != 0)
|
||||
fail("pthread_create");
|
||||
while (!gWorkerReady) (void)SysYield();
|
||||
if (!gWorkerInheritedBlock) fail("pthread signal-mask inheritance");
|
||||
|
||||
if (kill(getpid(), SIGUSR1) != 0) fail("process-directed kill");
|
||||
for (uint32_t i = 0; i < 10000u && gHandlerCount < 3; ++i) (void)SysYield();
|
||||
if (gHandlerCount != 3 || gHandlerTid != worker.Tid)
|
||||
fail("process-directed target selection");
|
||||
gWorkerStop = 1;
|
||||
void* workerResult = (void*)0;
|
||||
if (pthread_join(worker, &workerResult) != 0 || workerResult != (void*)0)
|
||||
fail("pthread_join");
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, (sigset_t*)0) != 0) fail("unblock main");
|
||||
|
||||
if (kill(0, 0) != 0) fail("current process-group existence");
|
||||
pid_t group = getpgrp();
|
||||
if (group <= 0 || kill(-group, 0) != 0) fail("explicit process-group existence");
|
||||
errno = 0;
|
||||
if (kill((pid_t)0x7fffffff, 0) != -1 || errno != ESRCH)
|
||||
fail("missing process ESRCH");
|
||||
|
||||
struct sigaction killAction;
|
||||
killAction.sa_handler = signal_handler;
|
||||
killAction.sa_mask = 0;
|
||||
killAction.sa_flags = 0;
|
||||
errno = 0;
|
||||
if (sigaction(SIGKILL, &killAction, (struct sigaction*)0) != -1 || errno != EINVAL)
|
||||
fail("SIGKILL catch accepted");
|
||||
sigset_t killSet;
|
||||
sigemptyset(&killSet);
|
||||
sigaddset(&killSet, SIGKILL);
|
||||
if (sigprocmask(SIG_BLOCK, &killSet, &pending) != 0) fail("SIGKILL block request");
|
||||
if (sigprocmask(SIG_SETMASK, &pending, (sigset_t*)0) != 0) fail("restore signal mask");
|
||||
|
||||
char self[512];
|
||||
get_self_path(self, sizeof(self));
|
||||
test_spawn_signal_attributes(self);
|
||||
test_process_group_delivery(self);
|
||||
test_default_disposition(self);
|
||||
test_exception_translation(self);
|
||||
test_signal_waits();
|
||||
|
||||
print("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user