code wiki / _hdl_build / nx_pub_recover_exec.nx
nx_pub_recover_exec.nx source
↩ module page · 75 lines · 4091 B
1// nx_pub_recover_exec.nx -- THE PUBLISHER'S RECOVERY EXECUTOR (the action rung after nx_pub_recover's decision).
2//
3// Consumes PENDING recovery requests from the queue, KILLS the wedged daemon by name (proc_kill_by_name ->
4// the existing nx_hostctl keeper respawns a FRESH one = the wedge clears), flips the queue line PENDING->DONE
5// (atomic rewrite under fl_acquire), and appends a recovery LEDGER record. The Publisher owns the recovery
6// ACTION; hosting's supervisor owns the respawn (its existing job) -> clean separation, no second supervisor,
7// no touching nx_hostctl. SIGTERM (15) = graceful; never-brick #26 (kill+supervised-respawn is reversible,
8// no destructive write). Runs on the host where the daemons live (NAS); the gate proves it on a real mock.
9// pe_exec(queuepath, ledgerpath) -> number of recoveries executed
10// license_tier: ORIGINAL
11import "nx_syscalls.nx"
12import "nx_proc_ctl.nx" // proc_kill_by_name / proc_alive_by_name
13import "nx_pub_recover.nx" // pr_field / pr_n / pr_s / pr_slen + (transitively) fl_acquire / fl_release / fa_appendz
14const PE_MAGIC_100000: i64 = 100000
15const PE_MAGIC_4096: i64 = 4096
16
17const PE_QUEUE: *u8 = "knowledge/publish/recovery_queue.tsv"
18const PE_LEDGER: *u8 = "knowledge/publish/recovery_ledger.tsv"
19const PE_LOCK: *u8 = "publish_recover"
20const PE_SIGTERM: i64 = 15
21
22func pe_exec(queuepath: *u8, ledgerpath: *u8) -> i64 {
23 let lk: i64 = fl_acquire(PE_LOCK, PE_MAGIC_100000, 1)
24 let lp: *i64 = sys_mmap(16) as *i64; lp[0] = 0
25 let b: *u8 = sys_read_file(queuepath, lp)
26 if (b as i64) == 0 { fl_release(lk); return 0 }
27 let n: i64 = lp[0]
28 let out: *u8 = sys_mmap(n + PE_MAGIC_4096); var oo: i64 = 0
29 let daemon: *u8 = sys_mmap(128); let f0: *u8 = sys_mmap(32)
30 var executed: i64 = 0
31 var i: i64 = 0
32 while i < n {
33 var e: i64 = i; while e < n { if b[e] == (10 as u8) { break } else { e = e + 1 } }
34 let ll: i64 = e - i
35 if ll > 0 {
36 pr_field(((b as i64)+i) as *u8, ll, 0, f0)
37 if f0[0] == (80 as u8) { // 'P' of PENDING
38 pr_field(((b as i64)+i) as *u8, ll, 2, daemon)
39 let killed: i64 = proc_kill_by_name(daemon, PE_SIGTERM)
40 let rec: *u8 = sys_mmap(256); var ro: i64 = 0
41 ro = pr_n(rec, ro, sys_now_realtime_sec()); rec[ro]=9 as u8; ro=ro+1
42 ro = pr_s(rec, ro, daemon); rec[ro]=9 as u8; ro=ro+1
43 ro = pr_n(rec, ro, killed); rec[ro]=9 as u8; ro=ro+1
44 ro = pr_s(rec, ro, "KILLED" as *u8); rec[ro]=10 as u8; ro=ro+1
45 fa_appendz(ledgerpath, rec, 256)
46 executed = executed + 1
47 // rewrite line: PENDING(7 chars) -> DONE, rest verbatim from the first TAB onward
48 oo = pr_s(out, oo, "DONE" as *u8)
49 var c: i64 = i + 7
50 while c < e { out[oo] = b[c]; oo = oo + 1; c = c + 1 }
51 out[oo] = 10 as u8; oo = oo + 1
52 } else {
53 var c: i64 = i
54 while c < e { out[oo] = b[c]; oo = oo + 1; c = c + 1 }
55 out[oo] = 10 as u8; oo = oo + 1
56 }
57 }
58 i = e + 1
59 }
60 // atomic queue rewrite: write <queue>.tmp then rename over the live queue.
61 let tmp: *u8 = sys_mmap(320); var to: i64 = pr_s(tmp, 0, queuepath)
62 tmp[to]=46 as u8; to=to+1; tmp[to]=116 as u8; to=to+1; tmp[to]=109 as u8; to=to+1; tmp[to]=112 as u8; to=to+1; tmp[to]=0 as u8 // ".tmp"
63 let fd: i64 = sys_openat_wr(tmp, 0x1a4)
64 if fd >= 0 { sys_write(fd, out, oo); sys_close(fd); __syscall(82, tmp as i64, queuepath as i64, 0, 0, 0, 0) } // rename(2)
65 fl_release(lk)
66 return executed
67}
68
69func pe_uw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
70func main(argc: i64, argv: *i64) -> i64 {
71 pe_uw("=== nishi-publisher recovery EXECUTOR ===\n" as *u8)
72 let c: i64 = pe_exec(PE_QUEUE, PE_LEDGER)
73 pe_uw("recoveries executed: " as *u8); let nb: *u8=sys_mmap(24); let e: i64=pr_n(nb,0,c); sys_write(1,nb,e); pe_uw("\n" as *u8)
74 return 0
75}