code wiki / (root) / nx_proc.nx

nx_proc.nx source

↩ module page · 149 lines · 5123 B

1// nx_proc.nx -- process management syscalls (fork / execve / wait4 / clone). 2// 3// Used by: 4// - Test harnesses that fork off child workers 5// - Build tools that exec compilers / linkers / assemblers 6// - Sandboxes that spawn restricted subprocesses 7// - Watchdogs that fork supervisors 8// 9// All syscalls go through __syscall. We accept i64 for argv / envp 10// pointer arrays since NishiLang doesn't have *char ** in the type 11// system today. 12// 13// Exit-status macros: nx_proc_wait_exited / nx_proc_wait_status / 14// nx_proc_wait_signaled mirror the WIFEXITED / WEXITSTATUS / WIFSIGNALED 15// glibc macros for inspecting wait4's status word. 16 17// nx_safety_envelope: 18// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 19// sil_target: SIL1 20// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 21// verdict: NOT_YET_EVALUATED 22 23import "syscalls.nx" 24 25// Linux RV64 syscall numbers (excerpt). 26const NX_SYS_CLONE: i64 = 220 27const NX_SYS_EXECVE: i64 = 221 28const NX_SYS_EXIT: i64 = 93 29const NX_SYS_EXIT_GRP: i64 = 94 30const NX_SYS_WAIT4: i64 = 260 31const NX_SYS_GETPID: i64 = 172 32const NX_SYS_GETPPID: i64 = 173 33 34// clone flags 35const NX_CLONE_VM: i64 = 0x100 36const NX_CLONE_FS: i64 = 0x200 37const NX_CLONE_FILES: i64 = 0x400 38const NX_CLONE_SIGHAND: i64 = 0x800 39const NX_CLONE_THREAD: i64 = 0x10000 40 41// fork(): clone(SIGCHLD) on Linux. RV64 has no sys_fork; clone 42// with no special flags + SIGCHLD as the signal mask achieves it. 43// 44// Returns: 45// 0 in the child 46// pid in the parent 47// -errno on failure 48const NX_SIGCHLD_NUM: i64 = 17 49 50func nx_proc_fork() -> i64 { 51 return __syscall(NX_SYS_CLONE, NX_SIGCHLD_NUM, 0, 0, 0, 0, 0) 52} 53 54// execve(path, argv, envp). argv / envp are NULL-terminated arrays 55// of *u8 pointers. Caller passes them as i64 pointer values; we 56// hand them to the kernel verbatim. 57// 58// Returns: 59// no return on success (process is replaced) 60// -errno on failure 61func nx_proc_execve(path: *u8, argv: i64, envp: i64) -> i64 { 62 return __syscall(NX_SYS_EXECVE, path as i64, argv, envp, 0, 0, 0) 63} 64 65// wait4(pid, status_ptr, options, rusage_ptr). status_ptr receives 66// the encoded exit status; pass 0 for rusage_ptr if you don't care 67// about resource accounting. 68// 69// pid semantics: 70// -1 = any child 71// >0 = specific pid 72// 0 = any child in same process group 73// <-1 = any child in process group |pid| 74// 75// Returns: 76// pid of reaped child 77// 0 if WNOHANG was set and no child is ready 78// -errno on failure 79func nx_proc_wait4(pid: i64, status: *i64, options: i64) -> i64 { 80 return __syscall(NX_SYS_WAIT4, pid, status as i64, options, 0, 0, 0) 81} 82 83// Convenience: wait for any child, blocking, no rusage. 84func nx_proc_wait_any(status: *i64) -> i64 { 85 return nx_proc_wait4(0 - 1, status, 0) 86} 87 88// WIFEXITED: child exited normally (low byte of status == 0). 89func nx_proc_wait_exited(status: i64) -> i64 { 90 if (status & 0x7F) == 0 { return 1 } 91 return 0 92} 93 94// WEXITSTATUS: extract the 8-bit exit code (when WIFEXITED). 95func nx_proc_wait_status(status: i64) -> i64 { 96 return (status >> 8) & 0xFF 97} 98 99// WIFSIGNALED: child killed by signal (low 7 bits != 0 and != 0x7F). 100func nx_proc_wait_signaled(status: i64) -> i64 { 101 let low: i64 = status & 0x7F 102 if low == 0 { return 0 } 103 if low == 0x7F { return 0 } 104 return 1 105} 106 107// WTERMSIG: which signal killed the child (low 7 bits). 108func nx_proc_wait_termsig(status: i64) -> i64 { 109 return status & 0x7F 110} 111 112// getpid / getppid pass-through for completeness. 113func nx_proc_getpid() -> i64 { 114 return __syscall(NX_SYS_GETPID, 0, 0, 0, 0, 0, 0) 115} 116 117func nx_proc_getppid() -> i64 { 118 return __syscall(NX_SYS_GETPPID, 0, 0, 0, 0, 0, 0) 119} 120 121// ---- self-test --------------------------------------------------- 122 123func main() -> i64 { 124 // Constants are within Linux range. 125 if NX_SYS_CLONE != 220 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 126 if NX_SYS_EXECVE != 221 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 127 if NX_SIGCHLD_NUM != 17 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 128 129 // Status decoding edge cases. 130 // Child exited with code 5: status = 5 << 8 = 0x500. 131 if nx_proc_wait_exited(0x500) != 1 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 132 if nx_proc_wait_status(0x500) != 5 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 133 if nx_proc_wait_signaled(0x500) != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 134 135 // Child killed by SIGTERM (15): status = 15. 136 if nx_proc_wait_exited(15) != 0 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 137 if nx_proc_wait_signaled(15) != 1 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 138 if nx_proc_wait_termsig(15) != 15 { return __syscall(93, 9, 0, 0, 0, 0, 0) } 139 140 // Child stopped (low 7 = 0x7F): not exited, not killed. 141 if nx_proc_wait_exited(0x7F) != 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 142 if nx_proc_wait_signaled(0x7F) != 0 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 143 144 // getpid should return a positive number. 145 let pid: i64 = nx_proc_getpid() 146 if pid <= 0 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 147 148 return 0 149}