nx_portcheck.nx source
↩ module page · 365 lines · 19770 B
1// nx_portcheck.nx -- R1 of the sovereign-:443 arc: a SOVEREIGN "what owns this port" inspector. The deploy team
2// (nx_hostctl/nx_aw_hostctl) can start/stop/swap named daemons but has NO way to answer "what process is bound to
3// :443" -- the never-brick prerequisite before a sovereign terminator can take :443 from DSM (operator: "build them
4// up if they lack the capability, rung by rung; dont use shell"). This reads /proc sovereignly (syscalls only, no
5// ss/lsof/netstat): /proc/net/tcp{,6} for a LISTEN socket on the target port -> its inode; then /proc/<pid>/fd via
6// getdents64 + readlinkat(__syscall 267) to find the owning pid; then /proc/<pid>/comm for the process name.
7// license_tier: ORIGINAL expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_os_introspect.nx"
10const PC_MAGIC_32768: i64 = 32768
11const PC_MAGIC_53991: i64 = 53991
12
13const PC_TCP_LISTEN: i64 = 0x0A
14const PC_AT_FDCWD: i64 = 0 - 100
15const PC_SYS_READLINKAT: i64 = 267 // x86_64 readlinkat(dirfd, path, buf, bufsiz)
16
17func pc_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
18func pc_row(name: *u8, ok: i64) -> i64 { if ok == 1 { pc_w(" PASS " as *u8) } else { pc_w(" FAIL " as *u8) } pc_w(name); pc_w("\n" as *u8); return ok }
19func pc_is_space(c: i64) -> i64 { if c == 32 { return 1 } if c == 9 { return 1 } return 0 }
20func pc_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
21func pc_puti(v: i64) -> i64 { var t: i64=v; if t<0 { sys_write(1,"-" as *u8,1); t=0-t } let tm: *u8=sys_mmap(24); var k: i64=0; if t==0 {tm[0]=48 as u8;k=1} while t>0 {tm[k]=(48+(t%10)) as u8;t=t/10;k=k+1} let b: *u8=sys_mmap(24); var j: i64=0; while j<k {b[j]=tm[k-1-j];j=j+1} sys_write(1,b,k); return 0 }
22
23// parse a hex run [s,e) -> value
24func pc_hex(buf: *u8, s: i64, e: i64) -> i64 {
25 var v: i64 = 0; var i: i64 = s
26 while i < e {
27 let c: i64 = buf[i] & 0xff; var d: i64 = 0 - 1
28 if c >= 48 { if c <= 57 { d = c - 48 } }
29 if c >= 65 { if c <= 70 { d = c - 55 } }
30 if c >= 97 { if c <= 102 { d = c - 87 } }
31 if d < 0 { return v }
32 v = (v << 4) | d; i = i + 1
33 }
34 return v
35}
36// parse a decimal run [s,e) -> value
37func pc_dec(buf: *u8, s: i64, e: i64) -> i64 {
38 var v: i64 = 0; var i: i64 = s
39 while i < e { let c: i64 = buf[i] & 0xff; if c < 48 { return v } if c > 57 { return v } v = v*10 + (c-48); i = i + 1 }
40 return v
41}
42
43// field idx in [lstart,lend) (whitespace-delimited) -> sets fs/fe to its [start,end). returns 1/0.
44func pc_field(buf: *u8, lstart: i64, lend: i64, idx: i64, fs: *i64, fe: *i64) -> i64 {
45 var o: i64 = lstart; var f: i64 = 0
46 while o < lend {
47 while o < lend { if pc_is_space(buf[o] as i64) == 1 { o = o + 1 } else { break } } // skip leading spaces
48 if o >= lend { return 0 }
49 let st: i64 = o
50 while o < lend { if pc_is_space(buf[o] as i64) == 1 { break } else { o = o + 1 } } // field body
51 if f == idx { fs[0] = st; fe[0] = o; return 1 }
52 f = f + 1
53 }
54 return 0
55}
56
57// scan a /proc/net/tcp* file for a LISTEN socket on `port`; return its inode (0 = none).
58func pc_scan_file(path: *u8, port: i64) -> i64 {
59 let lb: *i64 = sys_mmap(8) as *i64
60 let data: *u8 = sys_read_file(path, lb)
61 if (data as i64) == 0 { return 0 }
62 let n: i64 = lb[0]
63 let fs: *i64 = sys_mmap(8) as *i64; let fe: *i64 = sys_mmap(8) as *i64
64 var ls: i64 = 0; var first: i64 = 1
65 while ls < n {
66 var le: i64 = ls
67 while le < n { if data[le] == (10 as u8) { break } else { le = le + 1 } }
68 if first == 1 { first = 0 } // skip the header row
69 else { if le > ls {
70 if pc_field(data, ls, le, 3, fs, fe) == 1 { // st
71 if pc_hex(data, fs[0], fe[0]) == PC_TCP_LISTEN {
72 if pc_field(data, ls, le, 1, fs, fe) == 1 { // local_address "IP:PORT"
73 var cp: i64 = fs[0]
74 while cp < fe[0] { if data[cp] == (58 as u8) { break } else { cp = cp + 1 } } // ':'
75 if cp < fe[0] {
76 if pc_hex(data, cp + 1, fe[0]) == port {
77 if pc_field(data, ls, le, 9, fs, fe) == 1 { return pc_dec(data, fs[0], fe[0]) } // inode
78 }
79 }
80 }
81 }
82 }
83 } }
84 ls = le + 1
85 }
86 return 0
87}
88// find the inode of a LISTEN socket on `port` across IPv4 + IPv6.
89func pc_listener_inode(port: i64) -> i64 {
90 let i4: i64 = pc_scan_file("/proc/net/tcp" as *u8, port)
91 if i4 > 0 { return i4 }
92 return pc_scan_file("/proc/net/tcp6" as *u8, port)
93}
94
95func pc_readlink(path: *u8, out: *u8, cap: i64) -> i64 {
96 let r: i64 = __syscall(PC_SYS_READLINKAT, PC_AT_FDCWD, path as i64, out as i64, cap, 0, 0)
97 if r < 0 { return 0 }
98 if r < cap { out[r] = 0 as u8 }
99 return r
100}
101func pc_str_all_digits(s: *u8) -> i64 { var i: i64 = 0; if s[0] == (0 as u8) { return 0 } while s[i] != (0 as u8) { let c: i64 = s[i] & 0xff; if c < 48 { return 0 } if c > 57 { return 0 } i = i + 1 } return 1 }
102func pc_join(a: *u8, b: *u8, c: *u8, d: *u8, out: *u8) -> i64 {
103 var o: i64 = 0
104 var i: i64 = 0; while a[i] != (0 as u8) { out[o] = a[i]; o = o+1; i = i+1 }
105 i = 0; while b[i] != (0 as u8) { out[o] = b[i]; o = o+1; i = i+1 }
106 i = 0; while c[i] != (0 as u8) { out[o] = c[i]; o = o+1; i = i+1 }
107 i = 0; while d[i] != (0 as u8) { out[o] = d[i]; o = o+1; i = i+1 }
108 out[o] = 0 as u8; return o
109}
110func pc_join3(a: *u8, b: *u8, c: *u8, out: *u8) -> i64 {
111 var o: i64 = 0
112 var i: i64 = 0; while a[i] != (0 as u8) { out[o]=a[i]; o=o+1; i=i+1 }
113 i=0; while b[i] != (0 as u8) { out[o]=b[i]; o=o+1; i=i+1 }
114 i=0; while c[i] != (0 as u8) { out[o]=c[i]; o=o+1; i=i+1 }
115 out[o]=0 as u8; return o
116}
117// does /proc/<piddir>/fd contain a symlink to socket:[inode]? returns 1/0.
118func pc_pid_has_inode(piddir: *u8, inode: i64) -> i64 {
119 let fdpath: *u8 = sys_mmap(256); pc_join3("/proc/" as *u8, piddir, "/fd" as *u8, fdpath)
120 let fd: i64 = sys_openat_rd(fdpath)
121 if fd < 0 { return 0 }
122 let buf: *u8 = sys_mmap(PC_MAGIC_32768); let lnk: *u8 = sys_mmap(256); let full: *u8 = sys_mmap(320)
123 var found: i64 = 0
124 var rn: i64 = sys_getdents64(fd, buf, PC_MAGIC_32768)
125 while rn > 0 {
126 var off: i64 = 0
127 while off < rn {
128 let rec: *u8 = (buf as i64 + off) as *u8
129 let nm: *u8 = dirent_name(rec)
130 if pc_str_all_digits(nm) == 1 {
131 pc_join("/proc/" as *u8, piddir, "/fd/" as *u8, nm, full)
132 let ln: i64 = pc_readlink(full, lnk, 256)
133 if ln > 8 {
134 // match "socket:[" prefix then the inode digits
135 if lnk[0]==(115 as u8) { if lnk[7]==(91 as u8) { // 's' ... '['
136 var k: i64 = 8; while k < ln { if lnk[k] == (93 as u8) { break } else { k = k+1 } }
137 if pc_dec(lnk, 8, k) == inode { found = 1 }
138 } }
139 }
140 }
141 let rl: i64 = dirent_reclen(rec); if rl <= 0 { off = rn } else { off = off + rl }
142 }
143 if found == 1 { rn = 0 } else { rn = sys_getdents64(fd, buf, PC_MAGIC_32768) }
144 }
145 sys_close(fd)
146 return found
147}
148// scan /proc/<pid> for the process owning `inode`; return the pid (0 = none).
149func pc_inode_to_pid(inode: i64) -> i64 {
150 let fd: i64 = sys_openat_rd("/proc" as *u8)
151 if fd < 0 { return 0 }
152 let buf: *u8 = sys_mmap(PC_MAGIC_32768)
153 var pid: i64 = 0
154 var rn: i64 = sys_getdents64(fd, buf, PC_MAGIC_32768)
155 while rn > 0 {
156 var off: i64 = 0
157 while off < rn {
158 let rec: *u8 = (buf as i64 + off) as *u8
159 let nm: *u8 = dirent_name(rec)
160 if pc_str_all_digits(nm) == 1 {
161 if pc_pid_has_inode(nm, inode) == 1 { pid = pc_dec(nm, 0, pc_strlen(nm)) }
162 }
163 let rl: i64 = dirent_reclen(rec); if rl <= 0 { off = rn } else { off = off + rl }
164 }
165 if pid > 0 { rn = 0 } else { rn = sys_getdents64(fd, buf, PC_MAGIC_32768) }
166 }
167 sys_close(fd)
168 return pid
169}
170func pc_pid_comm(pid: i64, out: *u8, cap: i64) -> i64 {
171 let ps: *u8 = sys_mmap(24); var t: i64 = pid; var k: i64 = 0
172 if t == 0 { ps[0]=48 as u8; k=1 } let tmp: *u8 = sys_mmap(24)
173 while t > 0 { tmp[k] = (48 + (t%10)) as u8; t=t/10; k=k+1 }
174 var j: i64 = 0; while j < k { ps[j] = tmp[k-1-j]; j=j+1 } ps[k] = 0 as u8
175 let path: *u8 = sys_mmap(64); pc_join3("/proc/" as *u8, ps, "/comm" as *u8, path)
176 let lb: *i64 = sys_mmap(8) as *i64
177 let d: *u8 = sys_read_file(path, lb)
178 if (d as i64) == 0 { out[0]=0 as u8; return 0 }
179 var n: i64 = lb[0]; if n > 0 { if d[n-1]==(10 as u8) { n = n-1 } }
180 var i: i64 = 0; while i < n { if i < cap-1 { out[i]=d[i] } i=i+1 } out[n]=0 as u8
181 return n
182}
183
184// ===================== in-process gate: bind a listener, find it, map it to US =====================
185func pc_legacy_gate() -> i64 {
186 pc_w("nx_portcheck gate -- sovereign 'what owns this port' via /proc (no ss/lsof/netstat); R1 sovereign-:443\n" as *u8)
187 let TESTPORT: i64 = PC_MAGIC_53991
188
189 // bind a real LISTEN socket on 127.0.0.1:TESTPORT
190 let fd: i64 = sys_socket(2, 1, 0)
191 if fd < 0 { pc_w("SOCKET FAIL\n" as *u8); sys_exit(1) }
192 let opt: *u8 = sys_mmap(4); opt[0]=1 as u8; sys_setsockopt(fd, 1, 2, opt, 4)
193 let addr: *u8 = sys_mmap(16)
194 addr[0]=2 as u8; addr[1]=0 as u8
195 addr[2]=((TESTPORT>>8)&0xff) as u8; addr[3]=(TESTPORT&0xff) as u8
196 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
197 var z: i64=8; while z<16 { addr[z]=0 as u8; z=z+1 }
198 if sys_bind(fd, addr, 16) < 0 { pc_w("BIND FAIL\n" as *u8); sys_exit(1) }
199 if sys_listen(fd, 4) < 0 { pc_w("LISTEN FAIL\n" as *u8); sys_exit(1) }
200
201 var pass: i64 = 0
202
203 // T1: the listener is found in /proc/net/tcp with a real inode
204 let inode: i64 = pc_listener_inode(TESTPORT)
205 var t1: i64 = 0; if inode > 0 { t1 = 1 }
206 pass = pass + pc_row("T1 LISTEN socket on the test port found in /proc/net/tcp (inode resolved)" as *u8, t1)
207
208
209 // T2: the inode maps to an owning pid
210 let pid: i64 = pc_inode_to_pid(inode)
211 var t2: i64 = 0; if pid > 0 { t2 = 1 }
212 pass = pass + pc_row("T2 inode -> owning pid via /proc/*/fd readlinkat" as *u8, t2)
213
214 // T3: that pid is US (readlinkat /proc/self), i.e. the mapping is CORRECT not arbitrary
215 let selfl: *u8 = sys_mmap(64); pc_readlink("/proc/self" as *u8, selfl, 64)
216 let selfpid: i64 = pc_dec(selfl, 0, pc_strlen(selfl))
217 var t3: i64 = 0; if pid == selfpid { if selfpid > 0 { t3 = 1 } }
218 pass = pass + pc_row("T3 owning pid == /proc/self (the mapping found THIS process, not a wrong one)" as *u8, t3)
219
220 // T4: the pid resolves to a process name
221 let comm: *u8 = sys_mmap(64); let cn: i64 = pc_pid_comm(pid, comm, 64)
222 var t4: i64 = 0; if cn > 0 { t4 = 1 }
223 pass = pass + pc_row("T4 pid -> process name via /proc/<pid>/comm" as *u8, t4)
224
225 // T5: a port with NO listener resolves to inode 0 (no false positive)
226 var t5: i64 = 0; if pc_listener_inode(TESTPORT + 1) == 0 { t5 = 1 }
227 pass = pass + pc_row("T5 an unbound port -> inode 0 (no false positive)" as *u8, t5)
228
229 pc_w(" owner: pid=" as *u8); let pb: *u8 = sys_mmap(24); var tp: i64=pid; var pk: i64=0
230 if tp==0 { pb[0]=48 as u8; pk=1 } let pt: *u8=sys_mmap(24); while tp>0 { pt[pk]=(48+(tp%10)) as u8; tp=tp/10; pk=pk+1 }
231 var pj: i64=0; while pj<pk { pb[pj]=pt[pk-1-pj]; pj=pj+1 } sys_write(1, pb, pk)
232 pc_w(" comm=" as *u8); sys_write(1, comm, cn); pc_w("\n" as *u8)
233 sys_close(fd)
234
235 if pass == 5 {
236 pc_w("NX-PORTCHECK GATE GREEN 5/5 (sovereign port->inode->pid->comm; ready to confirm who owns :443 on the NAS)\n" as *u8)
237 sys_exit(0)
238 }
239 pc_w("NX-PORTCHECK GATE RED\n" as *u8)
240 sys_exit(1)
241 return 1
242}
243
244// Additive structured observer; no registration, signal, bind or restart side effects.
245const PCL_EXIT_USAGE: i64 = 3
246const PCL_EXIT_OUTPUT: i64 = 4
247const PCL_INTEGER_BYTES: i64 = 21
248const PCL_I64_MIN: i64 = 0-9223372036854775807-1
249static pcl_output_failed: i64
250
251func pcl_raw(s: *u8, n: i64) -> i64 {
252 if pcl_output_failed != 0 { return 0-1 }
253 var done: i64=0
254 while done < n {
255 let wrote: i64=sys_write(1,((s as i64)+done) as *u8,n-done)
256 if wrote <= 0 { pcl_output_failed=1; return 0-1 }
257 done=done+wrote
258 }; return 0
259}
260func pcl_text(s: *u8) -> i64 { return pcl_raw(s,pc_strlen(s)) }
261func pcl_number(value: i64) -> i64 {
262 if value == PCL_I64_MIN { return pcl_text("-9223372036854775808" as *u8) }
263 let out: *u8=sys_mmap(PCL_INTEGER_BYTES)
264 if (out as i64) <= 0 { pcl_output_failed=1; return 0-1 }
265 var n: i64=0; var magnitude: i64=value
266 if magnitude < 0 { out[0]=45 as u8; n=1; magnitude=0-magnitude }
267 n=pon_obs_number(out,n,magnitude)
268 let result: i64=pcl_raw(out,n); sys_munmap(out,PCL_INTEGER_BYTES); return result
269}
270func pcl_equal(a: *u8, b: *u8) -> i64 {
271 var i: i64=0
272 while a[i] == b[i] { if a[i] == (0 as u8) { return 1 }; i=i+1 }; return 0
273}
274func pcl_status(status: i64) -> *u8 {
275 if status == PON_OBS_COMPLETE { return "COMPLETE" as *u8 }
276 if status == PON_OBS_UNKNOWN { return "UNKNOWN" as *u8 }
277 if status == PON_OBS_CAPACITY { return "CAPACITY" as *u8 }
278 return "BAD_INPUT" as *u8
279}
280func pcl_stage(stage: i64) -> *u8 {
281 if stage == PON_L_STAGE_INPUT { return "input" as *u8 }
282 if stage == PON_L_STAGE_OPEN { return "open" as *u8 }
283 if stage == PON_L_STAGE_READ { return "read" as *u8 }
284 if stage == PON_L_STAGE_PARSE { return "parse" as *u8 }
285 if stage == PON_L_STAGE_ROWS { return "rows" as *u8 }
286 if stage == PON_L_STAGE_CLOSE { return "close" as *u8 }
287 if stage == PON_L_STAGE_ALLOC { return "allocation" as *u8 }
288 return "none" as *u8
289}
290func pcl_refuse(code: *u8) -> i64 {
291 // `code` is always a literal chosen below; no untrusted argument is interpolated.
292 pcl_text("{\"schema\":\"nishi.port-listeners\",\"v\":1,\"status\":\"BAD_INPUT\",\"complete\":false,\"error\":{\"code\":\"" as *u8)
293 pcl_text(code)
294 pcl_text("\",\"stage\":\"input\"},\"usage\":\"nx_portcheck listeners <port> <row_capacity> <read_capacity_bytes>\",\"rows\":[]}\n" as *u8)
295 if pcl_output_failed != 0 { return PCL_EXIT_OUTPUT }; return PCL_EXIT_USAGE
296}
297func pcl_emit(port: i64, row_capacity: i64, read_capacity: i64, rows: *i64, addresses: *u8, st: *i64, begin: i64, end: i64) -> i64 {
298 let status: i64=st[PON_L_STATUS]
299 pcl_text("{\"schema\":\"nishi.port-listeners\",\"v\":1,\"status\":\"" as *u8); pcl_text(pcl_status(status))
300 pcl_text("\",\"complete\":" as *u8)
301 if status == PON_OBS_COMPLETE { pcl_text("true" as *u8) } else { pcl_text("false" as *u8) }
302 pcl_text(",\"error\":" as *u8)
303 if status == PON_OBS_COMPLETE { pcl_text("null" as *u8) } else {
304 pcl_text("{\"code\":\"" as *u8); pcl_text(pcl_status(status)); pcl_text("\",\"first_native_error\":" as *u8); pcl_number(st[PON_L_FIRST_ERROR])
305 pcl_text(",\"first_stage\":\"" as *u8); pcl_text(pcl_stage(st[PON_L_FIRST_STAGE])); pcl_text("\",\"count\":" as *u8); pcl_number(st[PON_L_ERRORS]); pcl_text("}" as *u8)
306 }
307 pcl_text(",\"request\":{\"port\":" as *u8); pcl_number(port); pcl_text(",\"row_capacity\":" as *u8); pcl_number(row_capacity)
308 pcl_text(",\"read_capacity_bytes_per_file\":" as *u8); pcl_number(read_capacity); pcl_text("}" as *u8)
309 pcl_text(",\"evidence\":{\"observer\":\"nx_os_introspect.pon_observe_listeners\",\"scope\":\"tcp_listen_rows_in_current_network_namespace\",\"sources\":[\"/proc/net/tcp\",\"/proc/net/tcp6\"],\"atomic_snapshot\":false,\"process_identity_verified\":false,\"restart_authority\":false,\"observation_start_unix\":" as *u8)
310 if begin > 0 { pcl_number(begin) } else { pcl_text("null" as *u8) }
311 pcl_text(",\"observation_end_unix\":" as *u8)
312 if end > 0 { pcl_number(end) } else { pcl_text("null" as *u8) }
313 pcl_text(",\"clock\":\"realtime_seconds\",\"fully_read_files\":" as *u8); pcl_number(st[PON_L_FILES_COMPLETE])
314 pcl_text(",\"bytes_read\":" as *u8); pcl_number(st[PON_L_BYTES]); pcl_text(",\"data_rows_examined\":" as *u8); pcl_number(st[PON_L_ROWS]); pcl_text("}" as *u8)
315 pcl_text(",\"matching_rows\":" as *u8); pcl_number(st[PON_L_MATCHES]); pcl_text(",\"returned_rows\":" as *u8); pcl_number(st[PON_L_STORED])
316 pcl_text(",\"address_encoding\":\"linux_proc_net_tcp_kernel_hex_not_normalized_ip\",\"rows\":[" as *u8)
317 var i: i64=0
318 while i < st[PON_L_STORED] {
319 if i > 0 { pcl_text("," as *u8) }
320 let r: i64=i*PON_L_ROW_SLOTS
321 pcl_text("{\"family\":\"" as *u8)
322 if rows[r+PON_L_FAMILY] == PON_L_FAMILY4 { pcl_text("ipv4" as *u8) } else { pcl_text("ipv6" as *u8) }
323 pcl_text("\",\"port\":" as *u8); pcl_number(rows[r+PON_L_PORT]); pcl_text(",\"inode\":" as *u8); pcl_number(rows[r+PON_L_INODE])
324 // The shared parser proved this span contains only exactly8/32 hex bytes.
325 pcl_text(",\"address_hex\":\"" as *u8); pcl_raw(((addresses as i64)+i*PON_L_ADDRESS_STRIDE) as *u8,rows[r+PON_L_ADDRESS_N]); pcl_text("\"}" as *u8)
326 i=i+1
327 }
328 pcl_text("]}\n" as *u8)
329 if pcl_output_failed != 0 { return PCL_EXIT_OUTPUT }
330 return status
331}
332func pcl_listeners(argc: i64, argv: *i64) -> i64 {
333 pcl_output_failed=0
334 if argc != 5 { return pcl_refuse("usage_listeners" as *u8) }
335 let port: i64=pon_obs_uint(argv[2] as *u8,0,pc_strlen(argv[2] as *u8))
336 let row_capacity: i64=pon_obs_uint(argv[3] as *u8,0,pc_strlen(argv[3] as *u8))
337 let read_capacity: i64=pon_obs_uint(argv[4] as *u8,0,pc_strlen(argv[4] as *u8))
338 if port <= 0 || port > PON_L_PORT_MAX { return pcl_refuse("invalid_port" as *u8) }
339 if row_capacity < 0 || row_capacity > PON_OBS_I64_MAX/PON_L_ADDRESS_STRIDE || row_capacity > PON_OBS_I64_MAX/(PON_L_ROW_SLOTS*PON_OBS_I64_BYTES) { return pcl_refuse("invalid_row_capacity" as *u8) }
340 if read_capacity <= 0 || read_capacity >= PON_OBS_I64_MAX/2 { return pcl_refuse("invalid_read_capacity" as *u8) }
341 let st: *i64=sys_mmap(PON_L_STATE_SLOTS*PON_OBS_I64_BYTES) as *i64
342 if (st as i64) <= 0 { pcl_text("{\"schema\":\"nishi.port-listeners\",\"v\":1,\"status\":\"UNKNOWN\",\"complete\":false,\"error\":{\"code\":\"allocation_failed\",\"stage\":\"allocation\"},\"rows\":[]}\n" as *u8); if pcl_output_failed != 0 { return PCL_EXIT_OUTPUT }; return PON_OBS_UNKNOWN }
343 pon_listener_init(st)
344 var rows: *i64=0 as *i64; var addresses: *u8=0 as *u8
345 if row_capacity > 0 {
346 rows=sys_mmap(row_capacity*PON_L_ROW_SLOTS*PON_OBS_I64_BYTES) as *i64
347 addresses=sys_mmap(row_capacity*PON_L_ADDRESS_STRIDE)
348 }
349 let begin: i64=sys_now_realtime_sec()
350 if row_capacity > 0 && ((rows as i64) <= 0 || (addresses as i64) <= 0) {
351 pon_listener_error(st,PON_L_STAGE_ALLOC,PON_OBS_E_ALLOC,0)
352 } else { pon_observe_listeners(port,rows,addresses,row_capacity,read_capacity,st) }
353 let end: i64=sys_now_realtime_sec()
354 let result: i64=pcl_emit(port,row_capacity,read_capacity,rows,addresses,st,begin,end)
355 if (rows as i64) > 0 { sys_munmap(rows as *u8,row_capacity*PON_L_ROW_SLOTS*PON_OBS_I64_BYTES) }
356 if (addresses as i64) > 0 { sys_munmap(addresses,row_capacity*PON_L_ADDRESS_STRIDE) }
357 sys_munmap(st as *u8,PON_L_STATE_SLOTS*PON_OBS_I64_BYTES)
358 return result
359}
360func main(argc: i64, argv: *i64) -> i64 {
361 if argc == 1 { return pc_legacy_gate() }
362 if sys_ignore_sigpipe() != 0 { return PCL_EXIT_OUTPUT }
363 if argc > 1 { if pcl_equal(argv[1] as *u8,"listeners" as *u8) == 1 { return pcl_listeners(argc,argv) } }
364 pcl_output_failed=0; return pcl_refuse("unknown_command" as *u8)
365}