code wiki / _hdl_build / nx_autorun_daemon.nx

nx_autorun_daemon.nx source

↩ module page · 173 lines · 8710 B

1// nx_autorun_daemon.nx -- THE SLOW BEAT (was: math-only; X-Q-007 made the 2// target list DATA): each beat re-proves every conf-listed target through 3// the durable lane. Kept OFF the 30-min Conductor pulse on purpose (rule 4// 21: the pulse stays cheap and reads evidence; THIS daemon renews it). 5// - interval from knowledge/status/autorun_daemon.conf (I row) 6// - targets from the SAME conf (T <basename> rows, re-read EVERY beat so 7// conf edits apply without re-arm; no T rows = the historical 8// nx_math_rung_run default) -- policy in nx_ad_core (gate-driven) 9// - beat output -> autorun_daemon.out; AUTORUN-TGT row per target + ONE 10// AUTORUN-BEAT row (rung_rc= kept as worst-rc for old readers) 11// - self-daemonizes; duplicate guard via autorun_daemon.pid + /proc cmdline 12// Sovereign (syscalls only, no .sh). Run from nxc2 root. license_tier: ORIGINAL 13// (nx_syscalls arrives via nx_ad_core -- a direct second import alongside it 14// produced a deterministic NXASM rc=6 on this file; dedup fixed it) 15import "nx_ad_core.nx" 16const SYS_MAGIC_4096: i64 = 4096 17const SYS_MAGIC_4080: i64 = 4080 18func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 19func _pn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; 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);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 } 20func _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 } 21func _fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; 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);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 22func ad_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 23func ad_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 } 24func ad_read_all(path: *u8, buf: *u8, cap: i64) -> i64 { 25 let fd: i64 = sys_openat_rd(path) 26 if fd < 0 { return 0 } 27 var n: i64 = 0 28 var go: i64 = 1 29 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 } } 30 sys_close(fd) 31 return n 32} 33func ad_contains(hay: *u8, n: i64, pat: *u8) -> i64 { 34 let pl: i64 = ad_len(pat) 35 if pl == 0 { return 0 } 36 var i: i64 = 0 37 while i + pl <= n { 38 var k: i64 = 0 39 var hit: i64 = 1 40 while k < pl { if hay[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 41 if hit == 1 { return 1 } 42 i = i + 1 43 } 44 return 0 45} 46func ad_sleep(secs: i64) -> i64 { 47 let ts: *i64 = sys_mmap(16) as *i64 48 ts[0] = secs 49 ts[1] = 0 50 return __syscall(SYS_CLOCK_NANOSLEEP, 0, 0, ts, 0, 0, 0) 51} 52func ad_interval() -> i64 { 53 let buf: *u8 = sys_mmap(SYS_MAGIC_4096) 54 let n: i64 = ad_read_all("knowledge/status/autorun_daemon.conf" as *u8, buf, SYS_MAGIC_4080) 55 var i: i64 = 0 56 while i < n { 57 var le: i64 = i 58 var scan: i64 = 1 59 while scan == 1 { 60 if le >= n { scan = 0 } 61 else { if buf[le] == (10 as u8) { scan = 0 } else { le = le + 1 } } 62 } 63 if buf[i] == (73 as u8) { 64 var v: i64 = 0 65 var q: i64 = i + 2 66 while q < le { 67 if buf[q] >= (48 as u8) { if buf[q] <= (57 as u8) { v = v * 10 + ((buf[q] as i64) - 48) } } 68 q = q + 1 69 } 70 return v 71 } 72 i = le + 1 73 } 74 return 0 75} 76func ad_already_running() -> i64 { 77 let pbuf: *u8 = sys_mmap(64) 78 let pn: i64 = ad_read_all("knowledge/status/autorun_daemon.pid" as *u8, pbuf, 32) 79 if pn <= 0 { return 0 } 80 let path: *u8 = sys_mmap(256) 81 var o: i64 = 0 82 o = ad_cat(path, o, "/proc/" as *u8) 83 var i: i64 = 0 84 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 } 85 o = ad_cat(path, o, "/cmdline" as *u8); path[o] = 0 as u8 86 let cbuf: *u8 = sys_mmap(512) 87 let cn: i64 = ad_read_all(path, cbuf, 500) 88 if cn <= 0 { return 0 } 89 return ad_contains(cbuf, cn, "nx_autorun_daemon" as *u8) 90} 91// ---- EVENT-DRIVEN trigger (replaces the time-based sleep): a sovereign FIFO. 92// The daemon blocks in read() on the FIFO -> ZERO compute until a producer pokes 93// it (nx_autorun_poke, a git post-commit, or the loop after authoring). inotify is 94// dead on /mnt/c (drvfs, empirically confirmed), so a FIFO on ext4 /tmp is the 95// reliable channel. "event driven not time": no clock, beats only on real events. 96const SYS_MKNODAT: i64 = 259 // x86-64 (the daemon runs on the WSL x86-64 host) 97const S_IFIFO_0666: i64 = 4534 // S_IFIFO (0o010000=4096) | 0o666 (438) 98const O_RDWR_FLAG: i64 = 2 // open the FIFO read+write so the daemon is always 99 // also a writer -> blocking read() never sees EOF 100 101// read the event-channel FIFO path from conf (F row); default if absent (rule 11). 102func ad_fifo_path(out: *u8) -> i64 { 103 let buf: *u8 = sys_mmap(SYS_MAGIC_4096) 104 let n: i64 = ad_read_all("knowledge/status/autorun_daemon.conf" as *u8, buf, SYS_MAGIC_4080) 105 var i: i64 = 0 106 while i < n { 107 var le: i64 = i 108 var scan: i64 = 1 109 while scan == 1 { if le >= n { scan = 0 } else { if buf[le] == (10 as u8) { scan = 0 } else { le = le + 1 } } } 110 if buf[i] == (70 as u8) { 111 var o: i64 = 0 112 var q: i64 = i + 2 113 while q < le { if buf[q] == (13 as u8) { q = le } else { out[o] = buf[q]; o = o + 1; q = q + 1 } } 114 out[o] = 0 as u8 115 if o > 0 { return 1 } 116 } 117 i = le + 1 118 } 119 let d: i64 = ad_cat(out, 0, "/tmp/nx_autorun.fifo" as *u8); out[d] = 0 as u8 120 return 0 121} 122 123// ensure the FIFO exists (idempotent; EEXIST harmless) and open it blocking R/W. 124func ad_open_event(path: *u8) -> i64 { 125 let mkr: i64 = __syscall(SYS_MKNODAT, AT_FDCWD, path as i64, S_IFIFO_0666, 0, 0, 0) 126 return __syscall(SYS_OPENAT, AT_FDCWD, path as i64, O_RDWR_FLAG, 0, 0, 0) 127} 128 129func ad_beat() -> i64 { 130 return adc_beat("knowledge/status/autorun_daemon.conf" as *u8, 131 "knowledge/status/autorun_daemon.out" as *u8, 132 "knowledge/status/autorun_daemon.log" as *u8) 133} 134func main() -> i64 { 135 let fifo: *u8 = sys_mmap(256) 136 ad_fifo_path(fifo) 137 // ATOMIC dup-guard (was: racy ad_already_running pidfile+/proc check -- a TOCTOU hole 138 // between fork and pidfile-write let two daemons coexist, hit 2026-06-13). O_EXCL create 139 // of a /tmp lockfile is ATOMIC: only ONE concurrent creator wins, the rest get EEXIST. 140 // Crash-staleness (the lockfile persists after a kill/crash) is reclaimed via the SAME 141 // /proc-liveness check the old guard used: pidfile owner alive -> real duplicate, exit; 142 // owner dead -> stale -> unlink + recreate (preserves the original auto-recovery). 143 let lockpath: *u8 = "/tmp/nx_autorun_daemon.lock" as *u8 144 var lockfd: i64 = ad_acquire_lock(lockpath) 145 if lockfd < 0 { 146 if ad_already_running() == 1 { _p("nx_autorun_daemon: ALREADY-RUNNING (pidfile live) -- not starting a duplicate\n" as *u8); sys_exit(0); return 0 } 147 __syscall(263, AT_FDCWD, lockpath as i64, 0, 0, 0, 0) // unlinkat: reclaim stale lock (x86-64 263 passthrough) 148 lockfd = ad_acquire_lock(lockpath) 149 if lockfd < 0 { _p("nx_autorun_daemon: ALREADY-RUNNING (reclaim race) -- not starting a duplicate\n" as *u8); sys_exit(0); return 0 } 150 } 151 let pid: i64 = sys_fork() 152 if pid != 0 { 153 _p("nx_autorun_daemon: ARMED pid=" as *u8); _pn(pid); _p(" mode=EVENT fifo=" as *u8); _p(fifo); _p(" (no timer; beats fire on poke -> knowledge/status/autorun_daemon.log)\n" as *u8) 154 let pfd: i64 = sys_openat_wr("knowledge/status/autorun_daemon.pid" as *u8, 0x1a4) 155 if pfd >= 0 { _fn(pfd, pid); sys_close(pfd) } 156 sys_exit(0) 157 return 0 158 } 159 nx_setsid() 160 let efd: i64 = ad_open_event(fifo) 161 if efd < 0 { sys_exit(1); return 1 } 162 // EVENT LOOP: block on the FIFO; each poke (>=1 byte) drives exactly one 163 // re-prove beat (adc_beat writes the AUTORUN-TGT rows + the AUTORUN-BEAT row). 164 // The blocking read is the whole point -- no busy-poll, no clock (rule 21). 165 let rbuf: *u8 = sys_mmap(64) 166 var go: i64 = 1 167 while go == 1 { 168 let r: i64 = sys_read(efd, rbuf, 64) 169 if r <= 0 { go = 0 } else { ad_beat() } 170 } 171 sys_exit(0) 172 return 0 173}