code wiki / _hdl_build / nx_pulse_daemon.nx
nx_pulse_daemon.nx source
↩ module page · 145 lines · 7135 B
1// nx_pulse_daemon.nx -- the CONDUCTOR'S LOOP, armed: re-runs nx_team_pulse on a schedule so every
2// role's evidence (triage, prove-all, security, examiner, scorecard) refreshes WITHOUT a human or
3// Claude asking. This is the unit the scorecard's "run on the loop" race-to-exceed names, made real.
4// - interval from knowledge/status/pulse_daemon.conf (I row; data-driven, no magic numbers)
5// - each beat: fork+exec the DURABLE runner _offc/nx_sov_build_run.elf nx_team_pulse
6// (runner rebuilds from settled source -> a /tmp wipe self-heals on the next beat)
7// - beat output appended to knowledge/status/pulse_daemon.out; one DAEMON-BEAT line per beat
8// to knowledge/status/pulse_daemon.log (epoch + pulse rc) = the durable heartbeat trend
9// - self-daemonizes (fork, parent exits, child setsid) so any caller returns immediately
10// - duplicate guard via knowledge/status/pulse_daemon.pid + /proc/<pid>/cmdline check
11// Sovereign (syscalls only, no .sh). Run from nxc2 root. license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
14const K_MAGIC_4096: i64 = 4096
15const K_MAGIC_4080: i64 = 4080
16func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
17// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
18// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
19// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
20// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
21func _pn(v: i64) -> i64 { nxi_out(v); return 0 }
22func _fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
23// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
24// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
25// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
26// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
27func _fn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
28func pd_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
29func pd_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i }
30func pd_read_all(path: *u8, buf: *u8, cap: i64) -> i64 {
31 let fd: i64 = sys_openat_rd(path)
32 if fd < 0 { return 0 }
33 var n: i64 = 0
34 var go: i64 = 1
35 while go == 1 { let base: i64 = buf as i64; let r: i64 = sys_read(fd, (base + n) as *u8, cap - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } }
36 sys_close(fd)
37 return n
38}
39func pd_contains(hay: *u8, n: i64, pat: *u8) -> i64 {
40 let pl: i64 = pd_len(pat)
41 if pl == 0 { return 0 }
42 var i: i64 = 0
43 while i + pl <= n {
44 var k: i64 = 0
45 var hit: i64 = 1
46 while k < pl { if hay[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } }
47 if hit == 1 { return 1 }
48 i = i + 1
49 }
50 return 0
51}
52// sleep secs via clock_nanosleep CLOCK_REALTIME rel (the proven supervisor idiom)
53func pd_sleep(secs: i64) -> i64 {
54 let ts: *i64 = sys_mmap(16) as *i64
55 ts[0] = secs
56 ts[1] = 0
57 return __syscall(SYS_CLOCK_NANOSLEEP, 0, 0, ts, 0, 0, 0)
58}
59// interval from conf 'I <sec>' row; 0 if conf/row absent (caller fails loud)
60func pd_interval() -> i64 {
61 let buf: *u8 = sys_mmap(K_MAGIC_4096)
62 let n: i64 = pd_read_all("knowledge/status/pulse_daemon.conf" as *u8, buf, K_MAGIC_4080)
63 var i: i64 = 0
64 while i < n {
65 var le: i64 = i
66 var scan: i64 = 1
67 while scan == 1 {
68 if le >= n { scan = 0 }
69 else { if buf[le] == (10 as u8) { scan = 0 } else { le = le + 1 } }
70 }
71 if buf[i] == (73 as u8) {
72 var v: i64 = 0
73 var q: i64 = i + 2
74 while q < le {
75 if buf[q] >= (48 as u8) { if buf[q] <= (57 as u8) { v = v * 10 + ((buf[q] as i64) - 48) } }
76 q = q + 1
77 }
78 return v
79 }
80 i = le + 1
81 }
82 return 0
83}
84// is a prior daemon still alive? pidfile -> /proc/<pid>/cmdline contains our name
85func pd_already_running() -> i64 {
86 let pbuf: *u8 = sys_mmap(64)
87 let pn: i64 = pd_read_all("knowledge/status/pulse_daemon.pid" as *u8, pbuf, 32)
88 if pn <= 0 { return 0 }
89 let path: *u8 = sys_mmap(256)
90 var o: i64 = 0
91 o = pd_cat(path, o, "/proc/" as *u8)
92 var i: i64 = 0
93 while i < pn { if pbuf[i] >= (48 as u8) { if pbuf[i] <= (57 as u8) { path[o] = pbuf[i]; o = o + 1 } } i = i + 1 }
94 o = pd_cat(path, o, "/cmdline" as *u8); path[o] = 0 as u8
95 let cbuf: *u8 = sys_mmap(512)
96 let cn: i64 = pd_read_all(path, cbuf, 500)
97 if cn <= 0 { return 0 }
98 return pd_contains(cbuf, cn, "nx_pulse_daemon" as *u8)
99}
100// one beat: run the durable runner on nx_team_pulse, stdout+stderr -> pulse_daemon.out; return rc
101func pd_beat() -> i64 {
102 let pid: i64 = sys_fork()
103 if pid == 0 {
104 let runner: *u8 = "_offc/nx_sov_build_run.elf" as *u8
105 let argv: *i64 = sys_mmap(32) as *i64
106 argv[0] = runner as i64; argv[1] = "nx_team_pulse" as *u8 as i64; argv[2] = 0
107 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
108 let ofd: i64 = sys_openat_append("knowledge/status/pulse_daemon.out" as *u8, 0x1a4)
109 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) }
110 sys_execve(runner, argv, envp)
111 sys_exit(127)
112 }
113 let st: *i64 = sys_mmap(16) as *i64
114 sys_wait4(pid, st, 0)
115 if (st[0] % 128) != 0 { return 0 - 1 }
116 return (st[0] >> 8) & 0xff
117}
118func main() -> i64 {
119 let interval: i64 = pd_interval()
120 if interval <= 0 { _p("nx_pulse_daemon: conf MISSING/invalid (knowledge/status/pulse_daemon.conf I row) -- fail loud\n" as *u8); sys_exit(1); return 1 }
121 if pd_already_running() == 1 { _p("nx_pulse_daemon: ALREADY-RUNNING (pidfile live) -- not starting a duplicate\n" as *u8); sys_exit(0); return 0 }
122 // daemonize: parent exits so the caller (runner/scheduler) returns; child owns the loop
123 let pid: i64 = sys_fork()
124 if pid != 0 {
125 _p("nx_pulse_daemon: ARMED pid=" as *u8); _pn(pid); _p(" interval=" as *u8); _pn(interval); _p("s (beats -> knowledge/status/pulse_daemon.log)\n" as *u8)
126 let pfd: i64 = sys_openat_wr("knowledge/status/pulse_daemon.pid" as *u8, 0x1a4)
127 if pfd >= 0 { _fn(pfd, pid); sys_close(pfd) }
128 sys_exit(0)
129 return 0
130 }
131 nx_setsid()
132 var go: i64 = 1
133 while go == 1 {
134 let rc: i64 = pd_beat()
135 let lfd: i64 = sys_openat_append("knowledge/status/pulse_daemon.log" as *u8, 0x1a4)
136 if lfd >= 0 {
137 _fp(lfd, "DAEMON-BEAT epoch=" as *u8); _fn(lfd, sys_now_realtime_sec())
138 _fp(lfd, " pulse_rc=" as *u8); _fn(lfd, rc)
139 if rc == 0 { _fp(lfd, " verdict=GREEN\n" as *u8) } else { _fp(lfd, " verdict=ATTENTION\n" as *u8) }
140 sys_close(lfd)
141 }
142 pd_sleep(interval)
143 }
144 return 0
145}