feat: process exit codes, posix_spawn, 4 KiB command lines
Groundwork for the GCC driver: a compiler driver must spawn cc1/as/ld and know whether each stage succeeded. Kernel: the scheduler keeps an exit-code ledger (pid -> code; pids are monotonic so entries never alias), published during teardown right before waiters wake. SYS_EXIT records main()'s return value, SYS_KILL records 256+SIGKILL, and the exception handler records 256+signal mapped from the fault vector (#PF/#GP -> SIGSEGV, #DE/FP -> SIGFPE, #UD -> SIGILL). SYS_WAITPID now returns the code: 0..255 for a normal exit, 256+signal for a violent death. Process args grow from 256 bytes to 4 KiB (cc1 invocations do not fit in 256), with crt1 now parsing up to 255 argv entries from a static buffer. libc: new spawn.h with posix_spawn/posix_spawnp over SYS_SPAWN - libiberty's pex layer has a posix_spawn backend, so GCC's driver works without fork. argv is joined into the kernel args string (spaces in arguments rejected; no kernel quoting), envp is not transferred, and non-empty file actions fail loudly with ENOTSUP until the kernel can redirect stdio on spawn. waitpid() now decodes real POSIX status and the sys/wait.h macros distinguish exited from signaled children. Shell: prints [exit code N] after nonzero exits and [terminated by signal N] for killed or crashed children. Verified on the OS: cat on a missing file reports exit code 1; a window-close (clean exit 0) stays silent as it should. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
+157
-4
@@ -21,6 +21,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <utime.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -3404,17 +3405,21 @@ int dup2(int oldfd, int newfd) {
|
||||
return newfd;
|
||||
}
|
||||
|
||||
/* SYS_WAITPID blocks until the child exits but does not report its
|
||||
exit code yet, so successful waits decode as exit status 0. */
|
||||
/* SYS_WAITPID blocks until the child exits and returns 0..255 for a
|
||||
normal exit or 256+signal for a killed/crashed child. */
|
||||
pid_t waitpid(pid_t pid, int *status, int options) {
|
||||
(void)options;
|
||||
if (pid <= 0) {
|
||||
errno = ECHILD; /* no process groups / wait-any support */
|
||||
return -1;
|
||||
}
|
||||
_zos_syscall1(SYS_WAITPID, (long)pid);
|
||||
long code = _zos_syscall1(SYS_WAITPID, (long)pid);
|
||||
if (status != NULL) {
|
||||
*status = 0;
|
||||
if (code >= 256) {
|
||||
*status = (int)((code - 256) & 0x7F); /* signaled */
|
||||
} else {
|
||||
*status = (int)((code & 0xFF) << 8); /* exited */
|
||||
}
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
@@ -3448,6 +3453,154 @@ long pathconf(const char *path, int name) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================
|
||||
posix_spawn (spawn.h) over SYS_SPAWN
|
||||
======================================================================== */
|
||||
|
||||
int posix_spawnattr_init(posix_spawnattr_t *attr) {
|
||||
if (attr == NULL) return EINVAL;
|
||||
attr->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawnattr_destroy(posix_spawnattr_t *attr) {
|
||||
(void)attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
||||
if (attr == NULL) return EINVAL;
|
||||
attr->flags = flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
|
||||
if (attr == NULL || flags == NULL) return EINVAL;
|
||||
*flags = attr->flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *actions) {
|
||||
if (actions == NULL) return EINVAL;
|
||||
actions->action_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *actions) {
|
||||
(void)actions;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Actions are counted but cannot be honored yet (no kernel support for
|
||||
stdio redirection on spawn); posix_spawn refuses non-empty sets. */
|
||||
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *actions,
|
||||
int fd, int newfd) {
|
||||
(void)fd; (void)newfd;
|
||||
if (actions == NULL) return EINVAL;
|
||||
actions->action_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *actions,
|
||||
int fd) {
|
||||
(void)fd;
|
||||
if (actions == NULL) return EINVAL;
|
||||
actions->action_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *actions,
|
||||
int fd, const char *path, int oflag,
|
||||
mode_t mode) {
|
||||
(void)fd; (void)path; (void)oflag; (void)mode;
|
||||
if (actions == NULL) return EINVAL;
|
||||
actions->action_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawn(pid_t *pid, const char *path,
|
||||
const posix_spawn_file_actions_t *actions,
|
||||
const posix_spawnattr_t *attr,
|
||||
char *const argv[], char *const envp[]) {
|
||||
(void)attr;
|
||||
(void)envp; /* no environment transfer on Montauk spawn */
|
||||
|
||||
if (path == NULL) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (actions != NULL && actions->action_count > 0) {
|
||||
return ENOTSUP; /* redirection needs kernel support */
|
||||
}
|
||||
|
||||
/* Join argv[1..] into the kernel's single args string. The kernel
|
||||
tokenizer has no quoting, so embedded spaces cannot round-trip. */
|
||||
static char argsbuf[4096];
|
||||
size_t o = 0;
|
||||
argsbuf[0] = '\0';
|
||||
if (argv != NULL) {
|
||||
for (int i = 1; argv[i] != NULL; i++) {
|
||||
size_t alen = strlen(argv[i]);
|
||||
for (size_t k = 0; k < alen; k++) {
|
||||
if (argv[i][k] == ' ') {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
if (o + alen + 2 >= sizeof(argsbuf)) {
|
||||
return E2BIG;
|
||||
}
|
||||
if (o > 0) {
|
||||
argsbuf[o++] = ' ';
|
||||
}
|
||||
memcpy(argsbuf + o, argv[i], alen);
|
||||
o += alen;
|
||||
}
|
||||
}
|
||||
argsbuf[o] = '\0';
|
||||
|
||||
long child = _zos_syscall2(SYS_SPAWN, (long)path, (long)argsbuf);
|
||||
if (child < 0) {
|
||||
return ENOENT;
|
||||
}
|
||||
if (pid != NULL) {
|
||||
*pid = (pid_t)child;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_spawnp(pid_t *pid, const char *file,
|
||||
const posix_spawn_file_actions_t *actions,
|
||||
const posix_spawnattr_t *attr,
|
||||
char *const argv[], char *const envp[]) {
|
||||
if (file == NULL) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int has_slash = 0;
|
||||
for (const char *p = file; *p; p++) {
|
||||
if (*p == '/') {
|
||||
has_slash = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_slash) {
|
||||
return posix_spawn(pid, file, actions, attr, argv, envp);
|
||||
}
|
||||
|
||||
/* Bare name: search the SDK and system tool directories. */
|
||||
static const char *prefixes[] = { "/sdk/bin/", "/os/", "" };
|
||||
char candidate[512];
|
||||
for (int pi = 0; pi < 3; pi++) {
|
||||
for (int ext = 0; ext < 2; ext++) {
|
||||
snprintf(candidate, sizeof(candidate), "%s%s%s",
|
||||
prefixes[pi], file, ext ? "" : ".elf");
|
||||
if (access(candidate, F_OK) == 0) {
|
||||
return posix_spawn(pid, candidate, actions, attr, argv, envp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
/* The libc environment lives in name/value slots (getenv/setenv), not
|
||||
NAME=VALUE strings, and exec* is unimplemented, so nothing can
|
||||
consume a populated environ yet. An empty, valid vector satisfies
|
||||
|
||||
Reference in New Issue
Block a user