nx_torrent_daemon.nx source
↩ module page · 1473 lines · 88825 B
1// nx_torrent_daemon.nx -- the UI enabler (X-TORRENT-UI-001): an HTTP daemon that
2// serves the operator's torrent page and wires it to the LIVE download worker.
3//
4// GET /torrent -> sites/nishifamily/torrent/index.html
5// GET /torrent/app.js -> the page's last-mile JS (renders REAL state only)
6// GET /torrent/api/me -> {"user":"local"} (local single-operator)
7// POST /torrent/api/login -> {"ok":true}
8// POST /torrent/api/add {magnet} -> fork+execve the worker elf -> {"id":"1"}
9// GET /torrent/api/status -> reads outdir/download.status -> {downloads:[...]}
10// POST /torrent/api/<id>/<action> -> {"ok":true} (pause/resume/remove noop)
11//
12// Sovereign: composes nx_http_server (bind/listen/accept) + the worker elf the team
13// already built. NO mocked numbers -- /status emits exactly what the worker wrote to
14// disk; before any download the worker file is absent -> {downloads:[]} (honest empty).
15// license_tier: ORIGINAL kind: last-mile-ui-bridge layer: L6 (over L4 http + L5 worker)
16//
17// module: nishi-core.torrent.daemon
18// depends: nishi-core.http_server, nishi-core.syscalls
19import "nx_http_server.nx"
20import "nx_syscalls.nx"
21import "nx_itoa_lib.nx" // shared MSB-first emitter: nxi_buf is the NUL-free buffer form
22import "nx_bencode.nx" // parse download.meta to locate the video file's byte-range for streaming
23import "nx_torrent_access.nx" // R2: deny-by-default AREA policy (SFW=media family+, NSFW=gallery owner-only)
24import "nx_ioadmit_lib.nx" // the I/O-storm admission ruler shared with nx_build_admit: ask before adding D-state
25const TORRENT_MAGIC_131072: i64 = 131072
26const TORRENT_MAGIC_1024: i64 = 1024
27const TORRENT_MAGIC_8192: i64 = 8192
28const TORRENT_MAGIC_262144: i64 = 262144
29const TORRENT_MAGIC_4000: i64 = 4000
30const TORRENT_MAGIC_2000: i64 = 2000
31const TORRENT_MAGIC_1048576: i64 = 1048576
32const TORRENT_MAGIC_4096: i64 = 4096
33const TORRENT_MAGIC_4095: i64 = 4095
34const TORRENT_MAGIC_16384: i64 = 16384
35const TORRENT_MAGIC_4200: i64 = 4200
36const TORRENT_MAGIC_8097: i64 = 8097
37const TORRENT_MAGIC_1000000000: i64 = 1000000000
38
39// R3 auth: the OPAQUE session->level resolution lives in the FRONT GATEWAY (runtime/_hdl_build/nx_torrent_authn,
40// proven by nx_torrent_authn_gate 14/14) -- NOT in this daemon, which stays LEAN (the full OPAQUE stack won't
41// co-compile here -> empty .s). The gateway authenticates, resolves the level, and injects it as a TRUSTED
42// internal header X-Nishi-Level (Cardinal 12: validate at the boundary, trust internal). LOCAL marker = the
43// operator's own box (a hub push never carries this workstation-local file) -> OWNER.
44const TORRENT_LOCAL_MARKER: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents/.local_operator"
45
46// PERSISTENT RETRY (rung #2): how often the detached sweeper re-spawns workers for STUCK partials (a
47// worker gives up after ~20min of no new pieces, then the torrent sits idle). 10 min = tracker-friendly
48// re-announce cadence that still grabs a seed soon after it appears. Data-driven (Cardinal 11).
49const D_RETRY_INTERVAL_MS: i64 = 600000
50
51// ---- I/O ADMISSION FOR WORKER SPAWNS (2026-08-19) --------------------------------------------------
52// MEASURED: the daemon resumed EVERY active torrent at once (startup + every retry sweep), with no budget
53// and no resource awareness. On an 8-CPU box that put 5 nx_torrent_get workers into D-state on the RAID,
54// 5 of the 8 blocked processes that tripped nx_build_admit's I/O-storm witness and refused every lane's
55// builds for days (load 12-17; store_put/fsync paths queued behind the same array). A RAID5 array gains
56// NOTHING from parallel piece-writers -- aggregate throughput is network-bound -- so parallel workers
57// buy only contention. EVERY spawn path (add, dedup-resume, resume, reannounce, the retry sweep) now
58// goes through d_spawn_worker, which asks the SAME ruler the build gate asks (nx_ioadmit_lib):
59// budget = min( storm-line headroom from /proc/stat , max_workers(conf) - live workers )
60// A spawn the budget refuses is DEFERRED, never lost: the torrent stays `active` in its registry, the
61// retry sweep re-asks every D_RETRY_INTERVAL_MS, and the sweep ROTATES its start index per pass so a
62// small budget cannot starve the tail of the registry. The conf `<owner-root>/torrent_admit.conf` row
63// `max_workers N` is the operator's concurrency ceiling; absent -> the storm witness alone governs;
64// /proc/stat unreadable -> the conf ceiling alone; neither observable -> D_UNOBS_SPAWNS per ask
65// (conservative, never unbounded -- the same stance as nx_build_admit's BA_NCPU_FALLBACK).
66// Every ask prints `TORRENTD io-admit ...` so the effect is MEASURABLE in the daemon log, not asserted.
67const D_ADMIT_CONF: *u8 = "/torrent_admit.conf"
68const D_ADMIT_CONF_CAP: i64 = 4096 // a few `key value` rows; rm_read reports a full cap as its length
69const D_ADMIT_KEY_MAXW: *u8 = "max_workers"
70const D_WORKER_COMM: *u8 = "nx_torrent_get.sov.elf" // matched on its first IOA_COMM_MAX bytes, as the kernel truncates comm
71const D_UNOBS_SPAWNS: i64 = 1
72const D_QUEUED_STATUS: *u8 = "state=queued-io\n" // written ONLY when no status file exists yet (a first add that was deferred)
73// bud[] cells shared across one sweep (or one request): remaining budget, ncpu, blocked, live workers,
74// max_workers (-1 = no conf), spawned, deferred. Named so a reader never decodes indexes.
75const D_BUD_LEFT: i64 = 0
76const D_BUD_NCPU: i64 = 1
77const D_BUD_BLK: i64 = 2
78const D_BUD_LIVE: i64 = 3
79const D_BUD_MAXW: i64 = 4
80const D_BUD_SPAWNED: i64 = 5
81const D_BUD_DEFERRED: i64 = 6
82const D_BUD_CELLS: i64 = 8
83const D_BUD_BYTES: i64 = 64 // D_BUD_CELLS x 8
84
85// ---- small buffer-building helpers -------------------------------
86func d_strlen(s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){i=i+1} return i }
87
88func d_puts(dst: *u8, off: i64, s: *u8) -> i64 {
89 var i: i64 = 0
90 while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 }
91 return off + i
92}
93// MIGRATED to nxi_buf (debt 1785557603). The old body mmapped a reverse buffer per call and never
94// freed it -- 21 call sites in a LONG-LIVED daemon, so the leak was per-call. It also carried the
95// documented clone SIGN BUG: it did m = 0 - m for negatives but NEVER WROTE THE MINUS, so every
96// negative printed as positive. nxi_buf is MSB-first, allocates nothing, emits the sign (gate T9/T10)
97// and keeps this exact contract: digits only, NO NUL, returns off+len.
98func d_putn(dst: *u8, off: i64, v: i64) -> i64 { return nxi_buf(dst, off, v) }
99
100// read a whole file into buf (bounded by cap); returns bytes read or -1 if open fails
101func d_read_file(path: *u8, buf: *u8, cap: i64) -> i64 {
102 let fd: i64 = sys_openat_rd(path)
103 if fd < 0 { return 0 - 1 }
104 var off: i64 = 0; var go: i64 = 1
105 while go == 1 {
106 if off >= cap { go = 0 } else {
107 let r: i64 = sys_read(fd, buf + off, cap - off)
108 if r <= 0 { go = 0 } else { off = off + r }
109 }
110 }
111 sys_close(fd)
112 return off
113}
114
115// ---- THE REGISTRY IS READ WHOLE OR NOT AT ALL (2026-08-19) --------------------------------------------
116// Every reader of <root>/torrents.idx used d_read_file with a 64 KiB cap: past ~1,600 entries the tail
117// torrents silently vanished from /status, resume and by-btih lookup, and d_idx_remove REWROTE the registry
118// from that truncated prefix -- a delete that would destroy every entry past 64 KiB. This loader composes
119// sys_read_file, which sizes its buffer from the file (lseek END) and cannot short-read; lenout[0] is the
120// byte count, and the buffer is freed by the caller with sys_free_file (the sweeper is a long-lived process
121// -- a per-sweep mapping that is never unmapped is the VmSize-balloon class this estate has already paid
122// for). EMPTY OR ABSENT -> NULL + 0 *before* sys_read_file is asked: for a zero-size file that primitive
123// falls back to its 4 GiB special-file reserve (debt 1787076780), which a daemon must never map per sweep.
124// st_size lives at offset 48 of the x86_64 stat buffer (the same layout this file already reads st_mtim
125// from at 88).
126const D_STAT_SIZE_OFF: i64 = 48
127func d_idx_load(idxpath: *u8, lenout: *i64) -> *u8 {
128 lenout[0] = 0
129 let stb: *u8 = sys_mmap(160)
130 if sys_fstatat(idxpath, stb) != 0 { return 0 as *u8 }
131 let szp: *i64 = (((stb as i64) + D_STAT_SIZE_OFF)) as *i64
132 if szp[0] <= 0 { return 0 as *u8 }
133 let b: *u8 = sys_read_file(idxpath, lenout)
134 if (b as i64) == 0 { lenout[0] = 0 }
135 return b
136}
137func d_idx_free(b: *u8, n: i64) -> i64 { if (b as i64) != 0 { sys_free_file(b, n) } return 0 }
138
139// build outdir+suffix into out (NUL-terminated); returns out. (status/control/magnet sidecars)
140func d_path(outdir: *u8, suf: *u8, out: *u8) -> *u8 {
141 var o: i64 = d_puts(out, 0, outdir)
142 o = d_puts(out, o, suf)
143 out[o] = 0 as u8
144 return out
145}
146// write n bytes to path (O_CREAT|O_WRONLY|O_TRUNC) -- the control + magnet sidecar files
147func d_write_file(path: *u8, s: *u8, n: i64) -> i64 {
148 let fd: i64 = sys_openat_wr(path, 0x1a4)
149 if fd < 0 { return 0 - 1 }
150 sys_write(fd, s, n)
151 sys_close(fd)
152 return 0
153}
154
155// ---- HTTP response writer (header + body, then close) ------------
156func d_send(cfd: i64, status: *u8, ctype: *u8, body: *u8, blen: i64) -> i64 {
157 let h: *u8 = sys_mmap(512)
158 var o: i64 = 0
159 o = d_puts(h, o, "HTTP/1.1 " as *u8); o = d_puts(h, o, status)
160 o = d_puts(h, o, "\r\nContent-Type: " as *u8); o = d_puts(h, o, ctype)
161 o = d_puts(h, o, "\r\nContent-Length: " as *u8); o = d_putn(h, o, blen)
162 o = d_puts(h, o, "\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8)
163 sys_write(cfd, h, o)
164 if blen > 0 { sys_write(cfd, body, blen) }
165 sys_close(cfd)
166 return 0
167}
168func d_send_str(cfd: i64, status: *u8, ctype: *u8, s: *u8) -> i64 {
169 return d_send(cfd, status, ctype, s, d_strlen(s))
170}
171func d_serve_file(cfd: i64, path: *u8, ctype: *u8) -> i64 {
172 let buf: *u8 = sys_mmap(TORRENT_MAGIC_131072)
173 let n: i64 = d_read_file(path, buf, TORRENT_MAGIC_131072)
174 if n <= 0 { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "missing\n" as *u8); return 0 }
175 d_send(cfd, "200 OK" as *u8, ctype, buf, n)
176 return 0
177}
178
179// ---- path matching on the parsed request -------------------------
180func d_streq(buf: *u8, off: i64, len: i64, s: *u8) -> i64 {
181 var i: i64 = 0
182 while s[i] != (0 as u8) {
183 if i >= len { return 0 }
184 if buf[off+i] != s[i] { return 0 }
185 i = i + 1
186 }
187 if i != len { return 0 }
188 return 1
189}
190func d_starts(buf: *u8, off: i64, len: i64, s: *u8) -> i64 {
191 var i: i64 = 0
192 while s[i] != (0 as u8) {
193 if i >= len { return 0 }
194 if buf[off+i] != s[i] { return 0 }
195 i = i + 1
196 }
197 return 1
198}
199
200// ---- magnet extraction: copy the "magnet:..." value out of the JSON body ----
201// finds the byte run m a g n e t : (the value; the key "magnet" is followed by a quote)
202// and copies to the next double-quote. Returns length.
203func d_extract_magnet(body: *u8, blen: i64, out: *u8, ocap: i64) -> i64 {
204 var i: i64 = 0; var start: i64 = 0 - 1
205 while i + 7 <= blen {
206 if body[i]==(109 as u8) { if body[i+1]==(97 as u8) { if body[i+2]==(103 as u8) { if body[i+3]==(110 as u8) { if body[i+4]==(101 as u8) { if body[i+5]==(116 as u8) { if body[i+6]==(58 as u8) {
207 start = i; i = blen + 100
208 }}}}}}}
209 i = i + 1
210 }
211 if start < 0 { return 0 }
212 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
213 while go == 1 {
214 if j >= blen { go = 0 } else {
215 if body[j]==(34 as u8) { go = 0 } else {
216 if k < ocap - 1 { out[k] = body[j]; k = k + 1 }
217 j = j + 1
218 }
219 }
220 }
221 out[k] = 0 as u8
222 return k
223}
224
225// ---- spawn the worker elf (double-fork so it reparents to init: no zombie) ----
226// has this torrent already made progress? (non-empty donefile) -> re-add should RESUME, not truncate.
227func d_has_progress(outdir: *u8) -> i64 {
228 let dp: *u8 = sys_mmap(640); var p: i64 = d_puts(dp, 0, outdir); p = d_puts(dp, p, "/download.done" as *u8); dp[p] = 0 as u8
229 let fd: i64 = sys_openat_rd(dp); if fd < 0 { return 0 }
230 let b: *u8 = sys_mmap(8); let r: i64 = sys_read(fd, b, 1); sys_close(fd)
231 if r > 0 { return 1 }
232 return 0
233}
234// Before exec in a forked child: close every fd the daemon held open (the listen socket + the
235// per-connection cfd + any transient fds). A long-lived worker must NEVER keep the client's
236// connection open -- if it did, an HTTP client that reads to EOF (rather than honoring
237// Content-Length) blocks until the whole download ends. The daemon's own gate hung exactly this
238// way. stdio (0,1,2 -> /dev/null) is kept; sys_close on an unopened fd returns EBADF, so the
239// blind 3..255 sweep is safe. This is the no-hang law extended to the client side.
240func d_close_inherited() -> i64 {
241 var fdx: i64 = 3
242 while fdx < 256 { sys_close(fdx); fdx = fdx + 1 }
243 return 0
244}
245// The directory THIS daemon binary lives in, read from /proc/self/cmdline argv[0]. The spawn elfs (worker,
246// discover, hls, video, ts2fmp4) are deployed as its SIBLINGS, so they resolve correctly on BOTH the dev box
247// (/tmp, where the WOMB builds them) and the NAS hub (/volume1/ai/torrent, where the bundle is deployed AND
248// /tmp is mounted NOEXEC -- the reason the old hardcoded /tmp exec failed on the NAS). Fallback "/tmp".
249func d_self_dir() -> *u8 {
250 let buf: *u8 = sys_mmap(TORRENT_MAGIC_1024)
251 let fd: i64 = sys_openat_rd("/proc/self/cmdline" as *u8)
252 var n: i64 = 0
253 if fd >= 0 { n = sys_read(fd, buf, 1023); sys_close(fd) }
254 if n <= 0 { let fb: *u8 = sys_mmap(8); fb[0]=47 as u8; fb[1]=116 as u8; fb[2]=109 as u8; fb[3]=112 as u8; fb[4]=0 as u8; return fb }
255 var z: i64 = 0; var dz: i64 = 0
256 while dz == 0 { if z >= n { dz = 1 } else { if buf[z] == (0 as u8) { dz = 1 } else { z = z + 1 } } }
257 var i: i64 = z - 1; var df: i64 = 0
258 while df == 0 { if i <= 0 { df = 1 } else { if buf[i] == (47 as u8) { buf[i] = 0 as u8; df = 1 } else { i = i - 1 } } }
259 if buf[0] == (0 as u8) { let fb: *u8 = sys_mmap(8); fb[0]=47 as u8; fb[1]=116 as u8; fb[2]=109 as u8; fb[3]=112 as u8; fb[4]=0 as u8; return fb }
260 return buf
261}
262// Measure once per sweep/request: fills bud[] (see D_BUD_*) and returns the spawn budget.
263func d_admit_budget(owner_root: *u8, bud: *i64) -> i64 {
264 let m: *i64 = sys_mmap(32) as *i64
265 let rc: i64 = ioa_measure(m)
266 var wb: i64 = IOA_UNREADABLE
267 if rc == 0 { wb = ioa_spawn_budget(m[0], m[1], IOA_BLOCKED_PER_CPU, IOA_RESERVE_SLOTS) }
268 let cp: *u8 = d_path(owner_root, D_ADMIT_CONF, sys_mmap(700))
269 let cb: *u8 = sys_mmap(D_ADMIT_CONF_CAP)
270 let cn: i64 = rm_read(cp, cb, D_ADMIT_CONF_CAP)
271 var maxw: i64 = 0 - 1
272 if cn > 0 { maxw = rm_conf(cb, cn, D_ADMIT_KEY_MAXW, 0 - 1) }
273 let live: i64 = ioa_count_comm_roots(D_WORKER_COMM) // ROOT workers only: a worker forks handlers that keep its comm (measured: 2 workers read as 63 when the tree was counted)
274 var cbud: i64 = 0 - 1
275 if maxw >= 0 { if live >= 0 { cbud = maxw - live; if cbud < 0 { cbud = 0 } } }
276 var b: i64 = D_UNOBS_SPAWNS
277 if wb >= 0 { b = wb }
278 if cbud >= 0 { if wb >= 0 { if cbud < b { b = cbud } } else { b = cbud } }
279 bud[D_BUD_LEFT] = b
280 bud[D_BUD_NCPU] = m[0]
281 bud[D_BUD_BLK] = m[1]
282 bud[D_BUD_LIVE] = live
283 bud[D_BUD_MAXW] = maxw
284 bud[D_BUD_SPAWNED] = 0
285 bud[D_BUD_DEFERRED] = 0
286 return b
287}
288// One log line per ask, so the admission is MEASURABLE in /tmp/torrentd.log (nx_status tails it).
289func d_admit_log(tag: *u8, bud: *i64) -> i64 {
290 let lb: *u8 = sys_mmap(256)
291 var o: i64 = d_puts(lb, 0, "TORRENTD io-admit " as *u8)
292 o = d_puts(lb, o, tag)
293 o = d_puts(lb, o, " ncpu=" as *u8); o = d_putn(lb, o, bud[D_BUD_NCPU])
294 o = d_puts(lb, o, " blocked=" as *u8); o = d_putn(lb, o, bud[D_BUD_BLK])
295 o = d_puts(lb, o, " live_workers=" as *u8); o = d_putn(lb, o, bud[D_BUD_LIVE])
296 o = d_puts(lb, o, " max_workers=" as *u8); o = d_putn(lb, o, bud[D_BUD_MAXW])
297 o = d_puts(lb, o, " budget_left=" as *u8); o = d_putn(lb, o, bud[D_BUD_LEFT])
298 o = d_puts(lb, o, " spawned=" as *u8); o = d_putn(lb, o, bud[D_BUD_SPAWNED])
299 o = d_puts(lb, o, " deferred=" as *u8); o = d_putn(lb, o, bud[D_BUD_DEFERRED])
300 lb[o] = 10 as u8
301 sys_write(1, lb, o + 1)
302 return 0
303}
304// The ONE spawn chokepoint. bud[] must come from d_admit_budget (shared across a sweep, fresh per request).
305// Returns 0 = spawned, 1 = DEFERRED (budget exhausted; the registry still says active, the sweep re-asks).
306func d_spawn_worker(magnet: *u8, outdir: *u8, resume: i64, bud: *i64) -> i64 {
307 if bud[D_BUD_LEFT] <= 0 {
308 let sp: *u8 = d_path(outdir, "/download.status" as *u8, sys_mmap(640))
309 let sfd: i64 = sys_openat_rd(sp)
310 if sfd < 0 { d_write_file(sp, D_QUEUED_STATUS, d_strlen(D_QUEUED_STATUS)) } else { sys_close(sfd) }
311 bud[D_BUD_DEFERRED] = bud[D_BUD_DEFERRED] + 1
312 return 1
313 }
314 bud[D_BUD_LEFT] = bud[D_BUD_LEFT] - 1
315 bud[D_BUD_SPAWNED] = bud[D_BUD_SPAWNED] + 1
316 let wpath: *u8 = d_path(d_self_dir(), "/nx_torrent_get.sov.elf" as *u8, sys_mmap(640))
317 let argv: *i64 = sys_mmap(64) as *i64
318 argv[0] = wpath as i64; argv[1] = magnet as i64; argv[2] = outdir as i64
319 if resume == 1 { argv[3] = "r" as i64; argv[4] = 0 } else { argv[3] = 0 }
320 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
321 let p1: i64 = sys_fork()
322 if p1 == 0 {
323 let p2: i64 = sys_fork()
324 if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) }
325 sys_exit(0)
326 }
327 let st: *i64 = sys_mmap(16) as *i64
328 sys_wait4(p1, st, 0)
329 return 0
330}
331// Request-path spawn (add / dedup-resume / resume / reannounce): one fresh measurement, one ask, one log line.
332func d_spawn_admitted(magnet: *u8, outdir: *u8, resume: i64, owner_root: *u8, tag: *u8) -> i64 {
333 let bud: *i64 = sys_mmap(D_BUD_BYTES) as *i64
334 d_admit_budget(owner_root, bud)
335 let r: i64 = d_spawn_worker(magnet, outdir, resume, bud)
336 d_admit_log(tag, bud)
337 return r
338}
339
340// ---- /status: parse the worker's newline-delimited status file -> JSON ----
341func d_find_val(buf: *u8, n: i64, key: *u8) -> i64 {
342 let kl: i64 = d_strlen(key)
343 var i: i64 = 0
344 while i + kl <= n {
345 var j: i64 = 0; var ok: i64 = 1
346 while j < kl { if buf[i+j] != key[j] { ok = 0; j = kl } else { j = j + 1 } }
347 if ok == 1 { return i + kl }
348 i = i + 1
349 }
350 return 0 - 1
351}
352func d_int_at(buf: *u8, n: i64, pos: i64) -> i64 {
353 if pos < 0 { return 0 }
354 var v: i64 = 0; var i: i64 = pos; var go: i64 = 1
355 while go == 1 {
356 if i >= n { go = 0 } else {
357 let c: i64 = buf[i] as i64
358 if c >= 48 { if c <= 57 { v = v*10 + (c-48); i = i+1 } else { go = 0 } } else { go = 0 }
359 }
360 }
361 return v
362}
363func d_copy_line(buf: *u8, n: i64, pos: i64, out: *u8, ocap: i64) -> i64 {
364 out[0] = 0 as u8
365 if pos < 0 { return 0 }
366 var i: i64 = pos; var k: i64 = 0; var go: i64 = 1
367 while go == 1 {
368 if i >= n { go = 0 } else {
369 if buf[i]==(10 as u8) { go = 0 } else {
370 if k < ocap-1 { out[k]=buf[i]; k=k+1 }
371 i = i+1
372 }
373 }
374 }
375 out[k] = 0 as u8
376 return k
377}
378func d_status(cfd: i64, statpath: *u8, ctlpath: *u8) -> i64 {
379 // operator control overrides (file-based, written by pause/remove): removed -> empty list,
380 // paused -> force the rendered state to "paused" (the worker has self-stopped).
381 let cb: *u8 = sys_mmap(16); let cn: i64 = d_read_file(ctlpath, cb, 15)
382 var removed: i64 = 0; var paused: i64 = 0
383 if cn > 0 { let c0: i64 = cb[0] as i64; if c0 == 114 { removed = 1 } if c0 == 112 { paused = 1 } }
384 let buf: *u8 = sys_mmap(TORRENT_MAGIC_8192)
385 let n: i64 = d_read_file(statpath, buf, TORRENT_MAGIC_8192)
386 let out: *u8 = sys_mmap(TORRENT_MAGIC_8192)
387 var o: i64 = 0
388 if removed == 1 {
389 o = d_puts(out, o, "{\"downloads\":[]}" as *u8)
390 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o); return 0
391 }
392 if n <= 0 {
393 o = d_puts(out, o, "{\"downloads\":[]}" as *u8)
394 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o); return 0
395 }
396 let total: i64 = d_int_at(buf, n, d_find_val(buf, n, "total=" as *u8))
397 let done: i64 = d_int_at(buf, n, d_find_val(buf, n, "done=" as *u8))
398 let have: i64 = d_int_at(buf, n, d_find_val(buf, n, "have=" as *u8))
399 let pieces: i64= d_int_at(buf, n, d_find_val(buf, n, "pieces=" as *u8))
400 let peers: i64 = d_int_at(buf, n, d_find_val(buf, n, "peers=" as *u8))
401 let stateb: *u8 = sys_mmap(64); d_copy_line(buf, n, d_find_val(buf, n, "state=" as *u8), stateb, 64)
402 if paused == 1 { var pz: i64 = 0; let ps2: *u8 = "paused" as *u8; while ps2[pz] != (0 as u8) { stateb[pz] = ps2[pz]; pz = pz + 1 } stateb[pz] = 0 as u8 }
403 let nameb: *u8 = sys_mmap(512); d_copy_line(buf, n, d_find_val(buf, n, "name=" as *u8), nameb, 512)
404 var vpct: i64 = 0; if pieces > 0 { vpct = have*100/pieces }
405 o = d_puts(out, o, "{\"downloads\":[{\"id\":\"1\",\"name\":\"" as *u8)
406 o = d_puts(out, o, nameb)
407 o = d_puts(out, o, "\",\"size\":" as *u8); o = d_putn(out, o, total)
408 o = d_puts(out, o, ",\"done\":" as *u8); o = d_putn(out, o, done)
409 o = d_puts(out, o, ",\"peers\":" as *u8); o = d_putn(out, o, peers)
410 o = d_puts(out, o, ",\"havePieces\":" as *u8); o = d_putn(out, o, have)
411 o = d_puts(out, o, ",\"totalPieces\":" as *u8); o = d_putn(out, o, pieces)
412 o = d_puts(out, o, ",\"verified\":" as *u8); o = d_putn(out, o, vpct)
413 o = d_puts(out, o, ",\"status\":\"" as *u8); o = d_puts(out, o, stateb)
414 o = d_puts(out, o, "\"}]}" as *u8)
415 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
416 return 0
417}
418
419// ---- discover: extract a page URL from the JSON body ----
420func d_extract_url(body: *u8, blen: i64, out: *u8, ocap: i64) -> i64 {
421 var i: i64 = 0; var start: i64 = 0 - 1
422 while i + 4 <= blen {
423 if body[i]==(104 as u8) { if body[i+1]==(116 as u8) { if body[i+2]==(116 as u8) { if body[i+3]==(112 as u8) {
424 start = i; i = blen + 100
425 }}}}
426 i = i + 1
427 }
428 if start < 0 { return 0 }
429 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
430 while go == 1 {
431 if j >= blen { go = 0 } else {
432 let c: i64 = body[j] as i64
433 if c==34 { go=0 } else { if c==39 { go=0 } else { if c==32 { go=0 } else { if c==60 { go=0 } else { if c==62 { go=0 } else { if c==10 { go=0 } else { if c==13 { go=0 } else {
434 if k < ocap - 1 { out[k] = body[j]; k = k + 1 }
435 j = j + 1
436 }}}}}}}
437 }
438 }
439 out[k] = 0 as u8
440 return k
441}
442
443// spawn the discovery elf (double-fork, reparents to init)
444func d_spawn_discover(url: *u8, dpath: *u8) -> i64 {
445 let wpath: *u8 = d_path(d_self_dir(), "/nx_magnet_discover.sov.elf" as *u8, sys_mmap(640))
446 let argv: *i64 = sys_mmap(64) as *i64
447 argv[0] = wpath as i64; argv[1] = url as i64; argv[2] = dpath as i64; argv[3] = 0
448 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
449 let p1: i64 = sys_fork()
450 if p1 == 0 {
451 let p2: i64 = sys_fork()
452 if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) }
453 sys_exit(0)
454 }
455 let st: *i64 = sys_mmap(16) as *i64
456 sys_wait4(p1, st, 0)
457 return 0
458}
459
460// Build a same-origin Referer ("https://<host>/") from a media URL. Most CDNs gate direct .mp4 links on a
461// Referer header; the downloader already sends a browser UA, so an EMPTY referer is the #1 reason a
462// correctly-routed direct video still 403s. out = NUL-terminated referer (empty if the URL has no scheme).
463func d_url_referer(url: *u8, out: *u8) -> *u8 {
464 var i: i64 = 0; var n: i64 = 0; while url[n] != (0 as u8) { n = n + 1 }
465 var sce: i64 = 0 - 1
466 while i + 3 <= n { if url[i]==(58 as u8) { if url[i+1]==(47 as u8) { if url[i+2]==(47 as u8) { sce = i + 3; i = n + 100 } } } i = i + 1 }
467 if sce < 0 { out[0] = 0 as u8; return out }
468 var he: i64 = sce; var hgo: i64 = 1
469 while hgo == 1 { if he >= n { hgo = 0 } else { let c: i64 = url[he] as i64; if c==47 { hgo=0 } else { if c==58 { hgo=0 } else { if c==63 { hgo=0 } else { he = he + 1 } } } } }
470 var o: i64 = 0; var k: i64 = 0
471 while k < he { out[o] = url[k]; o = o + 1; k = k + 1 }
472 out[o] = 47 as u8; o = o + 1
473 out[o] = 0 as u8
474 return out
475}
476
477// spawn the sovereign video downloader (double-fork). argv: elf, url, outpath, referer.
478func d_spawn_video(url: *u8, outpath: *u8, referer: *u8) -> i64 {
479 let wpath: *u8 = d_path(d_self_dir(), "/nx_video_get.sov.elf" as *u8, sys_mmap(640))
480 let argv: *i64 = sys_mmap(64) as *i64
481 argv[0] = wpath as i64; argv[1] = url as i64; argv[2] = outpath as i64; argv[3] = referer as i64; argv[4] = 0
482 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
483 let p1: i64 = sys_fork()
484 if p1 == 0 { let p2: i64 = sys_fork(); if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) } sys_exit(0) }
485 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(p1, st, 0)
486 return 0
487}
488// spawn the sovereign HLS downloader (double-fork). argv: elf, m3u8-url, outpath(.ts), referer.
489func d_spawn_hls(url: *u8, outpath: *u8, referer: *u8) -> i64 {
490 let wpath: *u8 = d_path(d_self_dir(), "/nx_hls_get.sov.elf" as *u8, sys_mmap(640))
491 let argv: *i64 = sys_mmap(64) as *i64
492 argv[0] = wpath as i64; argv[1] = url as i64; argv[2] = outpath as i64; argv[3] = referer as i64; argv[4] = 0
493 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
494 let p1: i64 = sys_fork()
495 if p1 == 0 { let p2: i64 = sys_fork(); if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) } sys_exit(0) }
496 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(p1, st, 0)
497 return 0
498}
499// is this URL an HLS playlist? (path ends with .m3u8, case-insensitive, before any '?')
500func d_is_hls(url: *u8) -> i64 {
501 var n: i64 = 0; while url[n] != (0 as u8) { n = n + 1 }
502 var e: i64 = n; var i: i64 = 0
503 while i < n { if url[i] == (63 as u8) { e = i; i = n } else { i = i + 1 } } // stop at '?'
504 if e < 5 { return 0 }
505 let s: i64 = e - 5
506 if url[s] != (46 as u8) { return 0 } // '.'
507 var c0: i64 = url[s+1] as i64; if c0 >= 65 { if c0 <= 90 { c0 = c0 + 32 } }
508 var c1: i64 = url[s+2] as i64; if c1 >= 65 { if c1 <= 90 { c1 = c1 + 32 } }
509 var c2: i64 = url[s+3] as i64; if c2 >= 65 { if c2 <= 90 { c2 = c2 + 32 } }
510 var c3: i64 = url[s+4] as i64; if c3 >= 65 { if c3 <= 90 { c3 = c3 + 32 } }
511 if c0 != 109 { return 0 } // m
512 if c1 != 51 { return 0 } // 3
513 if c2 != 117 { return 0 } // u
514 if c3 != 56 { return 0 } // 8
515 return 1
516}
517// HLS output name: basename (up to the first '.') sanitized, then a forced .ts (stitched MPEG-TS).
518func d_hls_outname(url: *u8, outdir: *u8, out: *u8) -> *u8 {
519 var ulen: i64 = 0; while url[ulen] != (0 as u8) { ulen = ulen + 1 }
520 var qend: i64 = ulen; var qi: i64 = 0; while qi < ulen { if url[qi] == (63 as u8) { qend = qi; qi = ulen } else { qi = qi + 1 } }
521 var seg: i64 = 0; var si: i64 = 0; while si < qend { if url[si] == (47 as u8) { seg = si + 1 } si = si + 1 }
522 var o: i64 = d_puts(out, 0, outdir); o = d_puts(out, o, "/" as *u8)
523 var nz: i64 = 0; var k: i64 = seg; var stop: i64 = 0
524 while k < qend { if stop == 0 {
525 let c: i64 = url[k] as i64
526 if c == 46 { stop = 1 } else { var ok: i64 = 0; if c>=48 { if c<=57 { ok=1 } } if c>=65 { if c<=90 { ok=1 } } if c>=97 { if c<=122 { ok=1 } } if c==45 { ok=1 } if c==95 { ok=1 } if ok==1 { out[o]=url[k]; o=o+1; nz=nz+1 } }
527 } k = k + 1 }
528 if nz == 0 { o = d_puts(out, o, "video" as *u8) }
529 o = d_puts(out, o, ".ts" as *u8)
530 out[o] = 0 as u8
531 return out
532}
533// derive outdir/<basename> from a video URL (last path segment before '?'; sanitized; fallback video.mp4).
534func d_vidname(url: *u8, outdir: *u8, out: *u8) -> *u8 {
535 var ulen: i64 = 0; while url[ulen] != (0 as u8) { ulen = ulen + 1 }
536 var qend: i64 = ulen; var qi: i64 = 0; while qi < ulen { if url[qi] == (63 as u8) { qend = qi; qi = ulen } else { qi = qi + 1 } }
537 var seg: i64 = 0; var si: i64 = 0; while si < qend { if url[si] == (47 as u8) { seg = si + 1 } si = si + 1 }
538 var o: i64 = d_puts(out, 0, outdir); o = d_puts(out, o, "/" as *u8)
539 var nz: i64 = 0; var k: i64 = seg
540 while k < qend { let c: i64 = url[k] as i64; var ok: i64 = 0; if c>=48 { if c<=57 { ok=1 } } if c>=65 { if c<=90 { ok=1 } } if c>=97 { if c<=122 { ok=1 } } if c==46 { ok=1 } if c==45 { ok=1 } if c==95 { ok=1 } if ok==1 { out[o]=url[k]; o=o+1; nz=nz+1 } k=k+1 }
541 if nz == 0 { o = d_puts(out, o, "video.mp4" as *u8) }
542 out[o] = 0 as u8
543 return out
544}
545
546func d_scan_to(buf: *u8, n: i64, from: i64, delim: i64) -> i64 {
547 var i: i64 = from
548 while i < n { if (buf[i] as i64) == delim { return i } i = i + 1 }
549 return n
550}
551func d_json_buf(dst: *u8, off: i64, src: *u8, from: i64, to: i64) -> i64 {
552 var i: i64 = from
553 while i < to {
554 let c: i64 = src[i] as i64
555 if c==34 { dst[off]=92 as u8; off=off+1; dst[off]=34 as u8; off=off+1 }
556 else { if c==92 { dst[off]=92 as u8; off=off+1; dst[off]=92 as u8; off=off+1 }
557 else { dst[off]=src[i]; off=off+1 } }
558 i = i + 1
559 }
560 return off
561}
562func d_raw_buf(dst: *u8, off: i64, src: *u8, from: i64, to: i64) -> i64 {
563 if from >= to { dst[off]=48 as u8; return off+1 }
564 var i: i64 = from; while i < to { dst[off]=src[i]; off=off+1; i=i+1 }
565 return off
566}
567// GET /discover_result -> read discover.tsv (+ .status) -> JSON
568func d_discover_result(cfd: i64, dpath: *u8) -> i64 {
569 let sp: *u8 = sys_mmap(TORRENT_MAGIC_1024); var i: i64 = 0; while dpath[i]!=(0 as u8){sp[i]=dpath[i];i=i+1}
570 let suf: *u8 = ".status" as *u8; var j: i64 = 0; while suf[j]!=(0 as u8){sp[i]=suf[j];i=i+1;j=j+1} sp[i]=0 as u8
571 let sbuf: *u8 = sys_mmap(TORRENT_MAGIC_1024); let sn: i64 = d_read_file(sp, sbuf, TORRENT_MAGIC_1024)
572 let stateb: *u8 = sys_mmap(64); stateb[0]=0 as u8
573 var scount: i64 = 0; var sbytes: i64 = 0
574 if sn > 0 {
575 d_copy_line(sbuf, sn, d_find_val(sbuf, sn, "state=" as *u8), stateb, 64)
576 scount = d_int_at(sbuf, sn, d_find_val(sbuf, sn, "count=" as *u8))
577 sbytes = d_int_at(sbuf, sn, d_find_val(sbuf, sn, "bytes=" as *u8))
578 } else { let df: *u8 = "fetching" as *u8; var z: i64 = 0; while df[z]!=(0 as u8){stateb[z]=df[z];z=z+1} stateb[z]=0 as u8 }
579 let tbuf: *u8 = sys_mmap(TORRENT_MAGIC_262144); let tn: i64 = d_read_file(dpath, tbuf, TORRENT_MAGIC_262144)
580 let out: *u8 = sys_mmap(TORRENT_MAGIC_262144); var o: i64 = 0
581 o = d_puts(out, o, "{\"state\":\"" as *u8); o = d_json_buf(out, o, stateb, 0, d_strlen(stateb))
582 o = d_puts(out, o, "\",\"count\":" as *u8); o = d_putn(out, o, scount)
583 o = d_puts(out, o, ",\"bytes\":" as *u8); o = d_putn(out, o, sbytes)
584 o = d_puts(out, o, ",\"magnets\":[" as *u8)
585 if tn > 0 {
586 var p: i64 = 0; var first: i64 = 1; var guard: i64 = 0
587 while p < tn {
588 if guard > TORRENT_MAGIC_4000 { p = tn } else {
589 guard = guard + 1
590 let a: i64 = d_scan_to(tbuf, tn, p, 9)
591 let b: i64 = d_scan_to(tbuf, tn, a+1, 9)
592 let c: i64 = d_scan_to(tbuf, tn, b+1, 9)
593 let e: i64 = d_scan_to(tbuf, tn, c+1, 10)
594 if a < tn {
595 if first == 0 { o = d_puts(out, o, "," as *u8) } first = 0
596 o = d_puts(out, o, "{\"hash\":\"" as *u8); o = d_json_buf(out, o, tbuf, p, a)
597 o = d_puts(out, o, "\",\"name\":\"" as *u8); o = d_json_buf(out, o, tbuf, a+1, b)
598 o = d_puts(out, o, "\",\"trackers\":" as *u8); o = d_raw_buf(out, o, tbuf, b+1, c)
599 o = d_puts(out, o, ",\"magnet\":\"" as *u8); o = d_json_buf(out, o, tbuf, c+1, e)
600 o = d_puts(out, o, "\"}" as *u8)
601 }
602 p = e + 1
603 }
604 }
605 }
606 o = d_puts(out, o, "]}" as *u8)
607 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
608 return 0
609}
610
611// ============ MULTI-TORRENT: per-info-hash isolation + a registry ============
612// Each torrent lives in outdir/<btih>/download.* (the worker already takes its outdir).
613// outdir/torrents.idx is a newline-list of active <btih> ids. No clobber between torrents.
614
615// extract the btih value from a magnet ("...btih:<id>&..." or end) -> the per-torrent id (hex/base32)
616func d_extract_btih(mag: *u8, n: i64, out: *u8, ocap: i64) -> i64 {
617 var i: i64 = 0; var start: i64 = 0 - 1
618 while i + 5 <= n {
619 if mag[i]==(98 as u8) { if mag[i+1]==(116 as u8) { if mag[i+2]==(105 as u8) { if mag[i+3]==(104 as u8) { if mag[i+4]==(58 as u8) {
620 start = i + 5; i = n + 100
621 }}}}}
622 i = i + 1
623 }
624 if start < 0 { return 0 }
625 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
626 while go == 1 {
627 if j >= n { go = 0 } else { if mag[j]==(38 as u8) { go = 0 } else {
628 if k < ocap - 1 { out[k] = mag[j]; k = k + 1 }
629 j = j + 1
630 } }
631 }
632 out[k] = 0 as u8
633 return k
634}
635// hex char -> 0..15 (else -1), for %XX url-decode in dn= names
636func d_hexv(c: i64) -> i64 {
637 if c >= 48 { if c <= 57 { return c - 48 } }
638 if c >= 97 { if c <= 102 { return c - 97 + 10 } }
639 if c >= 65 { if c <= 70 { return c - 65 + 10 } }
640 return 0 - 1
641}
642// extract dn= (display name) from the magnet, url-decode (%XX, '+'), make filesystem+URL-safe -> out.
643// Keeps [A-Za-z0-9._-]; every run of other chars collapses to a single '_' (never leading). So the user
644// sees a recognizable folder ("javdb.com_326FCT-218") in Downloads instead of a raw info-hash. 0 = no dn=.
645func d_extract_name(mag: *u8, n: i64, out: *u8, ocap: i64) -> i64 {
646 var i: i64 = 0; var start: i64 = 0 - 1
647 while i + 3 <= n {
648 if mag[i]==(100 as u8) { if mag[i+1]==(110 as u8) { if mag[i+2]==(61 as u8) { start = i + 3; i = n + 100 } } } // 'd','n','='
649 i = i + 1
650 }
651 if start < 0 { return 0 }
652 var j: i64 = start; var k: i64 = 0; var pendu: i64 = 0; var go: i64 = 1
653 while go == 1 {
654 if j >= n { go = 0 } else { if mag[j]==(38 as u8) { go = 0 } else {
655 var c: i64 = mag[j] as i64
656 if c == 37 { if j + 2 < n { let hi: i64 = d_hexv(mag[j+1] as i64); let lo: i64 = d_hexv(mag[j+2] as i64); if hi >= 0 { if lo >= 0 { c = hi*16+lo; j = j + 2 } } } }
657 if c == 43 { c = 32 }
658 var keep: i64 = 0
659 if c >= 48 { if c <= 57 { keep = 1 } }
660 if c >= 65 { if c <= 90 { keep = 1 } }
661 if c >= 97 { if c <= 122 { keep = 1 } }
662 if c == 46 { keep = 1 }
663 if c == 45 { keep = 1 }
664 if c == 95 { keep = 1 }
665 if keep == 1 {
666 if pendu == 1 { if k < ocap - 1 { out[k] = 95 as u8; k = k + 1 } pendu = 0 }
667 if k < ocap - 1 { out[k] = c as u8; k = k + 1 }
668 } else { if k > 0 { pendu = 1 } }
669 j = j + 1
670 } }
671 }
672 out[k] = 0 as u8
673 return k
674}
675// build outdir/<id> into dir (NUL-terminated); returns dir.
676func d_subdir(outdir: *u8, id: *u8, dir: *u8) -> *u8 {
677 var p: i64 = d_puts(dir, 0, outdir); p = d_puts(dir, p, "/" as *u8); p = d_puts(dir, p, id); dir[p] = 0 as u8
678 return dir
679}
680func d_ends(buf: *u8, off: i64, len: i64, suf: *u8) -> i64 {
681 let sl: i64 = d_strlen(suf)
682 if sl > len { return 0 }
683 var i: i64 = 0
684 while i < sl { if buf[off+len-sl+i] != suf[i] { return 0 } i = i + 1 }
685 return 1
686}
687// is id one of the newline-delimited lines in buf[0..n)?
688func d_idx_has(buf: *u8, n: i64, id: *u8) -> i64 {
689 let idl: i64 = d_strlen(id)
690 var i: i64 = 0
691 while i < n {
692 let e: i64 = d_scan_to(buf, n, i, 10)
693 if (e - i) == idl { var j: i64 = 0; var m: i64 = 1; while j < idl { if buf[i+j] != id[j] { m = 0; j = idl } else { j = j + 1 } } if m == 1 { return 1 } }
694 i = e + 1
695 }
696 return 0
697}
698// add id to the registry if absent (rewrite the small file; idempotent)
699func d_idx_add(idxpath: *u8, id: *u8) -> i64 {
700 // best-effort dedup, then ATOMIC O_APPEND -- not read-modify-write. Two simultaneous adds used to
701 // both rewrite the whole file, losing one entry (the gate's false-RED); O_APPEND can't clobber.
702 let nl: *i64 = sys_mmap(16) as *i64; let buf: *u8 = d_idx_load(idxpath, nl); let n: i64 = nl[0]
703 if n > 0 { if d_idx_has(buf, n, id) == 1 { d_idx_free(buf, n); return 0 } }
704 d_idx_free(buf, n)
705 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, idxpath as i64, O_WRONLY_CA, 0x1a4, 0, 0)
706 if fd < 0 { return 0 - 1 }
707 let line: *u8 = sys_mmap(160); var k: i64 = 0; while id[k] != (0 as u8) { line[k] = id[k]; k = k + 1 } line[k] = 10 as u8; k = k + 1
708 sys_write(fd, line, k)
709 sys_close(fd)
710 return 0
711}
712// drop id's line from the registry (rewrite without it)
713func d_idx_remove(idxpath: *u8, id: *u8) -> i64 {
714 let nl: *i64 = sys_mmap(16) as *i64; let buf: *u8 = d_idx_load(idxpath, nl); let n: i64 = nl[0]; if n <= 0 { return 0 }
715 let idl: i64 = d_strlen(id)
716 let out: *u8 = sys_mmap(n + 16); var o: i64 = 0 // a rewrite is never larger than the registry it came from
717 var i: i64 = 0
718 while i < n {
719 let e: i64 = d_scan_to(buf, n, i, 10)
720 let len: i64 = e - i
721 var keep: i64 = 1
722 if len == idl { var j: i64 = 0; var m: i64 = 1; while j < idl { if buf[i+j] != id[j] { m = 0; j = idl } else { j = j + 1 } } if m == 1 { keep = 0 } }
723 if keep == 1 { if len > 0 { var c: i64 = i; while c < e { out[o] = buf[c]; o = o + 1; c = c + 1 } out[o] = 10 as u8; o = o + 1 } }
724 i = e + 1
725 }
726 d_write_file(idxpath, out, o)
727 return 0
728}
729// append ONE Download JSON object (id + its dir's status, control-overridden) to out at o0; return new o.
730func d_status_one(out: *u8, o0: i64, id: *u8, dir: *u8) -> i64 {
731 var o: i64 = o0
732 let statpath: *u8 = d_path(dir, "/download.status" as *u8, sys_mmap(640))
733 let ctl2: *u8 = d_path(dir, "/download.control" as *u8, sys_mmap(640))
734 let cb: *u8 = sys_mmap(16); let cn: i64 = d_read_file(ctl2, cb, 15)
735 var paused: i64 = 0; if cn > 0 { if (cb[0] as i64) == 112 { paused = 1 } }
736 let buf: *u8 = sys_mmap(TORRENT_MAGIC_8192); let n: i64 = d_read_file(statpath, buf, TORRENT_MAGIC_8192)
737 let total: i64 = d_int_at(buf, n, d_find_val(buf, n, "total=" as *u8))
738 let done: i64 = d_int_at(buf, n, d_find_val(buf, n, "done=" as *u8))
739 let have: i64 = d_int_at(buf, n, d_find_val(buf, n, "have=" as *u8))
740 let pieces: i64= d_int_at(buf, n, d_find_val(buf, n, "pieces=" as *u8))
741 let peers: i64 = d_int_at(buf, n, d_find_val(buf, n, "peers=" as *u8))
742 let stateb: *u8 = sys_mmap(64); d_copy_line(buf, n, d_find_val(buf, n, "state=" as *u8), stateb, 64)
743 if n <= 0 { var z: i64 = 0; let s0: *u8 = "started" as *u8; while s0[z] != (0 as u8) { stateb[z] = s0[z]; z = z + 1 } stateb[z] = 0 as u8 }
744 if paused == 1 { var z2: i64 = 0; let s1: *u8 = "paused" as *u8; while s1[z2] != (0 as u8) { stateb[z2] = s1[z2]; z2 = z2 + 1 } stateb[z2] = 0 as u8 }
745 let nameb: *u8 = sys_mmap(512); d_copy_line(buf, n, d_find_val(buf, n, "name=" as *u8), nameb, 512)
746 if n <= 0 { var z3: i64 = 0; let s2: *u8 = "resolving" as *u8; while s2[z3] != (0 as u8) { nameb[z3] = s2[z3]; z3 = z3 + 1 } nameb[z3] = 0 as u8 }
747 var vpct: i64 = 0; if pieces > 0 { vpct = have*100/pieces }
748 o = d_puts(out, o, "{\"id\":\"" as *u8); o = d_puts(out, o, id)
749 o = d_puts(out, o, "\",\"name\":\"" as *u8); o = d_json_buf(out, o, nameb, 0, d_strlen(nameb))
750 o = d_puts(out, o, "\",\"size\":" as *u8); o = d_putn(out, o, total)
751 o = d_puts(out, o, ",\"done\":" as *u8); o = d_putn(out, o, done)
752 o = d_puts(out, o, ",\"peers\":" as *u8); o = d_putn(out, o, peers)
753 o = d_puts(out, o, ",\"havePieces\":" as *u8); o = d_putn(out, o, have)
754 o = d_puts(out, o, ",\"totalPieces\":" as *u8); o = d_putn(out, o, pieces)
755 o = d_puts(out, o, ",\"verified\":" as *u8); o = d_putn(out, o, vpct)
756 o = d_puts(out, o, ",\"status\":\"" as *u8); o = d_puts(out, o, stateb)
757 o = d_puts(out, o, "\"}" as *u8)
758 return o
759}
760// GET /status: aggregate every torrent in the registry into the downloads array.
761func d_status_all(cfd: i64, outdir: *u8, idxpath: *u8) -> i64 {
762 let nl: *i64 = sys_mmap(16) as *i64; let idx: *u8 = d_idx_load(idxpath, nl); let n: i64 = nl[0]
763 let out: *u8 = sys_mmap(TORRENT_MAGIC_262144); var o: i64 = 0
764 o = d_puts(out, o, "{\"downloads\":[" as *u8)
765 var first: i64 = 1
766 if n > 0 {
767 var i: i64 = 0; var guard: i64 = 0
768 while i < n {
769 if guard > TORRENT_MAGIC_2000 { i = n } else {
770 guard = guard + 1
771 let e: i64 = d_scan_to(idx, n, i, 10)
772 let len: i64 = e - i
773 if len > 0 {
774 let id: *u8 = sys_mmap(128); var k: i64 = 0; while k < len { if k < 127 { id[k] = idx[i+k]; k = k + 1 } else { k = len } } id[len] = 0 as u8
775 if d_idx_has(idx, i, id) == 0 { // dedup: O_APPEND may leave a rare dup line; render each id once
776 let dir: *u8 = d_subdir(outdir, id, sys_mmap(640))
777 if first == 0 { o = d_puts(out, o, "," as *u8) } first = 0
778 o = d_status_one(out, o, id, dir)
779 }
780 }
781 i = e + 1
782 }
783 }
784 }
785 o = d_puts(out, o, "]}" as *u8)
786 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
787 return 0
788}
789
790// ---- stream-while-downloading: serve the VIDEO file's downloaded prefix from download.part (HTTP Range) ----
791// The torrent's LARGEST file is the video; its bytes live at [voff, voff+vlen) inside download.part. We serve
792// the contiguous-downloaded intersection so a <video> plays what has arrived. Works fully for a completed
793// download (streams straight from download.part, no extraction) and progressively in sequential stream-mode.
794
795// largest file's (offset,length) within download.part, from download.meta. out[0]=offset, out[1]=length.
796func d_video_extent(meta: *u8, msize: i64, out: *i64) -> i64 {
797 out[0] = 0; out[1] = 0
798 let filo: i64 = nx_bc_dict_get(meta, 0, msize, "files" as *u8, 5)
799 if filo >= 0 { if (meta[filo] as i64) == 0x6C { // 'l' -> multi-file
800 var p: i64 = filo + 1; var off: i64 = 0; var bestoff: i64 = 0; var bestlen: i64 = 0; var run: i64 = 1
801 while run == 1 {
802 if p >= msize { run = 0 } else { if (meta[p] as i64) == 0x65 { run = 0 } else {
803 let lo: i64 = nx_bc_dict_get(meta, p, msize, "length" as *u8, 6); let lv: *i64 = sys_mmap(8) as *i64; lv[0]=0; if lo>=0 { nx_bc_int(meta, lo, msize, lv) }
804 if lv[0] > bestlen { bestlen = lv[0]; bestoff = off }
805 off = off + lv[0]; p = nx_bc_skip(meta, p, msize)
806 } }
807 }
808 out[0] = bestoff; out[1] = bestlen; return 1
809 } }
810 let lno: i64 = nx_bc_dict_get(meta, 0, msize, "length" as *u8, 6); let lv2: *i64 = sys_mmap(8) as *i64; lv2[0]=0; if lno>=0 { nx_bc_int(meta, lno, msize, lv2) }
811 out[0] = 0; out[1] = lv2[0]; return 1 // single-file -> whole part is the file
812}
813
814// contiguous downloaded bytes from offset 0 (full pieces only) = read download.done + download.npc.
815func d_contig_bytes(dir: *u8) -> i64 {
816 let donep: *u8 = d_path(dir, "/download.done" as *u8, sys_mmap(640))
817 let db: *u8 = sys_mmap(TORRENT_MAGIC_262144); let dn: i64 = d_read_file(donep, db, TORRENT_MAGIC_262144)
818 if dn <= 0 { return 0 }
819 var contig: i64 = 0; var i: i64 = 0
820 while i < dn { if (db[i] as i64) == 1 { contig = contig + 1; i = i + 1 } else { i = dn } }
821 let npcp: *u8 = d_path(dir, "/download.npc" as *u8, sys_mmap(640))
822 let nb: *u8 = sys_mmap(16); let nn: i64 = d_read_file(npcp, nb, 16)
823 var plen: i64 = 0
824 if nn >= 8 { plen = ((nb[4] as i64)<<24)|((nb[5] as i64)<<16)|((nb[6] as i64)<<8)|(nb[7] as i64) }
825 return contig * plen
826}
827
828// extract + url-decode the urlencoded "magnet=" form field (the browser UI sends
829// magnet=<encodeURIComponent(...)>&area=...). %XX -> byte, '+' -> space; the value runs to the field
830// separator '&' or end. The magnet's OWN '&' (between dn=/tr=) arrive as %26, so they do NOT terminate
831// early. Returns the decoded length, or 0 if there's no magnet= field / it isn't a magnet (caller then
832// falls back to the JSON/raw scan the gate uses). FIXES: browser "Add" returning 400 "no magnet".
833func d_form_magnet(body: *u8, blen: i64, out: *u8, ocap: i64) -> i64 {
834 var i: i64 = 0; var start: i64 = 0 - 1
835 while i + 7 <= blen {
836 if body[i]==(109 as u8){ if body[i+1]==(97 as u8){ if body[i+2]==(103 as u8){ if body[i+3]==(110 as u8){ if body[i+4]==(101 as u8){ if body[i+5]==(116 as u8){ if body[i+6]==(61 as u8){
837 start = i + 7; i = blen + 100
838 }}}}}}}
839 i = i + 1
840 }
841 if start < 0 { return 0 }
842 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
843 while go == 1 {
844 if j >= blen { go = 0 } else {
845 var c: i64 = body[j] as i64
846 if c == 38 { go = 0 } else { // '&' = end of this form field
847 if c == 37 { if j + 2 < blen { let hi: i64 = d_hexv(body[j+1] as i64); let lo: i64 = d_hexv(body[j+2] as i64); if hi >= 0 { if lo >= 0 { c = hi*16 + lo; j = j + 2 } } } }
848 if c == 43 { c = 32 } // '+' -> space
849 if k < ocap - 1 { out[k] = c as u8; k = k + 1 }
850 j = j + 1
851 }
852 }
853 }
854 out[k] = 0 as u8
855 if k < 7 { return 0 } // must be at least "magnet:"
856 if out[0]!=(109 as u8){ return 0 } if out[1]!=(97 as u8){ return 0 } if out[2]!=(103 as u8){ return 0 } if out[3]!=(110 as u8){ return 0 } if out[4]!=(101 as u8){ return 0 } if out[5]!=(116 as u8){ return 0 } if out[6]!=(58 as u8){ return 0 }
857 return k
858}
859
860// size in bytes of a file (st_size @ off 48 of the x86_64 struct stat; mirrors the mtime@88 read in
861// d_autoresume). 0 if it doesn't exist. Lets /stream serve a (near-)complete video's full extent.
862func d_filesize(path: *u8) -> i64 {
863 let stbuf: *u8 = sys_mmap(160)
864 if sys_fstatat(path, stbuf) == 0 { let szp: *i64 = (((stbuf as i64) + 48)) as *i64; return szp[0] }
865 return 0
866}
867
868// out[0] = pieces verified on disk (count of 1-bytes in download.done), out[1] = total pieces (download.npc).
869func d_have_npc(dir: *u8, out: *i64) -> i64 {
870 out[0] = 0; out[1] = 0
871 let donep: *u8 = d_path(dir, "/download.done" as *u8, sys_mmap(640))
872 let db: *u8 = sys_mmap(TORRENT_MAGIC_262144); let dn: i64 = d_read_file(donep, db, TORRENT_MAGIC_262144)
873 var have: i64 = 0; var i: i64 = 0
874 while i < dn { if (db[i] as i64) == 1 { have = have + 1 } i = i + 1 }
875 out[0] = have
876 let npcp: *u8 = d_path(dir, "/download.npc" as *u8, sys_mmap(640))
877 let nb: *u8 = sys_mmap(16); let nn: i64 = d_read_file(npcp, nb, 16)
878 if nn >= 4 { out[1] = ((nb[0] as i64)<<24)|((nb[1] as i64)<<16)|((nb[2] as i64)<<8)|(nb[3] as i64) }
879 return 0
880}
881
882// parse "Range: bytes=START-END" (END optional). out[0]=start, out[1]=end(-1 open). returns 1 if present.
883func d_parse_range(req: *u8, rn: i64, out: *i64) -> i64 {
884 out[0] = 0; out[1] = 0 - 1
885 let pat: *u8 = "Range: bytes=" as *u8; let pl: i64 = 13
886 var i: i64 = 0; var pos: i64 = 0 - 1
887 while i + pl <= rn { var j: i64 = 0; var m: i64 = 1; while j < pl { if req[i+j] != pat[j] { m = 0; j = pl } else { j = j + 1 } } if m == 1 { pos = i + pl; i = rn } else { i = i + 1 } }
888 if pos < 0 { return 0 }
889 var s: i64 = 0; var sgo: i64 = 1
890 while sgo == 1 { if pos >= rn { sgo = 0 } else { let c: i64 = req[pos] as i64; if c >= 48 { if c <= 57 { s = s*10 + (c-48); pos = pos + 1 } else { sgo = 0 } } else { sgo = 0 } } }
891 out[0] = s // pos now points at the first non-digit (the '-') or rn
892 if pos < rn { if (req[pos] as i64) == 45 { // '-'
893 pos = pos + 1; var e: i64 = 0; var he: i64 = 0
894 while pos < rn { let c2: i64 = req[pos] as i64; if c2 >= 48 { if c2 <= 57 { e = e*10 + (c2-48); he = 1; pos = pos + 1 } else { pos = rn } } else { pos = rn } }
895 if he == 1 { out[1] = e }
896 } }
897 return 1
898}
899
900func d_stream(cfd: i64, req: *u8, rn: i64, dir: *u8) -> i64 {
901 // a /stream request means the operator is watching -> switch this torrent to SEQUENTIAL download so
902 // the playable prefix grows in order (the worker reads download.stream each pick). Harmless if complete.
903 d_write_file(d_path(dir, "/download.stream" as *u8, sys_mmap(640)), "1" as *u8, 1)
904 let metap: *u8 = d_path(dir, "/download.meta" as *u8, sys_mmap(640))
905 let meta: *u8 = sys_mmap(TORRENT_MAGIC_1048576); let msize: i64 = d_read_file(metap, meta, TORRENT_MAGIC_1048576)
906 if msize <= 0 { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "no metadata yet\n" as *u8); return 0 }
907 let ext: *i64 = sys_mmap(16) as *i64; d_video_extent(meta, msize, ext)
908 let voff: i64 = ext[0]; let vlen: i64 = ext[1]
909 if vlen <= 0 { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "no playable file\n" as *u8); return 0 }
910 // AVAIL = how much of the video we can serve now. Near-complete or complete (>=90% of pieces) -> serve
911 // the whole furthest-downloaded extent of download.part (any still-missing piece is a sparse-zero gap the
912 // player skips), so a 99%-done OR finished torrent IS watchable. Early (<90%) -> only the contiguous
913 // prefix from byte 0, so sequential stream-mode plays cleanly from the start. FIXES "can't watch 99%
914 // done" + lets you watch one torrent while others keep downloading.
915 let hn: *i64 = sys_mmap(16) as *i64; d_have_npc(dir, hn)
916 let have: i64 = hn[0]; let npc: i64 = hn[1]
917 // COMPLETE single-file MPEG-TS -> fly-remux to fMP4 so a plain <video> (Chrome/Firefox) plays it,
918 // mirroring the gallery's gs_vid_remux_ts (rung 8). In-progress / multi-file fall through to the partial Range
919 // serve below (watch-while-downloading + native mp4). The daemon forks per-connection, so execve here
920 // replaces only this child.
921 if npc > 0 { if have >= npc { if voff == 0 {
922 let partp0: *u8 = d_path(dir, "/download.part" as *u8, sys_mmap(640))
923 let mb0: *u8 = sys_mmap(64); let pf0: i64 = sys_openat_rd(partp0); var g0: i64 = 0; if pf0 >= 0 { g0 = sys_read(pf0, mb0, 16) }
924 var is_ts: i64 = 0; if g0 >= 1 { if (mb0[0] as i64) == 0x47 { is_ts = 1 } } // 0x47 = MPEG-TS sync byte
925 var is_ftyp: i64 = 0; if g0 >= 16 { if mb0[4]==(102 as u8) { if mb0[5]==(116 as u8) { if mb0[6]==(121 as u8) { if mb0[7]==(112 as u8) { is_ftyp = 1 } } } } } // 'ftyp' = mp4
926 var is_fast: i64 = 0
927 if is_ftyp == 1 { // is the box AFTER ftyp already 'moov' (faststart)? then the Range serve below is seekable.
928 let ftsz: i64 = ((mb0[0] as i64)<<24)|((mb0[1] as i64)<<16)|((mb0[2] as i64)<<8)|(mb0[3] as i64)
929 let nb: *u8 = sys_mmap(16); var ng: i64 = 0; if pf0 >= 0 { sys_lseek(pf0, ftsz, 0); ng = sys_read(pf0, nb, 8) }
930 if ng >= 8 { if nb[4]==(109 as u8) { if nb[5]==(111 as u8) { if nb[6]==(111 as u8) { if nb[7]==(118 as u8) { is_fast = 1 } } } } } // 'moov'
931 }
932 if pf0 >= 0 { sys_close(pf0) }
933 // COMPLETE single-file MPEG-TS -> fly-remux to fMP4 so a plain <video> (Chrome/Firefox) plays it.
934 if is_ts == 1 {
935 let fh: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8
936 sys_write(cfd, fh, d_strlen(fh)); sys_dup3(cfd, 1, 0)
937 let wexe: *u8 = d_path(d_self_dir(), "/nx_ts2fmp4.sov.elf" as *u8, sys_mmap(640)) // sibling of the daemon (self-dir): /tmp on dev, /volume1/ai/torrent on the NAS
938 let av2: *i64 = sys_mmap(64) as *i64; av2[0] = wexe as i64; av2[1] = partp0 as i64; av2[2] = 0
939 let ev2: *i64 = sys_mmap(16) as *i64; ev2[0] = 0
940 sys_execve(wexe, av2, ev2); sys_exit(127)
941 }
942 // NON-FASTSTART mp4 ([ftyp][mdat..][moov]): a browser streaming via Range reads the front, finds no moov
943 // -> "no video, this format is not supported". Relocate moov to the FRONT on the fly (nx_mp4_faststart,
944 // lossless, gate-proven) so a plain <video> plays it. Faststart mp4s (moov already front) fall through
945 // to the seekable Range serve below.
946 if is_ftyp == 1 { if is_fast == 0 {
947 let fh2: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8
948 sys_write(cfd, fh2, d_strlen(fh2)); sys_dup3(cfd, 1, 0)
949 let wexe2: *u8 = d_path(d_self_dir(), "/nx_mp4_faststart.sov.elf" as *u8, sys_mmap(640))
950 let av3: *i64 = sys_mmap(64) as *i64; av3[0] = wexe2 as i64; av3[1] = partp0 as i64; av3[2] = 0
951 let ev3: *i64 = sys_mmap(16) as *i64; ev3[0] = 0
952 sys_execve(wexe2, av3, ev3); sys_exit(127)
953 } }
954 } } }
955 var avail: i64 = 0
956 if npc > 0 { if have * 100 >= npc * 90 {
957 let partp0: *u8 = d_path(dir, "/download.part" as *u8, sys_mmap(640))
958 avail = d_filesize(partp0) - voff
959 } else { avail = d_contig_bytes(dir) - voff } }
960 else { avail = d_contig_bytes(dir) - voff }
961 if avail < 0 { avail = 0 } if avail > vlen { avail = vlen }
962 if avail <= 0 { d_send_str(cfd, "425 Too Early" as *u8, "text/plain" as *u8, "not enough downloaded yet\n" as *u8); return 0 }
963 let rng: *i64 = sys_mmap(16) as *i64; d_parse_range(req, rn, rng)
964 var rs: i64 = rng[0]; var re: i64 = rng[1]
965 if re < 0 { re = vlen - 1 }
966 if rs < 0 { rs = 0 }
967 if rs >= avail { d_send_str(cfd, "416 Range Not Satisfiable" as *u8, "text/plain" as *u8, "beyond available\n" as *u8); return 0 }
968 if re > avail - 1 { re = avail - 1 }
969 let served: i64 = re - rs + 1
970 let h: *u8 = sys_mmap(512); var o: i64 = 0
971 o = d_puts(h, o, "HTTP/1.1 206 Partial Content\r\nContent-Type: video/mp4\r\nAccept-Ranges: bytes\r\nContent-Range: bytes " as *u8)
972 o = d_putn(h, o, rs); o = d_puts(h, o, "-" as *u8); o = d_putn(h, o, re); o = d_puts(h, o, "/" as *u8); o = d_putn(h, o, vlen)
973 o = d_puts(h, o, "\r\nContent-Length: " as *u8); o = d_putn(h, o, served)
974 o = d_puts(h, o, "\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8)
975 sys_write(cfd, h, o)
976 let partp: *u8 = d_path(dir, "/download.part" as *u8, sys_mmap(640))
977 let pfd: i64 = sys_openat_rd(partp)
978 if pfd >= 0 {
979 sys_lseek(pfd, voff + rs, 0)
980 let chunk: *u8 = sys_mmap(TORRENT_MAGIC_1048576); var rem: i64 = served
981 while rem > 0 { var want: i64 = TORRENT_MAGIC_1048576; if rem < want { want = rem } let g: i64 = sys_read(pfd, chunk, want); if g <= 0 { rem = 0 } else { sys_write(cfd, chunk, g); rem = rem - g } }
982 sys_close(pfd)
983 }
984 sys_close(cfd)
985 return 0
986}
987
988// DEDUP BY INFO-HASH: scan existing torrents' download.magnet for one whose btih == `btih`. Returns 1
989// + copies that torrent's id into out_id (so an add of the same content under a different display name
990// RESUMES the one download instead of creating a duplicate subdir). Works for torrents added before this
991// fix too (it reads their magnet, not a sidecar).
992func d_find_by_btih(outdir: *u8, idxpath: *u8, btih: *u8, btn: i64, out_id: *u8, ocap: i64) -> i64 {
993 let nl: *i64 = sys_mmap(16) as *i64; let idx: *u8 = d_idx_load(idxpath, nl); let n: i64 = nl[0]
994 if n <= 0 { return 0 }
995 var i: i64 = 0; var guard: i64 = 0
996 while i < n {
997 if guard > TORRENT_MAGIC_2000 { i = n } else {
998 guard = guard + 1
999 let e: i64 = d_scan_to(idx, n, i, 10)
1000 let idlen: i64 = e - i
1001 if idlen > 0 { if idlen < 500 {
1002 let id: *u8 = sys_mmap(512); var k: i64 = 0; while k < idlen { id[k] = idx[i+k]; k = k + 1 } id[idlen] = 0 as u8
1003 let dir: *u8 = d_subdir(outdir, id, sys_mmap(640))
1004 let magp: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640))
1005 let mb: *u8 = sys_mmap(TORRENT_MAGIC_4096); let mln: i64 = d_read_file(magp, mb, TORRENT_MAGIC_4095)
1006 if mln > 0 {
1007 mb[mln] = 0 as u8
1008 let eb: *u8 = sys_mmap(128); let ebn: i64 = d_extract_btih(mb, mln, eb, 128)
1009 if ebn == btn {
1010 var m: i64 = 1; var j: i64 = 0
1011 while j < btn { if eb[j] != btih[j] { m = 0; j = btn } else { j = j + 1 } }
1012 if m == 1 { var c: i64 = 0; while c < idlen { if c < ocap-1 { out_id[c] = id[c] } c = c + 1 } out_id[idlen] = 0 as u8; return 1 }
1013 }
1014 }
1015 } }
1016 i = e + 1
1017 }
1018 }
1019 return 0
1020}
1021
1022// STARTUP AUTO-RESUME (crash/reboot recovery): resume every torrent that is control=active AND has
1023// on-disk progress AND whose status file is STALE (not touched in 60s = its worker is dead). The
1024// staleness guard is load-bearing: a GRACEFUL daemon restart leaves the workers alive (they double-fork
1025// + reparent to init, so they survive), and their status keeps updating -> NOT stale -> NOT re-spawned
1026// (no duplicate worker). Only a crash/reboot (workers gone, status frozen) triggers a resume. Returns count.
1027// bud = the sweep's shared admission budget (d_admit_budget, ONE measurement per sweep so N resumes cannot
1028// each read the same pre-spawn headroom and all be admitted). rot = the sweep ordinal: the walk starts at
1029// registry entry (rot mod entries) and wraps, so under a small budget every torrent gets its turn instead of
1030// the head of the registry winning forever (round-robin by construction, no per-torrent bookkeeping).
1031func d_autoresume(outdir: *u8, idxpath: *u8, bud: *i64, rot: i64) -> i64 {
1032 let nl: *i64 = sys_mmap(16) as *i64; let idx: *u8 = d_idx_load(idxpath, nl); let n: i64 = nl[0]
1033 if n <= 0 { return 0 }
1034 let ts: *i64 = sys_mmap(16) as *i64; sys_clock_gettime_real(ts); let now: i64 = ts[0]
1035 let stbuf: *u8 = sys_mmap(160)
1036 // pass 1: registry line starts (bounded by the same entry guard the walk always had)
1037 let starts: *i64 = sys_mmap((TORRENT_MAGIC_2000 + 1) * 8) as *i64
1038 var ne: i64 = 0
1039 var sc: i64 = 0
1040 while sc < n {
1041 if ne > TORRENT_MAGIC_2000 { sc = n } else {
1042 starts[ne] = sc
1043 ne = ne + 1
1044 sc = d_scan_to(idx, n, sc, 10) + 1
1045 }
1046 }
1047 if ne <= 0 { d_idx_free(idx, n); return 0 }
1048 var rs: i64 = 0
1049 if rot > 0 { rs = rot % ne }
1050 var ki: i64 = 0; var resumed: i64 = 0
1051 while ki < ne {
1052 var ei: i64 = rs + ki
1053 if ei >= ne { ei = ei - ne }
1054 let i: i64 = starts[ei]
1055 ki = ki + 1
1056 let e: i64 = d_scan_to(idx, n, i, 10)
1057 let idlen: i64 = e - i
1058 if idlen > 0 { if idlen < 500 {
1059 let id: *u8 = sys_mmap(512); var k: i64 = 0; while k < idlen { id[k] = idx[i+k]; k = k + 1 } id[idlen] = 0 as u8
1060 let dir: *u8 = d_subdir(outdir, id, sys_mmap(640))
1061 let ctlp: *u8 = d_path(dir, "/download.control" as *u8, sys_mmap(640)); let cb: *u8 = sys_mmap(16); let cn: i64 = d_read_file(ctlp, cb, 15)
1062 var active: i64 = 0; if cn > 0 { if (cb[0] as i64) == 97 { active = 1 } } // 'a'ctive
1063 if active == 1 {
1064 let hn: *i64 = sys_mmap(16) as *i64; d_have_npc(dir, hn)
1065 // RESUMABLE = on-disk progress OR metadata-not-yet-resolved (npc==0). A magnet still fetching
1066 // its info dict legitimately has NO progress yet -- the old `d_has_progress` gate skipped it
1067 // FOREVER, so a magnet that failed metadata once was never retried (the BANK-155/MGFX-142
1068 // "never resolved metadata" stuck bug). npc>0 && have>=npc = COMPLETE -> skip (no forever-respawn).
1069 // RESUME ANY active torrent -- the `notdone` gate below is the sole COMPLETE-skip. The old gate
1070 // (resumable only if d_has_progress OR npc==0) STRANDED FOREVER the torrents that resolved
1071 // metadata (npc>0) but downloaded ZERO pieces (download.done absent -> d_has_progress=0): exactly
1072 // BANK-155/ajvr-256/ajvr-166 (the "IGNORING" torrents). They got ONE worker at /add, and once it
1073 // died without completing a piece they were NEVER re-spawned -> stuck at have=0 permanently. That
1074 // was the REAL "IGNORING" root cause -- not the unchoke timeout, not reciprocity: they simply had
1075 // no worker. notdone (npc==0 OR have<npc) already excludes COMPLETE, so resume-all is correct.
1076 var resumable: i64 = 1
1077 if resumable == 1 {
1078 var notdone: i64 = 1; if hn[1] > 0 { if hn[0] >= hn[1] { notdone = 0 } }
1079 if notdone == 1 {
1080 let statp: *u8 = d_path(dir, "/download.status" as *u8, sys_mmap(640))
1081 var stale: i64 = 1
1082 if sys_fstatat(statp, stbuf) == 0 { let mtp: *i64 = (((stbuf as i64) + 88)) as *i64; if now - mtp[0] < 60 { stale = 0 } }
1083 if stale == 1 {
1084 let mp: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640)); let mb: *u8 = sys_mmap(TORRENT_MAGIC_4096); let mln: i64 = d_read_file(mp, mb, TORRENT_MAGIC_4095)
1085 if mln > 0 { mb[mln] = 0 as u8; if d_spawn_worker(mb, dir, 1, bud) == 0 { resumed = resumed + 1 } }
1086 }
1087 }
1088 }
1089 }
1090 } }
1091 }
1092 d_idx_free(idx, n)
1093 return resumed
1094}
1095
1096// ---- AREA routing + access enforcement (R2: two areas; R3 wires the real session->level) ----
1097// scan buf[0..n) for the NUL-terminated literal pat; return 1 if present.
1098func d_buf_find(buf: *u8, n: i64, pat: *u8) -> i64 {
1099 let pln: i64 = d_strlen(pat)
1100 if pln <= 0 { return 0 }
1101 var i: i64 = 0
1102 while i + pln <= n {
1103 var j: i64 = 0; var m: i64 = 1
1104 while j < pln { if buf[i+j] != pat[j] { m = 0; j = pln } else { j = j + 1 } }
1105 if m == 1 { return 1 }
1106 i = i + 1
1107 }
1108 return 0
1109}
1110// which destination AREA does this request select? "area=gallery"/"area=nsfw" -> NSFW; default SFW ("media").
1111// Default is the LOWER-privilege area so an unspecified/garbled flag can NEVER silently land in NSFW.
1112func d_parse_area(req: *u8, n: i64) -> i64 {
1113 if d_buf_find(req, n, "area=gallery" as *u8) == 1 { return NX_TAREA_NSFW }
1114 if d_buf_find(req, n, "area=nsfw" as *u8) == 1 { return NX_TAREA_NSFW }
1115 return NX_TAREA_SFW
1116}
1117// is this file present? (the local-operator marker check)
1118func d_marker_present(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
1119// parse a non-negative int from the TRUSTED internal header "X-Nishi-Level:" (set by the front gateway). -1 if absent.
1120func d_trusted_level(req: *u8, n: i64) -> i64 {
1121 let pat: *u8 = "X-Nishi-Level:" as *u8; let pl: i64 = 14
1122 var i: i64 = 0; var pos: i64 = 0 - 1
1123 while i + pl <= n { var j: i64 = 0; var m: i64 = 1; while j < pl { if req[i+j] != pat[j] { m = 0; j = pl } else { j = j + 1 } } if m == 1 { pos = i + pl; i = n } else { i = i + 1 } }
1124 if pos < 0 { return 0 - 1 }
1125 if pos < n { if req[pos]==(32 as u8) { pos = pos + 1 } }
1126 var v: i64 = 0; var got: i64 = 0
1127 while pos < n { let c: i64 = req[pos] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48); got = 1; pos = pos + 1 } else { pos = n } } else { pos = n } }
1128 if got == 0 { return 0 - 1 }
1129 return v
1130}
1131// the requester's GRANTED level (R3 -- DENY-BY-DEFAULT).
1132// LOCAL mode (marker present = the operator's own box): OWNER -- today's single-operator behavior.
1133// HUB mode (no marker): the front gateway already authenticated (OPAQUE) + resolved the level via
1134// nx_torrent_authn and injected the trusted internal header X-Nishi-Level. Read it; absent/garbled -> 0
1135// (ANON), so nx_taccess_allow denies every gated area. The daemon MUST bind localhost behind the gateway in
1136// hub mode so an outside client can't spoof the header (Cardinal 12: trust internal, validate at the boundary).
1137func d_viewer_level(req: *u8, n: i64) -> i64 {
1138 if d_marker_present(TORRENT_LOCAL_MARKER) == 1 { return NX_TACCESS_OWNER }
1139 let lv: i64 = d_trusted_level(req, n)
1140 if lv < 0 { return 0 }
1141 return lv
1142}
1143// resolve the dir for torrent `id` in a root the viewer MAY access. SFW always; NSFW only for OWNER.
1144// returns 1 + fills out_dir + out_area[0]=area, else 0 (fail-closed: a family viewer cannot even ADDRESS
1145// an NSFW torrent, so /stream and the control verbs can't reach it by id-guessing).
1146func d_resolve_dir(id: *u8, level: i64, sfw_root: *u8, sfw_idx: *u8, nsfw_root: *u8, nsfw_idx: *u8, out_dir: *u8, out_area: *i64) -> i64 {
1147 let nl1: *i64 = sys_mmap(16) as *i64; let ib: *u8 = d_idx_load(sfw_idx, nl1); let n1: i64 = nl1[0]
1148 if n1 > 0 { if d_idx_has(ib, n1, id) == 1 { d_subdir(sfw_root, id, out_dir); out_area[0] = NX_TAREA_SFW; return 1 } }
1149 if level >= NX_TACCESS_OWNER {
1150 let nl2: *i64 = sys_mmap(16) as *i64; let ib2: *u8 = d_idx_load(nsfw_idx, nl2); let n2: i64 = nl2[0]
1151 if n2 > 0 { if d_idx_has(ib2, n2, id) == 1 { d_subdir(nsfw_root, id, out_dir); out_area[0] = NX_TAREA_NSFW; return 1 } }
1152 }
1153 return 0
1154}
1155// append all torrents in ONE root's registry to out (sharing offset + first-flag across roots); return new o.
1156func d_status_root(out: *u8, o0: i64, first_p: *i64, root: *u8, idxpath: *u8) -> i64 {
1157 var o: i64 = o0
1158 let nl: *i64 = sys_mmap(16) as *i64; let idx: *u8 = d_idx_load(idxpath, nl); let n: i64 = nl[0]
1159 if n > 0 {
1160 var i: i64 = 0; var guard: i64 = 0
1161 while i < n {
1162 if guard > TORRENT_MAGIC_2000 { i = n } else {
1163 guard = guard + 1
1164 let e: i64 = d_scan_to(idx, n, i, 10)
1165 let len: i64 = e - i
1166 if len > 0 {
1167 let id: *u8 = sys_mmap(128); var k: i64 = 0; while k < len { if k < 127 { id[k] = idx[i+k]; k = k + 1 } else { k = len } } id[len] = 0 as u8
1168 if d_idx_has(idx, i, id) == 0 {
1169 let dir: *u8 = d_subdir(root, id, sys_mmap(640))
1170 if first_p[0] == 0 { o = d_puts(out, o, "," as *u8) } first_p[0] = 0
1171 o = d_status_one(out, o, id, dir)
1172 }
1173 }
1174 i = e + 1
1175 }
1176 }
1177 }
1178 return o
1179}
1180// GET /status: aggregate the torrents the viewer MAY see -- SFW always, NSFW only for OWNER. Isolation by
1181// construction: a family viewer never even scans the NSFW root, so an NSFW torrent cannot appear in their list.
1182func d_status_two(cfd: i64, level: i64, sfw_root: *u8, sfw_idx: *u8, nsfw_root: *u8, nsfw_idx: *u8) -> i64 {
1183 let out: *u8 = sys_mmap(TORRENT_MAGIC_262144); var o: i64 = 0
1184 o = d_puts(out, o, "{\"downloads\":[" as *u8)
1185 let fp: *i64 = sys_mmap(16) as *i64; fp[0] = 1
1186 o = d_status_root(out, o, fp, sfw_root, sfw_idx)
1187 if level >= NX_TACCESS_OWNER { o = d_status_root(out, o, fp, nsfw_root, nsfw_idx) }
1188 o = d_puts(out, o, "]}" as *u8)
1189 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
1190 return 0
1191}
1192
1193// ---- router ------------------------------------------------------
1194func d_route(cfd: i64, req: *u8, method: i64, po: i64, pl: i64, cl_: i64, bo: i64, rn: i64,
1195 sfw_root: *u8, sfw_idx: *u8, nsfw_root: *u8, nsfw_idx: *u8, indexp: *u8, appjsp: *u8, discpath: *u8) -> i64 {
1196 let level: i64 = d_viewer_level(req, rn)
1197 if method == 1 {
1198 if d_streq(req,po,pl,"/torrent" as *u8)==1 { d_serve_file(cfd, indexp, "text/html; charset=utf-8" as *u8); return 0 }
1199 if d_streq(req,po,pl,"/torrent/" as *u8)==1 { d_serve_file(cfd, indexp, "text/html; charset=utf-8" as *u8); return 0 }
1200 if d_streq(req,po,pl,"/" as *u8)==1 { d_serve_file(cfd, indexp, "text/html; charset=utf-8" as *u8); return 0 }
1201 if d_streq(req,po,pl,"/torrent/app.js" as *u8)==1 { d_serve_file(cfd, appjsp, "application/javascript" as *u8); return 0 }
1202 if d_streq(req,po,pl,"/torrent/api/me" as *u8)==1 { d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"user\":\"local\"}" as *u8); return 0 }
1203 if d_streq(req,po,pl,"/torrent/api/status" as *u8)==1 { d_status_two(cfd, level, sfw_root, sfw_idx, nsfw_root, nsfw_idx); return 0 }
1204 // STREAM the video file's downloaded prefix (HTTP Range) so a <video> can play while/after download.
1205 if d_starts(req,po,pl,"/torrent/stream/" as *u8)==1 {
1206 var idlen: i64 = pl - 16 // strip "/torrent/stream/" (16)
1207 if idlen > 511 { idlen = 511 }
1208 if idlen > 0 {
1209 let id: *u8 = sys_mmap(512); var k: i64 = 0; while k < idlen { id[k] = req[po+16+k]; k = k + 1 } id[idlen] = 0 as u8
1210 let dir: *u8 = sys_mmap(640); let sa: *i64 = sys_mmap(16) as *i64
1211 if d_resolve_dir(id, level, sfw_root, sfw_idx, nsfw_root, nsfw_idx, dir, sa) == 1 {
1212 d_stream(cfd, req, rn, dir)
1213 } else { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "not found\n" as *u8) }
1214 } else { d_send_str(cfd, "400 Bad Request" as *u8, "text/plain" as *u8, "no id\n" as *u8) }
1215 return 0
1216 }
1217 if d_streq(req,po,pl,"/torrent/api/discover_result" as *u8)==1 { d_discover_result(cfd, discpath); return 0 }
1218 d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "not found\n" as *u8); return 0
1219 }
1220 if method == 2 {
1221 if d_streq(req,po,pl,"/torrent/api/login" as *u8)==1 { d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0 }
1222 if d_streq(req,po,pl,"/torrent/api/add" as *u8)==1 {
1223 // The POST body can trail the headers in a later TCP segment, and some clients
1224 // omit a parseable Content-Length. So: drain the socket (idle-bounded) into the
1225 // same buffer, then search the WHOLE request -- the "magnet:" run never occurs
1226 // in HTTP headers, so a full-buffer scan is unambiguous and Content-Length-free.
1227 sys_set_socket_timeout(cfd, 2)
1228 var tot: i64 = rn
1229 var go: i64 = 1
1230 while go == 1 {
1231 if tot >= TORRENT_MAGIC_16384 { go = 0 } else {
1232 let r: i64 = sys_read(cfd, req + tot, TORRENT_MAGIC_16384 - tot)
1233 if r <= 0 { go = 0 } else { tot = tot + r }
1234 }
1235 }
1236 let mag: *u8 = sys_mmap(TORRENT_MAGIC_4096)
1237 var mn: i64 = d_form_magnet(((req as i64) + bo) as *u8, tot - bo, mag, TORRENT_MAGIC_4096) // browser UI: urlencoded magnet=<%XX> -> decode
1238 if mn <= 0 { mn = d_extract_magnet(req, tot, mag, TORRENT_MAGIC_4096) } // JSON/raw fallback (the gate path)
1239 if mn <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"no magnet\"}" as *u8); return 0 }
1240 // AREA + ACCESS (R2): which destination did the request flag, and may THIS viewer reach it?
1241 // Deny-by-default via nx_taccess_allow (SFW="media" needs FAMILY level; NSFW="gallery" needs OWNER).
1242 let area: i64 = d_parse_area(req, tot)
1243 if nx_taccess_allow(level, area) != NX_TACCESS_ALLOW {
1244 d_send_str(cfd, "403 Forbidden" as *u8, "application/json" as *u8, "{\"error\":\"access denied for this area\"}" as *u8); return 0
1245 }
1246 var aroot: *u8 = sfw_root; var aidx: *u8 = sfw_idx
1247 if area == NX_TAREA_NSFW { aroot = nsfw_root; aidx = nsfw_idx }
1248 // MULTI-TORRENT: id = the magnet's btih; each torrent gets its OWN <area-root>/<id> + a registry
1249 // entry, so concurrent downloads never clobber each other (the BBB-clobber bug).
1250 let hbv: *u8 = sys_mmap(128); let hbn: i64 = d_extract_btih(mag, mn, hbv, 128); if hbn <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"bad magnet (no btih)\"}" as *u8); return 0 }
1251 // DEDUP BY INFO-HASH (within the area): if this exact content is already here (any display name),
1252 // RESUME that one download instead of spawning a duplicate subdir (the 326FCT-218-added-twice bug).
1253 let exid: *u8 = sys_mmap(512)
1254 if d_find_by_btih(aroot, aidx, hbv, hbn, exid, 512) == 1 {
1255 let edir: *u8 = d_subdir(aroot, exid, sys_mmap(640))
1256 d_write_file(d_path(edir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1257 d_spawn_admitted(mag, edir, 1, nsfw_root, "dedup-resume" as *u8)
1258 let rb0: *u8 = sys_mmap(256); var r0: i64 = d_puts(rb0, 0, "{\"id\":\"" as *u8); r0 = d_puts(rb0, r0, exid); r0 = d_puts(rb0, r0, "\",\"dedup\":true}" as *u8); rb0[r0] = 0 as u8
1259 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, rb0); return 0
1260 }
1261 // folder/id = the TITLE (dn=), sanitized -- so Downloads shows "javdb.com_326FCT-218/" not a raw
1262 // hash. Falls back to the info-hash only when the magnet carries no dn=.
1263 let id: *u8 = sys_mmap(512); var idl: i64 = d_extract_name(mag, mn, id, 512)
1264 if idl <= 0 { idl = d_extract_btih(mag, mn, id, 512) }
1265 let dir: *u8 = d_subdir(aroot, id, sys_mmap(640))
1266 sys_mkdir(dir, 0x1ff)
1267 d_write_file(d_path(dir, "/download.magnet" as *u8, sys_mmap(640)), mag, mn)
1268 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1269 d_idx_add(aidx, id)
1270 d_spawn_admitted(mag, dir, d_has_progress(dir), nsfw_root, "add" as *u8) // RESUME if a partial download already exists (idempotent re-add); DEFERRED when the I/O budget is spent (the sweep re-asks)
1271 let rb: *u8 = sys_mmap(256); var ro: i64 = d_puts(rb, 0, "{\"id\":\"" as *u8); ro = d_puts(rb, ro, id); ro = d_puts(rb, ro, "\"}" as *u8); rb[ro] = 0 as u8
1272 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, rb); return 0
1273 }
1274 if d_streq(req,po,pl,"/torrent/api/discover" as *u8)==1 {
1275 sys_set_socket_timeout(cfd, 2)
1276 var tot: i64 = rn
1277 var go: i64 = 1
1278 while go == 1 {
1279 if tot >= TORRENT_MAGIC_16384 { go = 0 } else {
1280 let r: i64 = sys_read(cfd, req + tot, TORRENT_MAGIC_16384 - tot)
1281 if r <= 0 { go = 0 } else { tot = tot + r }
1282 }
1283 }
1284 let urlb: *u8 = sys_mmap(TORRENT_MAGIC_4096)
1285 let un: i64 = d_extract_url(req + bo, tot - bo, urlb, TORRENT_MAGIC_4096)
1286 if un <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"no url\"}" as *u8); return 0 }
1287 d_spawn_discover(urlb, discpath)
1288 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0
1289 }
1290 if d_streq(req,po,pl,"/torrent/api/video" as *u8)==1 {
1291 sys_set_socket_timeout(cfd, 2)
1292 var tot: i64 = rn; var go: i64 = 1
1293 while go == 1 { if tot >= TORRENT_MAGIC_16384 { go = 0 } else { let r: i64 = sys_read(cfd, req + tot, TORRENT_MAGIC_16384 - tot); if r <= 0 { go = 0 } else { tot = tot + r } } }
1294 let urlb: *u8 = sys_mmap(TORRENT_MAGIC_4096); let un: i64 = d_extract_url(req + bo, tot - bo, urlb, TORRENT_MAGIC_4096)
1295 if un <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"no url\"}" as *u8); return 0 }
1296 let varea: i64 = d_parse_area(req, tot)
1297 if nx_taccess_allow(level, varea) != NX_TACCESS_ALLOW { d_send_str(cfd, "403 Forbidden" as *u8, "application/json" as *u8, "{\"error\":\"access denied for this area\"}" as *u8); return 0 }
1298 var vroot: *u8 = sfw_root; if varea == NX_TAREA_NSFW { vroot = nsfw_root }
1299 let vdir: *u8 = d_path(vroot, "/videos" as *u8, sys_mmap(640)); sys_mkdir(vdir, 0x1ff)
1300 let vref: *u8 = d_url_referer(urlb, sys_mmap(TORRENT_MAGIC_4200)) // same-origin Referer so CDNs that gate direct links serve the bytes (empty = #1 cause of 403)
1301 if d_is_hls(urlb) == 1 { // HLS .m3u8 -> fetch+stitch (+AES decrypt) all segments via nx_hls_get
1302 let houtp: *u8 = d_hls_outname(urlb, vdir, sys_mmap(TORRENT_MAGIC_1024))
1303 d_spawn_hls(urlb, houtp, vref)
1304 } else {
1305 let outp: *u8 = d_vidname(urlb, vdir, sys_mmap(TORRENT_MAGIC_1024))
1306 d_spawn_video(urlb, outp, vref)
1307 }
1308 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0
1309 }
1310 // MULTI-TORRENT control: /torrent/api/<id>/{pause,resume,remove}, id = the torrent's btih.
1311 if d_starts(req,po,pl,"/torrent/api/" as *u8)==1 {
1312 var act: i64 = 0; var sl: i64 = 0
1313 if d_ends(req,po,pl,"/pause" as *u8)==1 { act = 1; sl = 6 }
1314 if d_ends(req,po,pl,"/resume" as *u8)==1 { act = 2; sl = 7 }
1315 if d_ends(req,po,pl,"/remove" as *u8)==1 { act = 3; sl = 7 }
1316 if d_ends(req,po,pl,"/reannounce" as *u8)==1 { act = 4; sl = 11 } // the UNSTICK lever
1317 if d_ends(req,po,pl,"/recheck" as *u8)==1 { act = 5; sl = 8 }
1318 if act > 0 {
1319 var idlen: i64 = pl - 13 - sl // strip "/torrent/api/" (13) + the action suffix
1320 if idlen > 127 { idlen = 127 } // defensive: id buffer is 128
1321 if idlen > 0 {
1322 let id: *u8 = sys_mmap(128); var k: i64 = 0; while k < idlen { if k < 127 { id[k] = req[po+13+k]; k = k + 1 } else { k = idlen } } id[idlen] = 0 as u8
1323 let dir: *u8 = sys_mmap(640); let ca: *i64 = sys_mmap(16) as *i64
1324 if d_resolve_dir(id, level, sfw_root, sfw_idx, nsfw_root, nsfw_idx, dir, ca) == 1 {
1325 if act == 1 { d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "paused" as *u8, 6) }
1326 if act == 2 {
1327 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1328 let mp: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640)); let mb2: *u8 = sys_mmap(TORRENT_MAGIC_4096); let mln: i64 = d_read_file(mp, mb2, TORRENT_MAGIC_4095)
1329 if mln > 0 { mb2[mln] = 0 as u8; d_spawn_admitted(mb2, dir, 1, nsfw_root, "resume" as *u8) }
1330 }
1331 if act == 3 {
1332 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "removed" as *u8, 7)
1333 if ca[0] == NX_TAREA_NSFW { d_idx_remove(nsfw_idx, id) } else { d_idx_remove(sfw_idx, id) }
1334 }
1335 if act == 4 { // force-reannounce: respawn the worker -> re-hits trackers/DHT + re-discovers peers (the UNSTICK for IGNORING torrents)
1336 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1337 let mp4: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640)); let mb4: *u8 = sys_mmap(TORRENT_MAGIC_4096); let ml4: i64 = d_read_file(mp4, mb4, TORRENT_MAGIC_4095)
1338 if ml4 > 0 { mb4[ml4] = 0 as u8; d_spawn_admitted(mb4, dir, 1, nsfw_root, "reannounce" as *u8) }
1339 }
1340 if act == 5 { // force-recheck: signal the worker to re-verify on-disk pieces
1341 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "recheck" as *u8, 7)
1342 }
1343 }
1344 }
1345 }
1346 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0
1347 }
1348 d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "not found\n" as *u8); return 0
1349 }
1350 d_send_str(cfd, "405 Method Not Allowed" as *u8, "text/plain" as *u8, "method\n" as *u8)
1351 return 0
1352}
1353
1354func main(argc: i64, argv: *i64) -> i64 {
1355 // TWO download AREAS (operator 2026-06-20): a lower-privilege "media" area (family+) and an owner-only area.
1356 // Each area = its OWN root + registry, so a member's /status never even scans the owner root (isolation by
1357 // construction). DATA-DRIVEN ROOTS (CLAUDE.md #17): argv overrides the workstation defaults so the SAME binary
1358 // runs NAS-native -- argv[1]=media/SFW root (e.g. /volume1/movies), argv[2]=owner/NSFW root (e.g. /volume1/logging),
1359 // argv[3]=the UI shell file. idx/discover are derived from the roots. The worker stays /tmp (deployed at launch).
1360 var sfw_root: *u8 = "/mnt/c/Users/elder/Downloads/nishi-media" as *u8
1361 var nsfw_root: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents" as *u8
1362 var indexp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/sites/nishifamily/torrent/index.html" as *u8
1363 if argc > 1 { sfw_root = argv[1] as *u8 }
1364 if argc > 2 { nsfw_root = argv[2] as *u8 }
1365 if argc > 3 { indexp = argv[3] as *u8 }
1366 let sfw_idx: *u8 = d_path(sfw_root, "/torrents.idx" as *u8, sys_mmap(700))
1367 let nsfw_idx: *u8 = d_path(nsfw_root, "/torrents.idx" as *u8, sys_mmap(700))
1368 let discpath: *u8 = d_path(nsfw_root, "/discover.tsv" as *u8, sys_mmap(700))
1369 let appjsp: *u8 = indexp // the emitted shell is self-contained; /torrent/app.js serves it too (harmless)
1370 sys_mkdir(sfw_root, 0x1ff); sys_mkdir(nsfw_root, 0x1ff) // ensure both areas exist
1371
1372 let addr: *u8 = sys_mmap(16)
1373 nx_http_server_addr_any(addr, TORRENT_MAGIC_8097) // 0.0.0.0:TORRENT_MAGIC_8097 base; tightened to loopback below unless bind_mode="any"
1374 // SECURITY (s-class): behind the OPAQUE gateway the daemon TRUSTS the injected X-Nishi-Level header, so a
1375 // LAN-direct client reaching 0.0.0.0:8097 could FORGE that header and bypass auth. Default + the NAS "hub"
1376 // profile therefore bind LOOPBACK (only the loopback gateway on :18793 reaches :8097). Only an explicit
1377 // bind_mode="any" (argv[4]) keeps 0.0.0.0 -- the dev laptop needs it for WSL2 localhost-forwarding.
1378 var bind_any: i64 = 0
1379 if argc > 4 { if d_streq(argv[4] as *u8, 0, 3, "any" as *u8) == 1 { bind_any = 1 } }
1380 if bind_any == 0 {
1381 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8
1382 let bl: *u8 = "TORRENTD bind=LOOPBACK 127.0.0.1:8097 (gateway-only)\n" as *u8; sys_write(1, bl, d_strlen(bl))
1383 } else {
1384 let ba: *u8 = "TORRENTD bind=ANY 0.0.0.0:8097 (dev/WSL2)\n" as *u8; sys_write(1, ba, d_strlen(ba))
1385 }
1386 let vp: *i64 = sys_mmap(16) as *i64
1387 let lfd: i64 = nx_http_server_listen(addr, 16, vp)
1388 if lfd < 0 {
1389 sys_write(1, "TORRENTD bind FAILED (port 8097 in use?)\n" as *u8, 41); sys_exit(1); return 1
1390 }
1391 // FD_CLOEXEC on the listen socket: the download WORKERS are fork+execve'd from request-handling
1392 // children and would otherwise INHERIT this :8097 fd, holding the port alive after the daemon dies
1393 // and blocking every restart (the bug that wedged the redeploy). CLOEXEC closes it across execve.
1394 __syscall(72, lfd, 2, 1, 0, 0, 0) // fcntl(lfd, F_SETFD=2, FD_CLOEXEC=1) -> x86_64 nr 72
1395 // the length is DERIVED, never hand-counted: this line was written as 48 for a 49-byte literal and dropped
1396 // its own newline, gluing the next log line onto it (rule 11: a hand-counted length beside a literal drifts).
1397 let ll: *u8 = "TORRENTD listening http://127.0.0.1:8097/torrent\n" as *u8; sys_write(1, ll, d_strlen(ll))
1398 // crash/reboot recovery: resume active downloads in BOTH areas whose worker died -- under ONE I/O budget
1399 // for the whole pass (measured once), so recovery cannot re-create the storm it is recovering from.
1400 let bud0: *i64 = sys_mmap(D_BUD_BYTES) as *i64
1401 d_admit_budget(nsfw_root, bud0)
1402 let rsm: i64 = d_autoresume(nsfw_root, nsfw_idx, bud0, 0) + d_autoresume(sfw_root, sfw_idx, bud0, 0)
1403 d_admit_log("startup" as *u8, bud0)
1404 let rb1: *u8 = sys_mmap(64); var ro1: i64 = d_puts(rb1, 0, "TORRENTD autoresume resumed=" as *u8); ro1 = d_putn(rb1, ro1, rsm); rb1[ro1] = 10 as u8; sys_write(1, rb1, ro1 + 1)
1405
1406 // ---- PERSISTENT RETRY SWEEP (rung #2: the poorly-seeded "stuck at 99%" fix) ----------------------
1407 // A detached child re-runs the resume sweep every D_RETRY_INTERVAL_MS so a torrent whose worker gave
1408 // up (~20min of no new pieces) keeps getting re-spawned -> re-announces (re-triggering the wide
1409 // tracker scan, rung #1) -> it finishes whenever a seed with the missing tail piece appears. The sweep
1410 // REUSES d_autoresume, which already skips COMPLETE + paused + live-worker torrents, so only genuinely
1411 // stuck ones are re-spawned. SINGLE-INSTANCE via a generation file (.sweeper.gen): on a daemon restart
1412 // the new sweeper claims a fresh gen, so the old (reparented-to-init) one retires on its next wake ->
1413 // no sweeper pile-up, no tracker-ban spam. fds 3..255 closed so it never holds the :8097 listen socket.
1414 let genp: *u8 = d_path(nsfw_root, "/.sweeper.gen" as *u8, sys_mmap(700))
1415 let swp1: i64 = sys_fork()
1416 if swp1 == 0 {
1417 let swp2: i64 = sys_fork()
1418 if swp2 == 0 {
1419 var sfdx: i64 = 3; while sfdx < 256 { sys_close(sfdx); sfdx = sfdx + 1 }
1420 let gts: *i64 = sys_mmap(16) as *i64; sys_clock_gettime_real(gts); let mygen: i64 = gts[0]*TORRENT_MAGIC_1000000000 + gts[1]
1421 let gbuf: *u8 = sys_mmap(32); let gl: i64 = d_putn(gbuf, 0, mygen); d_write_file(genp, gbuf, gl)
1422 var run: i64 = 1
1423 var sweep_no: i64 = 0 // the sweep ordinal = the registry rotation index (round-robin under a budget)
1424 let budS: *i64 = sys_mmap(D_BUD_BYTES) as *i64
1425 while run == 1 {
1426 sys_sleep_ms(D_RETRY_INTERVAL_MS)
1427 let rbuf: *u8 = sys_mmap(32); let rgn: i64 = d_read_file(genp, rbuf, 31)
1428 var cur: i64 = 0; if rgn > 0 { cur = d_int_at(rbuf, rgn, 0) }
1429 if cur != mygen { run = 0 } else {
1430 sweep_no = sweep_no + 1
1431 d_admit_budget(nsfw_root, budS)
1432 let got: i64 = d_autoresume(nsfw_root, nsfw_idx, budS, sweep_no) + d_autoresume(sfw_root, sfw_idx, budS, sweep_no)
1433 d_admit_log("retry-sweep" as *u8, budS)
1434 let lb: *u8 = sys_mmap(64); var lo: i64 = d_puts(lb, 0, "TORRENTD retry-sweep re-spawned=" as *u8); lo = d_putn(lb, lo, got); lb[lo] = 10 as u8; sys_write(1, lb, lo + 1)
1435 }
1436 }
1437 sys_exit(0)
1438 }
1439 sys_exit(0)
1440 }
1441 let swst: *i64 = sys_mmap(16) as *i64; sys_wait4(swp1, swst, 0)
1442
1443 let req: *u8 = sys_mmap(TORRENT_MAGIC_16384)
1444 let m: *i64 = sys_mmap(16) as *i64; let po: *i64 = sys_mmap(16) as *i64; let pl: *i64 = sys_mmap(16) as *i64
1445 let cl_: *i64= sys_mmap(16) as *i64; let bo: *i64 = sys_mmap(16) as *i64; let rn: *i64 = sys_mmap(16) as *i64
1446
1447 // fork-per-connection: a browser opens several sockets (page, app.js, /me, then
1448 // /status every second). A single blocking accept-loop would stall if any one client
1449 // is slow/half-open. Each connection is handled in a disposable child; the parent
1450 // just accepts + reaps. Children are bounded by a socket read timeout (no-hang law).
1451 let st: *i64 = sys_mmap(16) as *i64
1452 var serving: i64 = 1
1453 while serving == 1 {
1454 let cfd: i64 = nx_http_server_accept_one(lfd, vp)
1455 if cfd >= 0 {
1456 let pid: i64 = sys_fork()
1457 if pid == 0 {
1458 sys_set_socket_timeout(cfd, 10)
1459 let rc: i64 = nx_http_server_read_request(cfd, req, TORRENT_MAGIC_16384, m, po, pl, cl_, bo, rn)
1460 if rc == 0 {
1461 d_route(cfd, req, m[0], po[0], pl[0], cl_[0], bo[0], rn[0], sfw_root, sfw_idx, nsfw_root, nsfw_idx, indexp, appjsp, discpath)
1462 } else {
1463 d_send_str(cfd, "400 Bad Request" as *u8, "text/plain" as *u8, "bad request\n" as *u8)
1464 }
1465 sys_exit(0)
1466 }
1467 sys_close(cfd)
1468 var reap: i64 = 1
1469 while reap == 1 { let w: i64 = sys_wait4(0 - 1, st, WNOHANG); if w <= 0 { reap = 0 } }
1470 }
1471 }
1472 return 0
1473}