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}