code wiki / _hdl_build / nx_hostdeploy.nx
nx_hostdeploy.nx source
↩ module page · 124 lines · 7650 B
1// nx_hostdeploy.nx -- sovereign deploy of the S-class hosting stack to the NAS. Streams 4 artifacts over
2// the team's OWN SSH (no scp): the router daemon -> sites.elf.new, sites.conf -> sites.conf.new, the
3// andelinwest legal page -> andelinwest_index.html.new, and the sovereign control plane -> nx_hostctl.
4// Then launches `nx_hostctl takeover` -- which does ALL the logic in Nishi (proc-kill the old .sh
5// supervisor + daemon, atomic sys_renameat publish, become the sovereign supervisor). The only shell is
6// the one-time chmod+x + setsid launch of the sovereign binary (the daemon-start bootstrap). Password from
7// /tmp/nxpw (staged + deleted by the runner). license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_ssh_lib.nx"
10const K_MAGIC_65536: i64 = 65536
11const K_MAGIC_1048576: i64 = 1048576
12const K_MAGIC_16384: i64 = 16384
13const K_MAGIC_1024: i64 = 1024
14
15func slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=0 as u8 { n=n+1 } return n }
16func ssh_putn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1}; sys_write(2,b,k); return 0 }
17
18// SSH flow-control: drain any pending server packets (WINDOW_ADJUST etc.) NON-BLOCKINGLY. Without this,
19// on a large transfer the server's send buffer fills (we never read while sending) -> sshd stops reading
20// us -> our sys_write blocks -> bidirectional deadlock. Polling + draining between chunks prevents it.
21func ssh_drain(st: *SshState) -> i64 {
22 let pfd: *u8 = sys_mmap(8)
23 let fdv: i64 = st.fd
24 pfd[0]=(fdv & 0xff) as u8; pfd[1]=((fdv>>8)&0xff) as u8; pfd[2]=((fdv>>16)&0xff) as u8; pfd[3]=((fdv>>24)&0xff) as u8
25 pfd[4]=1 as u8; pfd[5]=0 as u8; pfd[6]=0 as u8; pfd[7]=0 as u8 // events = POLLIN(1)
26 let buf: *u8 = sys_mmap(K_MAGIC_65536)
27 var run: i64 = 1
28 while run == 1 {
29 let r: i64 = sys_poll(pfd, 1, 0) // timeout 0 = non-blocking peek
30 if r <= 0 { run = 0 } else { if ssh_enc_recv(st, buf) < 0 { run = 0 } }
31 }
32 return 0
33}
34
35func ssh_put_file(st: *SshState, wcmd: *u8, wcmdlen: i64, data: *u8, datalen: i64) -> i64 {
36 let co: *u8 = sys_mmap(64); var c: i64 = 0
37 c = ssh_put_byte(co, c, 90); c = ssh_put_str(co, c, "session" as *u8, 7)
38 c = ssh_put_u32(co, c, 0); c = ssh_put_u32(co, c, K_MAGIC_1048576); c = ssh_put_u32(co, c, K_MAGIC_16384)
39 ssh_enc_send(st, co, c)
40 let rep: *u8 = sys_mmap(K_MAGIC_65536); var rcid: i64 = 0 - 1; var guard: i64 = 0
41 while rcid < 0 {
42 if guard > 16 { return 0 - 1 }
43 let rl: i64 = ssh_enc_recv(st, rep); if rl < 0 { return 0 - 1 }
44 if rep[0] == 91 as u8 { rcid = ssh_u32be(rep, 5) }
45 if rep[0] == 92 as u8 { return 0 - 2 }
46 guard = guard + 1
47 }
48 let cr: *u8 = sys_mmap(K_MAGIC_1024); var q: i64 = 0
49 q = ssh_put_byte(cr, q, 98); q = ssh_put_u32(cr, q, rcid); q = ssh_put_str(cr, q, "exec" as *u8, 4); q = ssh_put_byte(cr, q, 1); q = ssh_put_str(cr, q, wcmd, wcmdlen)
50 ssh_enc_send(st, cr, q)
51 var off: i64 = 0
52 while off < datalen {
53 var nn: i64 = datalen - off; if nn > K_MAGIC_16384 { nn = K_MAGIC_16384 }
54 let dp: *u8 = sys_mmap(nn + 64); var p: i64 = 0
55 p = ssh_put_byte(dp, p, 94); p = ssh_put_u32(dp, p, rcid); p = ssh_put_u32(dp, p, nn)
56 var i: i64 = 0; while i < nn { dp[p + i] = data[off + i]; i = i + 1 } p = p + nn
57 sys_write(2, "[s" as *u8, 2)
58 ssh_enc_send(st, dp, p)
59 sys_write(2, "d" as *u8, 1)
60 ssh_drain(st) // flow-control: drain server WINDOW_ADJUST between chunks
61 sys_write(2, "@" as *u8, 1); ssh_putn(off + nn); sys_write(2, "] " as *u8, 2)
62 off = off + nn
63 }
64 let eo: *u8 = sys_mmap(16); var e: i64 = 0; e = ssh_put_byte(eo, e, 96); e = ssh_put_u32(eo, e, rcid); ssh_enc_send(st, eo, e)
65 // POLL-BOUNDED close-wait: ssh_enc_recv blocks forever if CHANNEL_CLOSE is delayed/absent. Poll (500ms);
66 // read CLOSE if it comes; after ~3s quiet the file is fully written -> move on (open the next channel).
67 let pfd2: *u8 = sys_mmap(8)
68 let fdv2: i64 = st.fd
69 pfd2[0]=(fdv2&0xff) as u8; pfd2[1]=((fdv2>>8)&0xff) as u8; pfd2[2]=((fdv2>>16)&0xff) as u8; pfd2[3]=((fdv2>>24)&0xff) as u8
70 pfd2[4]=1 as u8; pfd2[5]=0 as u8
71 var done: i64 = 0; var quiet: i64 = 0
72 while done == 0 {
73 let pr: i64 = sys_poll(pfd2, 1, 500)
74 if pr <= 0 { quiet = quiet + 1; if quiet >= 6 { done = 1 } }
75 else { let rl: i64 = ssh_enc_recv(st, rep); if rl < 0 { done = 1 } else { if rep[0] == 97 as u8 { done = 1 } } }
76 }
77 let clo: *u8 = sys_mmap(16); var k: i64 = 0; k = ssh_put_byte(clo, k, 97); k = ssh_put_u32(clo, k, rcid); ssh_enc_send(st, clo, k)
78 return 0
79}
80
81func rd(path: *u8, lenout: *i64) -> *u8 {
82 let box: *i64 = sys_mmap(16) as *i64; box[0] = 0
83 let p: *u8 = sys_read_file(path, box); lenout[0] = box[0]; return p
84}
85
86func main() -> i64 {
87 let pwbox: *i64 = sys_mmap(16) as *i64
88 let pw: *u8 = sys_read_file("/tmp/nxpw" as *u8, pwbox)
89 if (pw as i64) == 0 { ssh_puts("no /tmp/nxpw\n" as *u8); return 1 }
90 var pwlen: i64 = pwbox[0]
91 while pwlen > 0 { if pw[pwlen-1] == 10 as u8 { pwlen = pwlen - 1 } else { if pw[pwlen-1] == 13 as u8 { pwlen = pwlen - 1 } else { break } } }
92
93 let dl: *i64 = sys_mmap(8) as *i64; let daemon: *u8 = rd("/tmp/sd2.elf" as *u8, dl)
94 if (daemon as i64)==0 { ssh_puts("missing /tmp/sd2.elf\n" as *u8); return 4 }
95 let cl: *i64 = sys_mmap(8) as *i64; let cfg: *u8 = rd("/tmp/sites.conf" as *u8, cl)
96 if (cfg as i64)==0 { ssh_puts("missing /tmp/sites.conf\n" as *u8); return 4 }
97 let il: *i64 = sys_mmap(8) as *i64; let idx: *u8 = rd("/tmp/aw_index.html" as *u8, il)
98 if (idx as i64)==0 { ssh_puts("missing /tmp/aw_index.html\n" as *u8); return 4 }
99 let hl: *i64 = sys_mmap(8) as *i64; let hc: *u8 = rd("/tmp/hc3.elf" as *u8, hl)
100 if (hc as i64)==0 { ssh_puts("missing /tmp/hc3.elf\n" as *u8); return 4 }
101
102 let st: *SshState = sys_mmap(SSH_STATE_BYTES) as *SshState
103 if ssh_open_session(st, (192 << 24) | (168 << 16) | (8 << 8) | 227) != 0 { ssh_puts("session fail\n" as *u8); return 2 }
104 if ssh_userauth_password(st, "elderwesto" as *u8, 10, pw, pwlen) != 1 { ssh_puts("auth fail\n" as *u8); sys_close(st.fd); return 3 }
105 ssh_puts("[auth ok] streaming router daemon + sites.conf + andelinwest page + sovereign control plane\n" as *u8)
106
107 let w1: *u8 = "cat > /volume1/homes/elderwesto/nishihost/sites.elf.new" as *u8
108 ssh_put_file(st, w1, slen(w1), daemon, dl[0])
109 let w2: *u8 = "cat > /volume1/homes/elderwesto/nishihost/sites.conf.new" as *u8
110 ssh_put_file(st, w2, slen(w2), cfg, cl[0])
111 let w3: *u8 = "cat > /volume1/homes/elderwesto/nishihost/andelinwest_index.html.new" as *u8
112 ssh_put_file(st, w3, slen(w3), idx, il[0])
113 let w4: *u8 = "cat > /volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
114 ssh_put_file(st, w4, slen(w4), hc, hl[0])
115 ssh_puts("[streamed] 4 artifacts; launching sovereign takeover\n" as *u8)
116
117 // The ONLY shell: chmod+x the freshly-streamed sovereign binary + setsid-launch it. ALL deploy LOGIC
118 // (proc-kill old, atomic publish, supervise) is inside nx_hostctl (pure Nishi). Then read its log back.
119 let c: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x nx_hostctl && ./nx_hostctl takeover; echo ===TAKEOVER-RC-$?===; sleep 16; echo ===LISTEN-8443===; (ss -ltn 2>/dev/null || netstat -ltn 2>/dev/null) | grep ':8443 '; echo ===END===" as *u8
120 ssh_exec(st, c, slen(c))
121 sys_close(st.fd)
122 ssh_puts("[deploy issued]\n" as *u8)
123 return 0
124}