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:
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 13
|
#define MONTAUK_BUILD_NUMBER 15
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
namespace montauk::abi {
|
namespace montauk::abi {
|
||||||
static void Sys_Exit(int exitCode) {
|
static void Sys_Exit(int exitCode) {
|
||||||
(void)exitCode;
|
Sched::SetProcessExitCode(Sched::GetCurrentPid(), exitCode & 0xFF);
|
||||||
Sched::ExitProcess();
|
Sched::ExitProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,8 +36,11 @@ namespace montauk::abi {
|
|||||||
return Sched::GetCurrentPid();
|
return Sched::GetCurrentPid();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Sys_WaitPid(int pid) {
|
// Returns 0..255 for a normal exit, 256+signal for killed/crashed,
|
||||||
|
// 0 when the pid is unknown or its ledger entry has been evicted.
|
||||||
|
static int Sys_WaitPid(int pid) {
|
||||||
Sched::BlockOnPid(pid);
|
Sched::BlockOnPid(pid);
|
||||||
|
return Sched::LookupExitCode(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Sys_Spawn(const char* path, const char* args) {
|
static int Sys_Spawn(const char* path, const char* args) {
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ namespace montauk::abi {
|
|||||||
|
|
||||||
static constexpr uint64_t kMaxPrintableStringBytes = 4096;
|
static constexpr uint64_t kMaxPrintableStringBytes = 4096;
|
||||||
static constexpr uint64_t kMaxPathBytes = 256;
|
static constexpr uint64_t kMaxPathBytes = 256;
|
||||||
static constexpr uint64_t kMaxArgsBytes = 256;
|
// Large enough for compiler driver command lines (cc1/ld invocations).
|
||||||
|
static constexpr uint64_t kMaxArgsBytes = 4096;
|
||||||
static constexpr uint64_t kMaxWindowTitleBytes = 256;
|
static constexpr uint64_t kMaxWindowTitleBytes = 256;
|
||||||
static constexpr uint64_t kMaxHostnameBytes = 256;
|
static constexpr uint64_t kMaxHostnameBytes = 256;
|
||||||
static constexpr uint64_t kMaxUserNameBytes = 32;
|
static constexpr uint64_t kMaxUserNameBytes = 32;
|
||||||
@@ -140,8 +141,7 @@ namespace montauk::abi {
|
|||||||
return (int64_t)Sys_Spawn((const char*)frame->arg1,
|
return (int64_t)Sys_Spawn((const char*)frame->arg1,
|
||||||
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
|
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
|
||||||
case SYS_WAITPID:
|
case SYS_WAITPID:
|
||||||
Sys_WaitPid((int)frame->arg1);
|
return Sys_WaitPid((int)frame->arg1);
|
||||||
return 0;
|
|
||||||
case SYS_FBINFO:
|
case SYS_FBINFO:
|
||||||
if (!UserMemory::Writable<FbInfo>(frame->arg1)) return -1;
|
if (!UserMemory::Writable<FbInfo>(frame->arg1)) return -1;
|
||||||
Sys_FbInfo((FbInfo*)frame->arg1);
|
Sys_FbInfo((FbInfo*)frame->arg1);
|
||||||
|
|||||||
@@ -143,6 +143,16 @@ namespace Hal {
|
|||||||
CrashReport::AddReport(rep);
|
CrashReport::AddReport(rep);
|
||||||
Sched::SpawnCrashPad(proc->pid);
|
Sched::SpawnCrashPad(proc->pid);
|
||||||
|
|
||||||
|
// Record a killed-by-signal exit code (256+sig) so a parent
|
||||||
|
// blocked in SYS_WAITPID can tell a crash from a clean exit.
|
||||||
|
{
|
||||||
|
int sig = 11; /* SIGSEGV */
|
||||||
|
if (i == 0x00) sig = 8; /* #DE -> SIGFPE */
|
||||||
|
else if (i == 0x06) sig = 4; /* #UD -> SIGILL */
|
||||||
|
else if (i == 0x10 || i == 0x13) sig = 8; /* x87/SIMD FP */
|
||||||
|
Sched::SetProcessExitCode(proc->pid, 256 + sig);
|
||||||
|
}
|
||||||
|
|
||||||
Sched::ExitProcess();
|
Sched::ExitProcess();
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -43,6 +43,15 @@ namespace Sched {
|
|||||||
static Process processTable[MaxProcesses];
|
static Process processTable[MaxProcesses];
|
||||||
static int nextPid = 0;
|
static int nextPid = 0;
|
||||||
|
|
||||||
|
// Exit-code ledger. Pids are monotonic and never recycled, so entries
|
||||||
|
// cannot alias. Written under schedLock during process teardown; read
|
||||||
|
// by SYS_WAITPID after the waiter unblocks. Codes: 0..255 = normal
|
||||||
|
// exit status, 256+signal = killed or crashed.
|
||||||
|
static constexpr int ExitLedgerSize = 128;
|
||||||
|
static int exitLedgerPid[ExitLedgerSize];
|
||||||
|
static int exitLedgerCode[ExitLedgerSize];
|
||||||
|
static int exitLedgerHead = 0;
|
||||||
|
|
||||||
// The scheduler lock MUST be a Spinlock (interrupt-disabling).
|
// The scheduler lock MUST be a Spinlock (interrupt-disabling).
|
||||||
// It is held ACROSS context switches to prevent the race where
|
// It is held ACROSS context switches to prevent the race where
|
||||||
// another CPU picks up a process whose RSP hasn't been saved yet.
|
// another CPU picks up a process whose RSP hasn't been saved yet.
|
||||||
@@ -212,6 +221,11 @@ namespace Sched {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextPid = 0;
|
nextPid = 0;
|
||||||
|
for (int i = 0; i < ExitLedgerSize; i++) {
|
||||||
|
exitLedgerPid[i] = -1;
|
||||||
|
exitLedgerCode[i] = 0;
|
||||||
|
}
|
||||||
|
exitLedgerHead = 0;
|
||||||
Hal::RegisterIrqHandler(Hal::IRQ_RESCHEDULE, RescheduleIpiHandler);
|
Hal::RegisterIrqHandler(Hal::IRQ_RESCHEDULE, RescheduleIpiHandler);
|
||||||
|
|
||||||
Kt::KernelLogStream(Kt::OK, "Sched") << "Initialized (" << MaxProcesses
|
Kt::KernelLogStream(Kt::OK, "Sched") << "Initialized (" << MaxProcesses
|
||||||
@@ -389,7 +403,7 @@ namespace Sched {
|
|||||||
proc.args[0] = '\0';
|
proc.args[0] = '\0';
|
||||||
if (args != nullptr) {
|
if (args != nullptr) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i < 255 && args[i]; i++) {
|
for (; i < 4095 && args[i]; i++) {
|
||||||
proc.args[i] = args[i];
|
proc.args[i] = args[i];
|
||||||
}
|
}
|
||||||
proc.args[i] = '\0';
|
proc.args[i] = '\0';
|
||||||
@@ -1152,6 +1166,12 @@ namespace Sched {
|
|||||||
proc.runningOnCpu = -1;
|
proc.runningOnCpu = -1;
|
||||||
proc.reapReady = true;
|
proc.reapReady = true;
|
||||||
|
|
||||||
|
// Publish the exit code before waking waiters so SYS_WAITPID
|
||||||
|
// observes it the moment it unblocks.
|
||||||
|
exitLedgerPid[exitLedgerHead] = exitingPid;
|
||||||
|
exitLedgerCode[exitLedgerHead] = proc.exitCode;
|
||||||
|
exitLedgerHead = (exitLedgerHead + 1) % ExitLedgerSize;
|
||||||
|
|
||||||
// Wake any processes blocked on this PID
|
// Wake any processes blocked on this PID
|
||||||
for (int i = 0; i < MaxProcesses; i++) {
|
for (int i = 0; i < MaxProcesses; i++) {
|
||||||
if (processTable[i].state == ProcessState::Blocked &&
|
if (processTable[i].state == ProcessState::Blocked &&
|
||||||
@@ -1232,6 +1252,8 @@ namespace Sched {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processTable[primarySlot_].exitCode = 256 + 9; /* killed (SIGKILL) */
|
||||||
|
|
||||||
// Flag the main thread so its next tick (or scheduler dispatch)
|
// Flag the main thread so its next tick (or scheduler dispatch)
|
||||||
// routes through ExitProcess, which sweeps every sibling thread
|
// routes through ExitProcess, which sweeps every sibling thread
|
||||||
// and tears down the address space. If the main thread is parked
|
// and tears down the address space. If the main thread is parked
|
||||||
@@ -1263,6 +1285,33 @@ namespace Sched {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LookupExitCode(int pid) {
|
||||||
|
schedLock.Acquire();
|
||||||
|
int code = 0;
|
||||||
|
for (int i = 0; i < ExitLedgerSize; i++) {
|
||||||
|
if (exitLedgerPid[i] == pid) {
|
||||||
|
code = exitLedgerCode[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedLock.Release();
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetProcessExitCode(int pid, int code) {
|
||||||
|
schedLock.Acquire();
|
||||||
|
for (int i = 0; i < MaxProcesses; i++) {
|
||||||
|
if (processTable[i].pid != pid) continue;
|
||||||
|
// Only a process (primary slot) carries a process exit code;
|
||||||
|
// sibling-thread exit values go through the join path.
|
||||||
|
if (processTable[i].primarySlot == i) {
|
||||||
|
processTable[i].exitCode = code;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
schedLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
void BlockOnPid(int pid) {
|
void BlockOnPid(int pid) {
|
||||||
// If the target is already dead, return immediately
|
// If the target is already dead, return immediately
|
||||||
if (!IsAlive(pid)) return;
|
if (!IsAlive(pid)) return;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace Sched {
|
|||||||
uint64_t userStackTop; // User-space stack top
|
uint64_t userStackTop; // User-space stack top
|
||||||
uint64_t heapNext; // Simple bump allocator for user heap
|
uint64_t heapNext; // Simple bump allocator for user heap
|
||||||
uint32_t readdirCursor; // Next SYS_READDIR scratch slot
|
uint32_t readdirCursor; // Next SYS_READDIR scratch slot
|
||||||
char args[256]; // Command-line arguments (set by parent via Spawn)
|
char args[4096]; // Command-line arguments (set by parent via Spawn)
|
||||||
char user[32]; // Owner user name (inherited from parent on spawn)
|
char user[32]; // Owner user name (inherited from parent on spawn)
|
||||||
char cwd[256]; // Absolute current working directory
|
char cwd[256]; // Absolute current working directory
|
||||||
|
|
||||||
@@ -158,6 +158,8 @@ namespace Sched {
|
|||||||
|
|
||||||
// Block the current process until the given PID exits.
|
// Block the current process until the given PID exits.
|
||||||
void BlockOnPid(int pid);
|
void BlockOnPid(int pid);
|
||||||
|
int LookupExitCode(int pid);
|
||||||
|
void SetProcessExitCode(int pid, int code);
|
||||||
|
|
||||||
// Block the current process for the given number of milliseconds.
|
// Block the current process for the given number of milliseconds.
|
||||||
void BlockForSleep(uint64_t ms);
|
void BlockForSleep(uint64_t ms);
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ extern int errno;
|
|||||||
#define EWOULDBLOCK EAGAIN
|
#define EWOULDBLOCK EAGAIN
|
||||||
#define EOVERFLOW 75
|
#define EOVERFLOW 75
|
||||||
#define ETIMEDOUT 110
|
#define ETIMEDOUT 110
|
||||||
|
#define ENOTSUP 95
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef _LIBC_SPAWN_H
|
||||||
|
#define _LIBC_SPAWN_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* posix_spawn for MontaukOS, layered on SYS_SPAWN.
|
||||||
|
*
|
||||||
|
* Limitations (kernel spawn model):
|
||||||
|
* - argv is joined into a single args string; arguments containing
|
||||||
|
* spaces are rejected with EINVAL (no quoting in the kernel).
|
||||||
|
* - envp is ignored (no environment transfer on spawn).
|
||||||
|
* - file actions must be empty: stdio redirection needs kernel
|
||||||
|
* support that does not exist yet, so any recorded action makes
|
||||||
|
* posix_spawn fail with ENOTSUP rather than misbehave silently.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
short flags;
|
||||||
|
} posix_spawnattr_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int action_count;
|
||||||
|
} posix_spawn_file_actions_t;
|
||||||
|
|
||||||
|
int posix_spawnattr_init(posix_spawnattr_t *attr);
|
||||||
|
int posix_spawnattr_destroy(posix_spawnattr_t *attr);
|
||||||
|
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags);
|
||||||
|
int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags);
|
||||||
|
|
||||||
|
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *actions);
|
||||||
|
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *actions);
|
||||||
|
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *actions,
|
||||||
|
int fd, int newfd);
|
||||||
|
int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *actions,
|
||||||
|
int fd);
|
||||||
|
int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *actions,
|
||||||
|
int fd, const char *path, int oflag,
|
||||||
|
mode_t mode);
|
||||||
|
|
||||||
|
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[]);
|
||||||
|
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[]);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LIBC_SPAWN_H */
|
||||||
@@ -9,12 +9,12 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Status decoding. The Montauk kernel does not yet report exit codes
|
/* POSIX status decoding. waitpid() encodes a normal exit as code<<8
|
||||||
through SYS_WAITPID, so waited children always decode as exit 0. */
|
and a killed/crashed child as the signal number in the low bits. */
|
||||||
#define WIFEXITED(s) (((s) & 0x7F) == 0)
|
#define WIFEXITED(s) (((s) & 0x7F) == 0)
|
||||||
#define WEXITSTATUS(s) (((s) >> 8) & 0xFF)
|
#define WEXITSTATUS(s) (((s) >> 8) & 0xFF)
|
||||||
#define WIFSIGNALED(s) (0)
|
#define WIFSIGNALED(s) (((s) & 0x7F) != 0)
|
||||||
#define WTERMSIG(s) (0)
|
#define WTERMSIG(s) ((s) & 0x7F)
|
||||||
#define WIFSTOPPED(s) (0)
|
#define WIFSTOPPED(s) (0)
|
||||||
#define WSTOPSIG(s) (0)
|
#define WSTOPSIG(s) (0)
|
||||||
|
|
||||||
|
|||||||
@@ -321,7 +321,9 @@ namespace montauk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process management
|
// Process management
|
||||||
inline void waitpid(int pid) { syscall1(montauk::abi::SYS_WAITPID, (uint64_t)pid); }
|
// Blocks until pid exits. Returns 0..255 for a normal exit,
|
||||||
|
// 256+signal when the process was killed or crashed.
|
||||||
|
inline int waitpid(int pid) { return (int)syscall1(montauk::abi::SYS_WAITPID, (uint64_t)pid); }
|
||||||
|
|
||||||
// Framebuffer
|
// Framebuffer
|
||||||
inline void fb_info(montauk::abi::FbInfo* info) { syscall1(montauk::abi::SYS_FBINFO, (uint64_t)info); }
|
inline void fb_info(montauk::abi::FbInfo* info) { syscall1(montauk::abi::SYS_FBINFO, (uint64_t)info); }
|
||||||
|
|||||||
@@ -37,23 +37,24 @@ static inline long _sys2(long nr, long a1, long a2) {
|
|||||||
extern int main(int argc, char** argv);
|
extern int main(int argc, char** argv);
|
||||||
|
|
||||||
void _start(void) {
|
void _start(void) {
|
||||||
char argbuf[256];
|
/* Static: 4 KiB args + 256 argv slots would crowd a 32 KiB stack. */
|
||||||
|
static char argbuf[4096];
|
||||||
|
static char* argv[256];
|
||||||
int len = (int)_sys2(SYS_GETARGS, (long)argbuf, (long)sizeof(argbuf));
|
int len = (int)_sys2(SYS_GETARGS, (long)argbuf, (long)sizeof(argbuf));
|
||||||
|
|
||||||
char* argv[32];
|
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
|
|
||||||
argv[argc++] = (char*)"prog";
|
argv[argc++] = (char*)"prog";
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
if (len > 255) {
|
if (len > (int)sizeof(argbuf) - 1) {
|
||||||
len = 255;
|
len = (int)sizeof(argbuf) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
argbuf[len] = '\0';
|
argbuf[len] = '\0';
|
||||||
|
|
||||||
char* p = argbuf;
|
char* p = argbuf;
|
||||||
while (*p != '\0' && argc < 31) {
|
while (*p != '\0' && argc < 255) {
|
||||||
while (*p == ' ') {
|
while (*p == ' ') {
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
+157
-4
@@ -21,6 +21,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
|
#include <spawn.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@@ -3404,17 +3405,21 @@ int dup2(int oldfd, int newfd) {
|
|||||||
return newfd;
|
return newfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SYS_WAITPID blocks until the child exits but does not report its
|
/* SYS_WAITPID blocks until the child exits and returns 0..255 for a
|
||||||
exit code yet, so successful waits decode as exit status 0. */
|
normal exit or 256+signal for a killed/crashed child. */
|
||||||
pid_t waitpid(pid_t pid, int *status, int options) {
|
pid_t waitpid(pid_t pid, int *status, int options) {
|
||||||
(void)options;
|
(void)options;
|
||||||
if (pid <= 0) {
|
if (pid <= 0) {
|
||||||
errno = ECHILD; /* no process groups / wait-any support */
|
errno = ECHILD; /* no process groups / wait-any support */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
_zos_syscall1(SYS_WAITPID, (long)pid);
|
long code = _zos_syscall1(SYS_WAITPID, (long)pid);
|
||||||
if (status != NULL) {
|
if (status != NULL) {
|
||||||
*status = 0;
|
if (code >= 256) {
|
||||||
|
*status = (int)((code - 256) & 0x7F); /* signaled */
|
||||||
|
} else {
|
||||||
|
*status = (int)((code & 0xFF) << 8); /* exited */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return pid;
|
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
|
/* The libc environment lives in name/value slots (getenv/setenv), not
|
||||||
NAME=VALUE strings, and exec* is unimplemented, so nothing can
|
NAME=VALUE strings, and exec* is unimplemented, so nothing can
|
||||||
consume a populated environ yet. An empty, valid vector satisfies
|
consume a populated environ yet. An empty, valid vector satisfies
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,11 +17,33 @@ static bool file_exists(const char* path) {
|
|||||||
|
|
||||||
// ---- Try to spawn an ELF at the given path ----
|
// ---- Try to spawn an ELF at the given path ----
|
||||||
|
|
||||||
|
static void print_exit_code(int code) {
|
||||||
|
if (code == 0) return;
|
||||||
|
char buf[16];
|
||||||
|
int n = 0;
|
||||||
|
if (code >= 256) {
|
||||||
|
montauk::print("[terminated by signal ");
|
||||||
|
code -= 256;
|
||||||
|
} else {
|
||||||
|
montauk::print("[exit code ");
|
||||||
|
}
|
||||||
|
if (code == 0) buf[n++] = '0';
|
||||||
|
while (code > 0 && n < 15) {
|
||||||
|
buf[n++] = (char)('0' + code % 10);
|
||||||
|
code /= 10;
|
||||||
|
}
|
||||||
|
while (n > 0) {
|
||||||
|
char c[2] = { buf[--n], 0 };
|
||||||
|
montauk::print(c);
|
||||||
|
}
|
||||||
|
montauk::print("]\n");
|
||||||
|
}
|
||||||
|
|
||||||
static bool try_exec(const char* path, const char* args) {
|
static bool try_exec(const char* path, const char* args) {
|
||||||
if (!file_exists(path)) return false;
|
if (!file_exists(path)) return false;
|
||||||
int pid = montauk::spawn(path, args);
|
int pid = montauk::spawn(path, args);
|
||||||
if (pid < 0) return false;
|
if (pid < 0) return false;
|
||||||
montauk::waitpid(pid);
|
print_exit_code(montauk::waitpid(pid));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user