code wiki / (root) / nx_runsv.nx

nx_runsv.nx source

↩ module page · 96 lines · 5217 B

1// nx_runsv.nx -- RUN + SUPERVISE one service (s6/runit `runsv` convention). This is the REAL fork/wait-liveness + 2// real-health + backoff supervisor that the existing nx_supervise gate (runtime/_hdl_build/nx_supervise.nx) explicitly 3// flags as "remaining" (its health is MODELED; this wires it to reality). Best-practice model (June-2026: supervisord/ 4// s6/runit/systemd): the SUPERVISOR daemonizes, the SERVICE runs FOREGROUND under it. Composes the team's organs -- 5// NO hand-rolled daemonize/health/backoff: 6// nx_health_probe.hp_probe -> readiness (serving, not just bound; catches HUNG) 7// nx_restart_guard.rg_should_restart -> crash-loop backoff (systemd-parity + exponential) 8// fork + fd->log + execve + wait4(WNOHANG) -> foreground spawn + liveness 9// Roadmap: knowledge/research/2026-06-30-service-supervisor-roadmap.md. Usage: nx_runsv <elf-path> <port> 10// license_tier: ORIGINAL 11import "nx_health_probe.nx" 12import "nx_restart_guard.nx" 13import "nx_svcreg.nx" 14import "nx_gate.nx" 15const RS_MAGIC_30000: i64 = 30000 16const RS_MAGIC_5000: i64 = 5000 17 18const RS_OK: i64 = 0 19const RS_RESPAWN: i64 = 1 20const RS_BACKOFF: i64 = 2 21const RS_KILLHUNG: i64 = 3 22 23func rs_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let d: i64=s[i] as i64; if d>=48 { if d<=57 { v=v*10+(d-48) } } i=i+1 } return v } 24 25// spawn `elf` as a FOREGROUND child of the supervisor; its stdout/stderr -> logpath; stdin <- /dev/null. Returns pid. 26func rs_spawn_fg(elf: *u8, logpath: *u8) -> i64 { 27 let pid: i64 = sys_fork() 28 if pid == 0 { 29 let din: i64 = sys_openat_rd("/dev/null" as *u8) 30 let dlog: i64 = sys_openat_wr(logpath, 0x1a4) 31 if din >= 0 { sys_dup3(din, 0, 0) } 32 if dlog >= 0 { sys_dup3(dlog, 1, 0); sys_dup3(dlog, 2, 0) } 33 let av: *i64 = sys_mmap(16) as *i64; av[0] = elf as i64; av[1] = 0 34 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 // real env (matches nx_hostctl / nx_sov_build_run's proven spawn) 35 sys_execve(elf, av, envp) 36 sys_exit(127) 37 } 38 return pid 39} 40 41// decide the supervisor action from liveness (dead) + readiness (verdict), updating the crash-loop state. 42func rs_decide(dead: i64, verdict: i64, ws: *i64, cnt: *i64, now: i64) -> i64 { 43 var down: i64 = 0 44 if dead == 1 { down = 1 } 45 if verdict == HP_REFUSED { down = 1 } 46 if down == 1 { 47 if rg_should_restart(ws, cnt, now, RS_MAGIC_30000, 100) == 1 { return RS_RESPAWN } 48 return RS_BACKOFF 49 } 50 if verdict == HP_HUNG { return RS_KILLHUNG } 51 return RS_OK 52} 53 54func main(argc: i64, argv: *i64) -> i64 { 55 if argc < 2 { gw("usage: nx_runsv <service-name> | <elf-path> <port>\n" as *u8); sys_exit(2); return 2 } 56 var elf: *u8 = argv[1] as *u8 57 var port: i64 = 0 58 if argc == 2 { 59 // declarative (R5): resolve the service BY NAME from the registry -> elf + port 60 let box: *i64 = sys_mmap(16) as *i64; let lenbox: *i64 = sys_mmap(16) as *i64 61 if svc_get(argv[1] as *u8, box, lenbox) != 1 { gw("nx_runsv: unknown service '" as *u8); gw(argv[1] as *u8); gw("' (not in registry)\n" as *u8); sys_exit(1); return 1 } 62 let rec: *u8 = box[0] as *u8; let rl: i64 = lenbox[0] 63 let out2: *i64 = sys_mmap(16) as *i64 64 if tr_field(rec, 0, rl, 1, out2) == 0 { gw("nx_runsv: bad unit record\n" as *u8); sys_exit(1); return 1 } 65 let elfbuf: *u8 = sys_mmap(512); var bi: i64=0; while bi < out2[1] { elfbuf[bi]=rec[out2[0]+bi]; bi=bi+1 } elfbuf[out2[1]]=0 as u8 66 elf = elfbuf 67 tr_field(rec, 0, rl, 2, out2); port = tr_atoi(rec, out2[0], out2[1]) 68 gw("nx_runsv: resolved '" as *u8); gw(argv[1] as *u8); gw("' -> " as *u8); gw(elf); gw(" :" as *u8); gn(port); gw("\n" as *u8) 69 } else { 70 port = rs_atoi(argv[2] as *u8) 71 } 72 73 // FOREGROUND supervisor, kept alive by init (the init -> supervisor -> service hierarchy). On a real sovereign 74 // host nx_runsv self-daemonizes; on WSL-from-Windows a detached daemon is torn down when the invoking command 75 // exits, so the supervisor runs foreground under the persistent session (the WSL init-equivalent). It does NOT 76 // redirect its own fds -- init captures its output; it redirects the SERVICE's fds (rs_spawn_fg). 77 gw("nx_runsv: supervising " as *u8); gw(elf); gw(" on :" as *u8); gn(port); gw(" (foreground supervisor)\n" as *u8) 78 79 let ws: *i64 = sys_mmap(16) as *i64; let cnt: *i64 = sys_mmap(16) as *i64; ws[0] = 0; cnt[0] = 0 80 let svclog: *u8 = "/tmp/nx_runsv_service.log" as *u8 81 var pid: i64 = rs_spawn_fg(elf, svclog) 82 var go: i64 = 1 83 while go == 1 { 84 sys_sleep_ms(RS_MAGIC_5000) 85 let st: *i64 = sys_mmap(16) as *i64 86 let reaped: i64 = sys_wait4(pid, st, 1) // WNOHANG -- liveness 87 var dead: i64 = 0; if reaped == pid { dead = 1 } 88 var verdict: i64 = HP_SERVING 89 if dead == 0 { verdict = hp_probe(port, 3) } // readiness 90 let now: i64 = sys_now_ms() 91 let act: i64 = rs_decide(dead, verdict, ws, cnt, now) 92 if act == RS_RESPAWN { pid = rs_spawn_fg(elf, svclog) } 93 if act == RS_KILLHUNG { nx_kill(pid, 9) } 94 } 95 return 0 96}