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:
maksw committed 2026-09-23 09:39:42 +02:00
1 parent 73abd6f90d
commit 899225f551
57 files changed
+8817 -1216

No files matched your search

@@ -0,0 +1,555 @@
// User-mode smoke test for server-owned POSIX spawn and child lifecycle semantics.
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <spawn.h>
#include <signal.h>
#include <sys/wait.h>
#include <ntlite/syscall.h>
#include <ntlite/posix_extensions.h>
#define CONTROL_READ_FD 40
#define ACTION_OPEN_FD 41
#define ACTION_DUP_FD 42
#define CONTROL_WRITE_FD 43
#define CONTROL_WRITE_A_FD 44
#define EXEC_PARENT_READ_FD 45
#define EXEC_CHILD_WRITE_FD 46
#define EXEC_CHILD_READ_FD 47
#define EXEC_PARENT_WRITE_FD 48
#define EXEC_RETAIN_FD 49
#define EXEC_CLOEXEC_FD 50
static const char* gExecFile = "posix_process_exec.tmp";
static volatile uint32_t gVforkChildTouched;
// Writes one NUL-terminated diagnostic to stdout.
static void print(const char* text) {
(void)write(1, text, strlen(text));
}
// Terminates the smoke test with one diagnostic.
static void fail(const char* message) {
print("posix_process_smoke: ");
print(message);
print("\n");
exit(1);
}
// Returns whether the command-line compatibility view contains one child marker.
static int child_mode(const char* marker) {
const char* command = posix_get_command_line();
return command && strstr(command, marker) != (char*)0;
}
// 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");
}
// Builds one inherited control pipe with a close-on-spawn parent write endpoint.
static void make_control_pipe(int writeFd) {
int pipefd[2];
if (pipe(pipefd) != 0)
fail("control pipe failed");
move_fd(pipefd[0], CONTROL_READ_FD);
move_fd(pipefd[1], writeFd);
if (fcntl(writeFd, F_SETFD, FD_CLOEXEC) != 0)
fail("control CLOEXEC failed");
}
// 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'))
fail("could not resolve self path");
}
// Converts one positive 32-bit value to decimal without depending on stdio.
static void format_u32(uint32_t value, char* text, size_t capacity) {
char reverse[16];
size_t count = 0;
if (!text || capacity == 0) return;
do {
reverse[count++] = (char)('0' + value % 10u);
value /= 10u;
} while (value && count < sizeof(reverse));
if (count + 1u > capacity) fail("numeric format overflow");
for (size_t i = 0; i < count; ++i) text[i] = reverse[count - i - 1u];
text[count] = '\0';
}
// Parses one nonzero decimal 32-bit value used by exec smoke stages.
static uint32_t parse_u32(const char* text) {
if (!text || !text[0]) return 0;
uint32_t value = 0;
for (size_t i = 0; text[i]; ++i) {
if (text[i] < '0' || text[i] > '9') return 0;
uint32_t digit = (uint32_t)(text[i] - '0');
if (value > (UINT32_MAX - digit) / 10u) return 0;
value = value * 10u + digit;
}
return value;
}
// Reports one exec-stage failure before preserving its distinct exit code.
static void exec_stage_fail(uint32_t code, const char* stage) {
char codeText[16];
format_u32(code, codeText, sizeof(codeText));
print("posix_process_smoke: exec stage failed: ");
print(stage);
print(" exit=");
print(codeText);
print("\n");
_exit((int)code);
}
// Exists only so exec can prove caught dispositions reset to default.
static void exec_caught_handler(int signalNumber) {
(void)signalNumber;
}
// First child stage captures state and replaces its native generation.
static void run_exec_stage1(const char* self) {
pid_t stablePid = getpid();
uint32_t nativePid = SysGetCurrentPid();
if (stablePid <= 0 || nativePid == 0) _exit(120);
int retained = open(gExecFile, O_CREAT | O_TRUNC | O_RDWR, 0666);
if (retained < 0) _exit(121);
move_fd(retained, EXEC_RETAIN_FD);
int cloexec = dup(EXEC_RETAIN_FD);
if (cloexec < 0) _exit(122);
move_fd(cloexec, EXEC_CLOEXEC_FD);
if (fcntl(EXEC_CLOEXEC_FD, F_SETFD, FD_CLOEXEC) != 0) _exit(123);
struct sigaction ignored;
memset(&ignored, 0, sizeof(ignored));
ignored.sa_handler = SIG_IGN;
if (sigaction(SIGUSR1, &ignored, (struct sigaction*)0) != 0) _exit(124);
struct sigaction caught;
memset(&caught, 0, sizeof(caught));
caught.sa_handler = exec_caught_handler;
if (sigaction(SIGUSR2, &caught, (struct sigaction*)0) != 0) _exit(125);
sigset_t blocked;
if (sigemptyset(&blocked) != 0 || sigaddset(&blocked, SIGTERM) != 0 ||
sigprocmask(SIG_BLOCK, &blocked, (sigset_t*)0) != 0)
_exit(126);
char stableText[16];
char nativeText[16];
format_u32((uint32_t)stablePid, stableText, sizeof(stableText));
format_u32(nativePid, nativeText, sizeof(nativeText));
char* nextArgv[] = { (char*)self, "--p7-exec2", stableText, nativeText, (char*)0 };
char* nextEnv[] = { "P7_ENV=replaced", (char*)0 };
(void)execve(self, nextArgv, nextEnv);
{
char errnoText[16];
format_u32((uint32_t)errno, errnoText, sizeof(errnoText));
print("posix_process_smoke: execve stage1 returned errno=");
print(errnoText);
print("\n");
_exit(127);
}
}
// Second child stage validates one committed replacement and performs a repeated exec.
static void run_exec_stage2(int argc, char** argv) {
if (argc != 4) exec_stage_fail(128, "argv");
uint32_t stablePid = parse_u32(argv[2]);
uint32_t oldNativePid = parse_u32(argv[3]);
uint32_t nativePid = SysGetCurrentPid();
if (stablePid == 0 || oldNativePid == 0 || (uint32_t)getpid() != stablePid ||
nativePid == 0 || nativePid == oldNativePid)
exec_stage_fail(129, "stable/native pid");
const char* marker = getenv("P7_ENV");
if (!marker || strcmp(marker, "replaced") != 0) exec_stage_fail(130, "environment");
if (fcntl(EXEC_RETAIN_FD, F_GETFD) < 0) exec_stage_fail(131, "retained fd");
errno = 0;
if (fcntl(EXEC_CLOEXEC_FD, F_GETFD) != -1 || errno != EBADF)
exec_stage_fail(132, "cloexec fd");
struct sigaction action;
memset(&action, 0, sizeof(action));
if (sigaction(SIGUSR1, (const struct sigaction*)0, &action) != 0 ||
action.sa_handler != SIG_IGN)
exec_stage_fail(133, "ignored disposition");
if (sigaction(SIGUSR2, (const struct sigaction*)0, &action) != 0 ||
action.sa_handler != SIG_DFL)
exec_stage_fail(134, "caught disposition reset");
sigset_t blocked = 0;
if (sigprocmask(SIG_BLOCK, (const sigset_t*)0, &blocked) != 0 ||
sigismember(&blocked, SIGTERM) != 1)
exec_stage_fail(135, "blocked mask");
if (write(EXEC_CHILD_WRITE_FD, "E", 1) != 1) exec_stage_fail(136, "marker write");
char release = 0;
if (read(EXEC_CHILD_READ_FD, &release, 1) != 1 || release != 'R') _exit(137);
char stableText[16];
char nativeText[16];
format_u32(stablePid, stableText, sizeof(stableText));
format_u32(nativePid, nativeText, sizeof(nativeText));
char self[512];
get_self_path(self, sizeof(self));
char* nextArgv[] = { self, "--p7-exec3", stableText, nativeText, (char*)0 };
char* nextEnv[] = { "P7_ENV=second", (char*)0 };
(void)execve(self, nextArgv, nextEnv);
_exit(138);
}
// Final child stage proves repeated native generations retain the same POSIX PID.
static void run_exec_stage3(int argc, char** argv) {
if (argc != 4) _exit(139);
uint32_t stablePid = parse_u32(argv[2]);
uint32_t oldNativePid = parse_u32(argv[3]);
uint32_t nativePid = SysGetCurrentPid();
if (stablePid == 0 || oldNativePid == 0 || (uint32_t)getpid() != stablePid ||
nativePid == 0 || nativePid == oldNativePid)
_exit(140);
const char* marker = getenv("P7_ENV");
if (!marker || strcmp(marker, "second") != 0 || fcntl(EXEC_RETAIN_FD, F_GETFD) < 0)
_exit(141);
if (write(EXEC_CHILD_WRITE_FD, "D", 1) != 1) _exit(142);
(void)close(EXEC_RETAIN_FD);
(void)close(EXEC_CHILD_WRITE_FD);
(void)close(EXEC_CHILD_READ_FD);
(void)unlink(gExecFile);
_exit(47);
}
// Executes with Linux-style null argv/envp. The replacement retains the spawned child parent association.
static void run_null_exec_stage(const char* self) {
(void)execve(self, (char* const*)0, (char* const*)0);
_exit(143);
}
// Exercises replacement, rollback, wait isolation, repeated generations, and null vectors.
static void run_exec_parent(const char* self) {
pid_t beforePid = getpid();
uint32_t beforeNativePid = SysGetCurrentPid();
char* missingArgv[] = { "/system/p7_missing_image.exe", (char*)0 };
errno = 0;
if (execve(missingArgv[0], missingArgv, (char* const*)0) != -1 ||
getpid() != beforePid || SysGetCurrentPid() != beforeNativePid)
fail("failed exec did not preserve old image");
int childToParent[2];
int parentToChild[2];
if (pipe(childToParent) != 0 || pipe(parentToChild) != 0)
fail("exec control pipe failed");
move_fd(childToParent[0], EXEC_PARENT_READ_FD);
move_fd(childToParent[1], EXEC_CHILD_WRITE_FD);
move_fd(parentToChild[0], EXEC_CHILD_READ_FD);
move_fd(parentToChild[1], EXEC_PARENT_WRITE_FD);
posix_spawn_file_actions_t actions;
if (posix_spawn_file_actions_init(&actions) != 0 ||
posix_spawn_file_actions_addclose(&actions, EXEC_PARENT_READ_FD) != 0 ||
posix_spawn_file_actions_addclose(&actions, EXEC_PARENT_WRITE_FD) != 0)
fail("exec spawn actions failed");
char* childArgv[] = { (char*)self, "--p7-exec1", (char*)0 };
pid_t child = (pid_t)-1;
int error = posix_spawn(&child, self, &actions, (const posix_spawnattr_t*)0,
childArgv, (char* const*)0);
if (error != 0 || child <= 0) fail("exec child spawn failed");
if (posix_spawn_file_actions_destroy(&actions) != 0) fail("exec actions destroy failed");
if (close(EXEC_CHILD_WRITE_FD) != 0 || close(EXEC_CHILD_READ_FD) != 0)
fail("exec parent child-end close failed");
char marker = 0;
if (read(EXEC_PARENT_READ_FD, &marker, 1) != 1 || marker != 'E') {
int earlyStatus = -1;
pid_t early = waitpid(child, &earlyStatus, WNOHANG);
if (early == child && WIFEXITED(earlyStatus)) {
char exitText[16];
format_u32((uint32_t)WEXITSTATUS(earlyStatus), exitText, sizeof(exitText));
print("posix_process_smoke: exec child exited before marker exit=");
print(exitText);
print("\n");
} else if (early == 0) {
print("posix_process_smoke: exec child still running after marker channel closed\n");
} else {
print("posix_process_smoke: exec child wait diagnostic unavailable\n");
}
fail("exec replacement marker missing");
}
int childStatus = -1;
if (waitpid(child, &childStatus, WNOHANG) != 0)
fail("exec retirement became waitable");
if (write(EXEC_PARENT_WRITE_FD, "R", 1) != 1)
fail("exec replacement release failed");
marker = 0;
if (read(EXEC_PARENT_READ_FD, &marker, 1) != 1 || marker != 'D')
fail("repeated exec marker missing");
if (close(EXEC_PARENT_READ_FD) != 0 || close(EXEC_PARENT_WRITE_FD) != 0)
fail("exec parent pipe close failed");
if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus) ||
WEXITSTATUS(childStatus) != 47)
fail("repeated exec child wait failed");
char* nullArgv[] = { (char*)self, "--p7-null", (char*)0 };
child = (pid_t)-1;
error = posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
(const posix_spawnattr_t*)0, nullArgv, (char* const*)0);
if (error != 0 || child <= 0) fail("null-vector child spawn failed");
childStatus = -1;
if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus) ||
WEXITSTATUS(childStatus) != 48)
fail("null argv/env exec failed");
}
// Validates the independent native generation created by vfork()+execve().
static void run_vfork_exec_stage(int argc, char** argv) {
if (argc != 4) _exit(150);
uint32_t stablePid = parse_u32(argv[2]);
uint32_t parentNativePid = parse_u32(argv[3]);
if (stablePid == 0 || parentNativePid == 0 || (uint32_t)getpid() != stablePid ||
SysGetCurrentPid() == 0 || SysGetCurrentPid() == parentNativePid)
_exit(151);
_exit(52);
}
// Exercises both terminal vfork paths and the child-zero/parent-PID continuation split.
static void run_vfork_parent(const char* self) {
gVforkChildTouched = 0;
pid_t child = vfork();
if (child < 0) fail("vfork _exit creation failed");
if (child == 0) {
gVforkChildTouched = 1u;
_exit(51);
}
if (!gVforkChildTouched) fail("vfork parent resumed before child terminal path");
int status = -1;
if (waitpid(child, &status, 0) != child || !WIFEXITED(status) || WEXITSTATUS(status) != 51)
fail("vfork _exit wait failed");
uint32_t parentNativePid = SysGetCurrentPid();
child = vfork();
if (child < 0) fail("vfork exec creation failed");
if (child == 0) {
char stableText[16];
char parentNativeText[16];
format_u32((uint32_t)getpid(), stableText, sizeof(stableText));
format_u32(parentNativePid, parentNativeText, sizeof(parentNativeText));
char* nextArgv[] = { (char*)self, "--p7-vfork-exec", stableText,
parentNativeText, (char*)0 };
(void)execve(self, nextArgv, (char* const*)0);
_exit(152);
}
status = -1;
if (waitpid(child, &status, 0) != child || !WIFEXITED(status) || WEXITSTATUS(status) != 52)
fail("vfork exec wait failed");
}
// Validates server-owned bootstrap state, then blocks until the parent releases the child.
static void run_full_child(void) {
pid_t pid = getpid();
pid_t parent = getppid();
if (pid <= 0 || parent <= 0 || parent == pid)
_exit(101);
if (getpgrp() != pid)
_exit(102);
const char* marker = getenv("P5_ENV");
if (!marker || strcmp(marker, "server-owned") != 0)
_exit(103);
const char* expectedCwd = getenv("P5_CWD");
char cwd[512];
if (!expectedCwd || !getcwd(cwd, sizeof(cwd)) || strcmp(cwd, expectedCwd) != 0)
_exit(104);
errno = 0;
if (fcntl(ACTION_OPEN_FD, F_GETFD) != -1 || errno != EBADF)
_exit(105);
if (write(ACTION_DUP_FD, "ok", 2) != 2)
_exit(106);
if (close(ACTION_DUP_FD) != 0)
_exit(107);
errno = 0;
if (fcntl(CONTROL_WRITE_FD, F_GETFD) != -1 || errno != EBADF)
_exit(108);
char release = 0;
if (read(CONTROL_READ_FD, &release, 1) != 1 || release != 'R')
_exit(109);
(void)close(CONTROL_READ_FD);
_exit(23);
}
// Blocks one child until the parent supplies the exit code byte.
static void run_wait_child(void) {
if (getppid() <= 0 || getppid() == getpid())
_exit(110);
char exitCode = 0;
if (read(CONTROL_READ_FD, &exitCode, 1) != 1)
_exit(111);
(void)close(CONTROL_READ_FD);
_exit((unsigned char)exitCode);
}
// Spawns one child that waits on CONTROL_READ_FD.
static pid_t spawn_wait_child(const char* self, int writeFd) {
make_control_pipe(writeFd);
char* argv[] = { (char*)self, "--p5-wait", (char*)0 };
pid_t child = (pid_t)-1;
int error = posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
(const posix_spawnattr_t*)0, argv, (char* const*)0);
if (error != 0 || child <= 0)
fail("wait child spawn failed");
if (close(CONTROL_READ_FD) != 0)
fail("parent control read close failed");
return child;
}
// Exercises the P5 server-owned process construction and wait contract.
int main(int argc, char** argv) {
if (argc == 0) {
pid_t parent = getppid();
if (parent < 0) _exit(145);
if (parent == 0) {
char self[512];
get_self_path(self, sizeof(self));
char* rootArgv[] = { self, "--p5-root", (char*)0 };
pid_t child = (pid_t)-1;
int error = posix_spawn(&child, self, (const posix_spawn_file_actions_t*)0,
(const posix_spawnattr_t*)0, rootArgv, environ);
if (error != 0 || child <= 0) _exit(146);
int childStatus = -1;
if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus)) _exit(147);
_exit(WEXITSTATUS(childStatus));
}
if (!environ || environ[0] != (char*)0) _exit(144);
_exit(48);
}
if (argc >= 2 && strcmp(argv[1], "--p7-exec1") == 0) run_exec_stage1(argv[0]);
if (argc >= 2 && strcmp(argv[1], "--p7-exec2") == 0) run_exec_stage2(argc, argv);
if (argc >= 2 && strcmp(argv[1], "--p7-exec3") == 0) run_exec_stage3(argc, argv);
if (argc >= 2 && strcmp(argv[1], "--p7-null") == 0) run_null_exec_stage(argv[0]);
if (argc >= 2 && strcmp(argv[1], "--p7-vfork-exec") == 0) run_vfork_exec_stage(argc, argv);
if (child_mode("--p5-full")) run_full_child();
if (child_mode("--p5-wait")) run_wait_child();
char self[512];
get_self_path(self, sizeof(self));
char cwd[512];
if (!getcwd(cwd, sizeof(cwd)))
fail("parent getcwd failed");
const char cwdPrefix[] = "P5_CWD=";
char cwdEnvironment[sizeof(cwdPrefix) + sizeof(cwd)];
size_t prefixLength = sizeof(cwdPrefix) - 1u;
size_t cwdLength = strlen(cwd);
if (prefixLength + cwdLength + 1u > sizeof(cwdEnvironment))
fail("cwd environment overflow");
memcpy(cwdEnvironment, cwdPrefix, prefixLength);
memcpy(cwdEnvironment + prefixLength, cwd, cwdLength + 1u);
const char* actionPath = "posix_process_spawn.tmp";
const char* collisionPath = "posix_process_collision.tmp";
(void)unlink(actionPath);
(void)unlink(collisionPath);
make_control_pipe(CONTROL_WRITE_FD);
// Seed the dup2 target with an unrelated inherited description so the child
// action must replace an already-occupied descriptor number.
int collision = open(collisionPath, O_CREAT | O_TRUNC | O_RDWR, 0666);
if (collision < 0)
fail("spawn collision source open failed");
move_fd(collision, ACTION_DUP_FD);
posix_spawn_file_actions_t actions;
if (posix_spawn_file_actions_init(&actions) != 0 ||
posix_spawn_file_actions_addopen(&actions, ACTION_OPEN_FD, actionPath,
O_CREAT | O_TRUNC | O_RDWR, 0666) != 0 ||
posix_spawn_file_actions_adddup2(&actions, ACTION_OPEN_FD, ACTION_DUP_FD) != 0 ||
posix_spawn_file_actions_addclose(&actions, ACTION_OPEN_FD) != 0)
fail("spawn file actions setup failed");
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("spawn attributes setup failed");
char* childArgv[] = { self, "--p5-full", (char*)0 };
char* childEnv[] = { "P5_ENV=server-owned", cwdEnvironment, (char*)0 };
pid_t child = (pid_t)-1;
int spawnError = posix_spawn(&child, self, &actions, &attributes, childArgv, childEnv);
if (spawnError != 0 || child <= 0)
fail("full child spawn failed");
if (posix_spawn_file_actions_destroy(&actions) != 0 ||
posix_spawnattr_destroy(&attributes) != 0)
fail("spawn setup destroy failed");
if (close(ACTION_DUP_FD) != 0)
fail("parent collision descriptor close failed");
if (unlink(collisionPath) != 0)
fail("parent collision source cleanup failed");
if (close(CONTROL_READ_FD) != 0)
fail("full parent control read close failed");
int childStatus = -1;
if (waitpid(child, &childStatus, WNOHANG) != 0)
fail("WNOHANG did not report running child");
if (write(CONTROL_WRITE_FD, "R", 1) != 1 || close(CONTROL_WRITE_FD) != 0)
fail("full child release failed");
if (waitpid(child, &childStatus, 0) != child || !WIFEXITED(childStatus) ||
WEXITSTATUS(childStatus) != 23)
fail("exact child wait failed");
int result = open(actionPath, O_RDONLY);
char actionBytes[3] = {0, 0, 0};
if (result < 0 || read(result, actionBytes, 2) != 2 || strcmp(actionBytes, "ok") != 0)
fail("spawn file action result was wrong");
if (close(result) != 0 || unlink(actionPath) != 0)
fail("spawn action cleanup failed");
errno = 0;
if (waitpid(child, &childStatus, WNOHANG) != (pid_t)-1 || errno != ECHILD)
fail("collected child remained waitable");
pid_t failedPid = 123;
if (posix_spawn(&failedPid, "/system/p5_missing_image.exe",
(const posix_spawn_file_actions_t*)0, (const posix_spawnattr_t*)0,
(char* const*)0, (char* const*)0) == 0 || failedPid != (pid_t)-1)
fail("failed construction returned a child");
pid_t first = spawn_wait_child(self, CONTROL_WRITE_A_FD);
pid_t second = spawn_wait_child(self, CONTROL_WRITE_FD);
char secondCode = 32;
if (write(CONTROL_WRITE_FD, &secondCode, 1) != 1 || close(CONTROL_WRITE_FD) != 0)
fail("second child release failed");
childStatus = -1;
if (wait(&childStatus) != second || WEXITSTATUS(childStatus) != 32)
fail("wait did not collect second child first");
char firstCode = 31;
if (write(CONTROL_WRITE_A_FD, &firstCode, 1) != 1 || close(CONTROL_WRITE_A_FD) != 0)
fail("first child release failed");
childStatus = -1;
if (wait(&childStatus) != first || WEXITSTATUS(childStatus) != 31)
fail("wait did not collect first child second");
errno = 0;
if (wait((int*)0) != (pid_t)-1 || errno != ECHILD)
fail("wait without children did not return ECHILD");
run_exec_parent(self);
run_vfork_parent(self);
print("posix_process_smoke: PASS\n");
return 0;
}