nx_pages_config.nx source
↩ module page · 208 lines · 7387 B
1// nx_pages_config.nx -- D1.6 of NISHI_DEPLOY_ROADMAP. Loads vhost map
2// from a config file so nx_pages_daemon + nx_pages_https_daemon can be
3// deployed without recompiling for per-host doc-root changes.
4//
5// File format (one entry per line, very simple to keep the parser tiny):
6//
7// # nishifamily.com primary
8// vhost=nishifamily.com:/share/nishi-pages/dist
9// vhost=www.nishifamily.com:/share/nishi-pages/dist
10// vhost=andelinwest.com:/share/andelin-pages/dist
11// fallback=0
12//
13// Rules:
14// - Blank lines + lines starting with '#' are ignored.
15// - 'vhost=<host>:<root>' adds a vhost (order preserved; fallback_idx
16// references this 0-based ordering).
17// - 'fallback=<idx>' picks the fallback vhost. Omit -> no fallback.
18// - All other keys (http_port, https_port, cert_path, key_path) are
19// recognized but consumed via separate accessor functions; the
20// parser stores their string values into a flat string arena and
21// exposes lookup by name.
22//
23// Per NISHI_DEPLOY_ROADMAP D1.6. Companion: SIGHUP zero-downtime reload
24// is deferred to D1.6.5.
25
26import "nx_syscalls.nx"
27import "nx_pages_vhost.nx"
28
29const NX_PCFG_OK: i64 = 0
30const NX_PCFG_ERR_OPEN: i64 = 1
31const NX_PCFG_ERR_READ: i64 = 2
32const NX_PCFG_ERR_TOO_BIG: i64 = 3
33const NX_PCFG_ERR_PARSE: i64 = 4
34const NX_PCFG_ERR_VHOST_OOM: i64 = 5
35
36const NX_PCFG_MAX_FILE_BYTES: i64 = 65536 // 64 KB conf cap (V1)
37const NX_PCFG_MAX_VHOSTS: i64 = 64
38
39// ===== Byte helpers ================================================
40
41func _pcfg_is_ws(b: i64) -> i64 {
42 if b == 0x20 { return 1 }
43 if b == 0x09 { return 1 }
44 return 0
45}
46
47func _pcfg_strlen(s: *u8) -> i64 {
48 var n: i64 = 0
49 while s[n] != 0 { n = n + 1 }
50 return n
51}
52
53func _pcfg_byte_eq(a: *u8, ao: i64, b: *u8, bo: i64, n: i64) -> i64 {
54 var i: i64 = 0
55 while i < n {
56 if a[ao + i] != b[bo + i] { return 0 }
57 i = i + 1
58 }
59 return 1
60}
61
62// Scan for the first occurrence of `needle_byte` within
63// [buf+start, buf+end). Returns position or `end` (sentinel).
64func _pcfg_find_byte(buf: *u8, start: i64, end: i64, needle: i64) -> i64 {
65 var p: i64 = start
66 var done: i64 = 0
67 while done == 0 {
68 if p >= end { done = 1 }
69 if done == 0 {
70 if buf[p] as i64 == needle { done = 1 }
71 if done == 0 { p = p + 1 }
72 }
73 }
74 return p
75}
76
77// Parse decimal i64 from buf[start..end-1]. Returns parsed value via
78// *out_val + 1 on success, 0 on failure.
79func _pcfg_parse_i64(buf: *u8, start: i64, end: i64, out_val: *i64) -> i64 {
80 if start >= end { return 0 }
81 var i: i64 = start
82 var sign: i64 = 1
83 if buf[i] == 0x2d { sign = -1; i = i + 1 } // '-'
84 if i >= end { return 0 }
85 var acc: i64 = 0
86 while i < end {
87 let b: i64 = buf[i] as i64
88 if b < 0x30 { return 0 }
89 if b > 0x39 { return 0 }
90 acc = acc * 10 + (b - 0x30)
91 i = i + 1
92 }
93 out_val[0] = acc * sign
94 return 1
95}
96
97// ===== Public: load a config file into a vhost table =================
98//
99// Reads the file at `path`, parses vhost= and fallback= lines, populates
100// the caller-allocated NxVHostTable. Caller is responsible for the
101// arena + vhosts array sizing.
102//
103// Returns NX_PCFG_OK or one of the NX_PCFG_ERR_* codes.
104
105func nx_pages_config_load(
106 path: *u8,
107 t: *NxVHostTable,
108 cursor: *i64
109) -> i64 {
110 let file_len_box: *i64 = sys_mmap(8) as *i64
111 let buf: *u8 = sys_read_file(path, file_len_box)
112 if (buf as i64) == 0 { return NX_PCFG_ERR_OPEN }
113 let n: i64 = file_len_box[0]
114 if n <= 0 { return NX_PCFG_ERR_READ }
115 if n > NX_PCFG_MAX_FILE_BYTES { return NX_PCFG_ERR_TOO_BIG }
116
117 let vhost_key: *u8 = "vhost" as *u8
118 let fallback_key: *u8 = "fallback" as *u8
119
120 var pos: i64 = 0
121 while pos < n {
122 // Find end-of-line (LF) or end-of-file.
123 let lf: i64 = _pcfg_find_byte(buf, pos, n, 0x0a)
124
125 // Skip leading whitespace. Use done-flag pattern so ls stays
126 // at the captured first-non-ws position (the sentinel-exit
127 // anti-pattern would clobber it -- see feedback memo
128 // feedback-nishilang-sentinel-exit-clobbers-position).
129 var ls: i64 = pos
130 var ls_done: i64 = 0
131 while ls_done == 0 {
132 if ls >= lf { ls_done = 1 }
133 if ls_done == 0 {
134 if _pcfg_is_ws(buf[ls] as i64) == 1 { ls = ls + 1 }
135 if _pcfg_is_ws(buf[ls] as i64) == 0 { ls_done = 1 }
136 }
137 }
138
139 // Trim trailing CR if present.
140 var le: i64 = lf
141 if le > ls {
142 if buf[le - 1] == 0x0d { le = le - 1 }
143 }
144
145 // Skip blank lines + comments.
146 var is_blank: i64 = 0
147 if ls >= le { is_blank = 1 }
148 if is_blank == 0 {
149 if buf[ls] == 0x23 { is_blank = 1 } // '#'
150 }
151
152 if is_blank == 0 {
153 // Find '=' separator.
154 let eq: i64 = _pcfg_find_byte(buf, ls, le, 0x3d)
155 if eq < le {
156 let key_off: i64 = ls
157 let key_len: i64 = eq - ls
158 let val_off: i64 = eq + 1
159 let val_len: i64 = le - val_off
160
161 // vhost=<host>:<root>
162 if key_len == 5 {
163 if _pcfg_byte_eq(buf, key_off, vhost_key, 0 as i64, 5 as i64) == 1 {
164 // Find ':' inside the value.
165 let colon: i64 = _pcfg_find_byte(buf, val_off, val_off + val_len,
166 0x3a as i64)
167 if colon < val_off + val_len {
168 // Copy host + root into temp null-terminated cstrs.
169 let host_n: i64 = colon - val_off
170 let root_n: i64 = (val_off + val_len) - (colon + 1)
171 let host_cs: *u8 = sys_mmap(host_n + 1)
172 let root_cs: *u8 = sys_mmap(root_n + 1)
173 var hi: i64 = 0
174 while hi < host_n {
175 host_cs[hi] = buf[val_off + hi]
176 hi = hi + 1
177 }
178 host_cs[host_n] = 0 as u8
179 var ri: i64 = 0
180 while ri < root_n {
181 root_cs[ri] = buf[colon + 1 + ri]
182 ri = ri + 1
183 }
184 root_cs[root_n] = 0 as u8
185 if t.n_vhosts < NX_PCFG_MAX_VHOSTS {
186 nx_vhost_add(t, cursor, host_cs, root_cs)
187 }
188 }
189 }
190 }
191 // fallback=<idx>
192 if key_len == 8 {
193 if _pcfg_byte_eq(buf, key_off, fallback_key, 0 as i64, 8 as i64) == 1 {
194 let fb: *i64 = sys_mmap(8) as *i64
195 if _pcfg_parse_i64(buf, val_off, val_off + val_len, fb) == 1 {
196 nx_vhost_set_fallback(t, fb[0])
197 }
198 }
199 }
200 }
201 }
202
203 // Advance to next line (past LF).
204 if lf < n { pos = lf + 1 }
205 if lf >= n { pos = n }
206 }
207 return NX_PCFG_OK
208}