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}