nx_state_spool.nx source
↩ module page · 63 lines · 3064 B
1// nx_state_spool.nx -- CLI of the store-and-forward state spool (CR-R1a; NAS=hub, laptop=worker:
2// sync to the NAS whenever reachable, go local when it isn't, drain on reconnect).
3// usage:
4// nx_state_spool spool <spoolfile> <line> append one state line locally (flock'd)
5// nx_state_spool drain <spoolfile> <transport_elf> deliver each line via transport (argv[1]=
6// line, exit 0=delivered); failures KEPT
7// lock = <spoolfile>.lock, tmp = <spoolfile>.tmp (atomic rewrite). Exit 0 on success (kept>0 is
8// NOT an error -- offline is a normal state); 2 = usage; 3 = spool/lock failure.
9// license_tier: ORIGINAL expect_exit: 0
10import "nx_syscalls.nx"
11import "nx_state_spool_core.nx"
12import "nx_itoa_lib.nx" // shared MSB-first emitter; declared explicitly rather than leaned on transitively
13const K_MAGIC_1024: i64 = 1024
14
15func sp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
16
17// MIGRATED to the shared emitter 2026-08-06 (debt 1785516350). The old body mmapped a 32-byte
18// scratch and never freed it -- at page granularity that is 4096B leaked PER CALL, and this is the
19// exact clone shape nx_itoa_lib's header quotes as the reason ~87 sites hand-rolled their own.
20// nxi_out runs the SAME MSB-first ccz_cat_num digits through a shim that always frees, so the
21// emitted bytes are identical including the sign; only the leak goes away.
22func sp_putn(v: i64) -> i64 { nxi_out(v); return 0 }
23
24func sp_streq(a: *u8, b: *u8) -> i64 {
25 var i: i64 = 0
26 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
27 if b[i] != (0 as u8) { return 0 }
28 return 1
29}
30
31func main(argc: i64, argv: *i64) -> i64 {
32 if argc < 4 { sp_puts("usage: nx_state_spool spool <file> <line> | drain <file> <transport_elf>\n" as *u8); return 2 }
33 let sub: *u8 = argv[1] as *u8
34 let spool: *u8 = argv[2] as *u8
35 let lock: *u8 = sys_mmap(K_MAGIC_1024)
36 var o: i64 = 0
37 o = ccz_cat_str(lock, o, spool)
38 o = ccz_cat_str(lock, o, ".lock" as *u8)
39 if sp_streq(sub, "spool" as *u8) == 1 {
40 let r: i64 = ssp_spool(spool, lock, argv[3] as *u8)
41 if r < 0 { sp_puts("SSP spool-failed\n" as *u8); return 3 }
42 sp_puts("SSP spooled\n" as *u8)
43 return 0
44 }
45 if sp_streq(sub, "drain" as *u8) == 1 {
46 let tmp: *u8 = sys_mmap(K_MAGIC_1024)
47 var t: i64 = 0
48 t = ccz_cat_str(tmp, t, spool)
49 t = ccz_cat_str(tmp, t, ".tmp" as *u8)
50 let sent: *i64 = sys_mmap(16) as *i64
51 let kept: *i64 = sys_mmap(16) as *i64
52 let r: i64 = ssp_drain(spool, lock, tmp, argv[3] as *u8, sent, kept)
53 if r < 0 { sp_puts("SSP drain-failed (lock/tmp)\n" as *u8); return 3 }
54 sp_puts("SSP drain sent=" as *u8)
55 sp_putn(sent[0])
56 sp_puts(" kept=" as *u8)
57 sp_putn(kept[0])
58 sp_puts("\n" as *u8)
59 return 0
60 }
61 sp_puts("usage: nx_state_spool spool <file> <line> | drain <file> <transport_elf>\n" as *u8)
62 return 2
63}