code wiki / _hdl_build / nx_mp_up.nx
nx_mp_up.nx source
↩ module page · 122 lines · 6340 B
1// nx_mp_up.nx -- SOVEREIGN bring-up for the multiplayer daemon (the build->launch->verify->publish loop,
2// the operator's "no guessing if it's live"). Pure NishiLang orchestration: builds nx_mp_serve via the WOMB
3// in --build-only mode, fork+setsid+execve's it DETACHED (sovereign nohup, survives this command's exit),
4// then does a SOVEREIGN HTTP liveness check (sys_socket/connect/write/read -- NO curl, NO node) and prints
5// the live URL. Modes: up (default) / down / status. Build+run: ./_offc/nx_sov_build_run.elf nx_mp_up
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
9import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
10const K_MAGIC_1024: i64 = 1024
11const K_MAGIC_7702: i64 = 7702
12
13func mu_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
15// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
16// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
17// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
18func mu_putn(v: i64) -> i64 { nxi_out(v); return 0 }
19func mu_streq(a: *u8, b: *u8) -> i64 {
20 var i: i64 = 0
21 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
22 if b[i] != (0 as u8) { return 0 }
23 return 1
24}
25func mu_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
26func mu_write_pid(path: *u8, pid: i64) -> i64 {
27 let fd: i64 = sys_openat_wr(path, 0x1a4)
28 if fd < 0 { return 0 - 1 }
29 let b: *u8 = sys_mmap(32); var m: i64 = pid; var k: i64 = 0
30 if m == 0 { b[0] = 48 as u8; k = 1 } else {
31 let t: *u8 = sys_mmap(32); var j: i64 = 0
32 while m > 0 { t[j] = (48 + (m % 10)) as u8; m = m / 10; j = j + 1 }
33 while j > 0 { b[k] = t[j-1]; k = k + 1; j = j - 1 }
34 }
35 sys_write(fd, b, k); sys_close(fd); return 0
36}
37func mu_read_pid(path: *u8) -> i64 {
38 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 - 1 }
39 let b: *u8 = sys_mmap(32); let n: i64 = sys_read(fd, b, 31); sys_close(fd)
40 if n <= 0 { return 0 - 1 }
41 var v: i64 = 0; var i: i64 = 0
42 while i < n { let c: i64 = b[i] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48); i = i + 1 } else { i = n } } else { i = n } }
43 return v
44}
45// build an organ via the WOMB in --build-only mode (no run); parent waits. returns child exit code.
46func mu_build(womb: *u8, name: *u8, envp: *i64) -> i64 {
47 let pid: i64 = sys_fork()
48 if pid == 0 {
49 let av: *i64 = sys_mmap(8*5) as *i64
50 av[0] = womb as i64; av[1] = name as i64; av[2] = "--build-only" as *u8 as i64; av[3] = 0
51 sys_execve(womb, av, envp); sys_exit(127)
52 }
53 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0)
54 return (st[0] >> 8) & 0xff
55}
56// SOVEREIGN HTTP liveness check: connect to 127.0.0.1:port, GET /pets3d, look for "200" in the reply.
57// Pure syscalls -- no curl, no node. returns 1 live / 0 not.
58func mu_check(port: i64) -> i64 {
59 let addr: *u8 = sys_mmap(16)
60 addr[0] = 2 as u8; addr[1] = 0 as u8
61 addr[2] = ((port >> 8) & 0xff) as u8; addr[3] = (port & 0xff) as u8
62 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8
63 var z: i64 = 8; while z < 16 { addr[z] = 0 as u8; z = z + 1 }
64 let fd: i64 = sys_socket(2, 1, 0)
65 if fd < 0 { return 0 }
66 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 }
67 let req: *u8 = "GET /pets3d HTTP/1.0\r\nHost: localhost\r\n\r\n" as *u8
68 var rl: i64 = 0; while req[rl] != (0 as u8) { rl = rl + 1 }
69 sys_write(fd, req, rl)
70 let buf: *u8 = sys_mmap(K_MAGIC_1024)
71 let n: i64 = sys_read(fd, buf, 1023)
72 sys_close(fd)
73 if n <= 0 { return 0 }
74 var i: i64 = 0
75 while i + 3 <= n { if buf[i] == (50 as u8) { if buf[i+1] == (48 as u8) { if buf[i+2] == (48 as u8) { return 1 } } } i = i + 1 } // "200"
76 return 0
77}
78
79func main(argc: i64, argv: *i64) -> i64 {
80 let womb: *u8 = "_offc/nx_sov_build_run.elf" as *u8
81 let pidfile: *u8 = "/tmp/nx_mpd.pid" as *u8
82 let delf: *u8 = "/tmp/nx_mp_serve.sov.elf" as *u8
83 let envp: *i64 = sys_mmap(8*4) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
84 let PORT: i64 = K_MAGIC_7702
85
86 var mode: *u8 = "up" as *u8
87 if argc >= 2 { mode = argv[1] as *u8 }
88
89 if mu_streq(mode, "down" as *u8) == 1 {
90 let pid: i64 = mu_read_pid(pidfile)
91 if pid > 0 { nx_kill(pid, 9) }
92 mu_puts("[mp-up] DOWN (daemon pid=" as *u8); mu_putn(pid); mu_puts(" signalled)\n" as *u8)
93 sys_exit(0); return 0
94 }
95 if mu_streq(mode, "status" as *u8) == 1 {
96 if mu_check(PORT) == 1 { mu_puts("[mp-up] UP + SERVING -> http://localhost:7702/pets3d\n" as *u8); sys_exit(0); return 0 }
97 mu_puts("[mp-up] DOWN\n" as *u8); sys_exit(1); return 1
98 }
99
100 // up (default): build the daemon (build-only) if missing, then launch detached + verify
101 if mu_exists(delf) == 0 { mu_puts("[mp-up] building daemon (nx_mp_serve)...\n" as *u8); mu_build(womb, "nx_mp_serve" as *u8, envp) }
102 let old: i64 = mu_read_pid(pidfile)
103 if old > 0 { if nx_kill(old, 0) == 0 { if mu_check(PORT) == 1 { mu_puts("[mp-up] already UP pid=" as *u8); mu_putn(old); mu_puts(" -> http://localhost:7702/pets3d\n" as *u8); sys_exit(0); return 0 } } }
104
105 let pid: i64 = sys_fork()
106 if pid == 0 {
107 nx_setsid()
108 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
109 if dn >= 0 { sys_dup3(dn, 0, 0); sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) }
110 let av: *i64 = sys_mmap(8*4) as *i64; av[0] = delf as i64; av[1] = 0
111 sys_execve(delf, av, envp); sys_exit(127)
112 }
113 mu_write_pid(pidfile, pid)
114 sys_sleep_ms(800)
115 if mu_check(PORT) == 1 {
116 mu_puts("[mp-up] UP + VERIFIED SERVING pid=" as *u8); mu_putn(pid)
117 mu_puts(" -> http://localhost:7702/pets3d (open in 2 tabs, same #room, to co-op)\n" as *u8)
118 sys_exit(0); return 0
119 }
120 mu_puts("[mp-up] launched pid=" as *u8); mu_putn(pid); mu_puts(" but liveness check FAILED (port not answering)\n" as *u8)
121 sys_exit(1); return 1
122}