@ai-ecoverse/slicc-kernel 1.182.0 → 1.183.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -5
- package/dist/driver-worker.js +290 -90
- package/dist/driver-worker.js.map +4 -4
- package/dist/index.js +328 -102
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +1705 -695
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +298 -98
- package/dist/node-driver-worker.js.map +4 -4
- package/dist/node-process-worker.cjs +849 -193
- package/dist/node-process-worker.cjs.map +4 -4
- package/dist/node-process-worker.js +849 -193
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +1875 -743
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +805 -149
- package/dist/process-worker.js.map +4 -4
- package/dist/types/client/attach.d.ts +2 -0
- package/dist/types/client/protocol.d.ts +1 -0
- package/dist/types/client/serve-client.d.ts +1 -0
- package/dist/types/devices-relay.d.ts +74 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/kernel/devices.d.ts +155 -0
- package/dist/types/kernel/embedder.d.ts +2 -0
- package/dist/types/kernel/fd-table.d.ts +3 -1
- package/dist/types/kernel/host.d.ts +2 -0
- package/dist/types/kernel/process.d.ts +20 -0
- package/dist/types/kernel/serial-ioctl.d.ts +73 -0
- package/dist/types/launcher.d.ts +4 -0
- package/dist/types/node.d.ts +6 -2
- package/dist/types/process/js/js-dev.d.ts +24 -0
- package/dist/types/process/kernel-streams.d.ts +11 -1
- package/dist/types/process/process-pty.d.ts +1 -0
- package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
- package/dist/types/process/wasi/wasi-termios.d.ts +1 -0
- package/dist/types/serve.d.ts +14 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -96,11 +96,231 @@ function portCdpHook(ask, gone) {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// src/
|
|
99
|
+
// src/devices-relay.ts
|
|
100
|
+
function quiet() {
|
|
101
|
+
return void 0;
|
|
102
|
+
}
|
|
103
|
+
function errorOf(err) {
|
|
104
|
+
const name2 = err?.name;
|
|
105
|
+
const message3 = err instanceof Error ? err.message : String(err);
|
|
106
|
+
return { name: typeof name2 === "string" ? name2 : "Error", message: message3 };
|
|
107
|
+
}
|
|
108
|
+
function raised(failure) {
|
|
109
|
+
return Object.assign(new Error(failure.message), { name: failure.name });
|
|
110
|
+
}
|
|
100
111
|
function fresh2(bytes) {
|
|
101
112
|
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
|
|
102
113
|
}
|
|
103
|
-
function
|
|
114
|
+
function devicesAsker(port) {
|
|
115
|
+
const remotes = /* @__PURE__ */ new Map();
|
|
116
|
+
let next3 = 0;
|
|
117
|
+
const post = (message3, transfer = []) => port.postMessage({ devices: message3 }, transfer);
|
|
118
|
+
const push = (remote, item) => {
|
|
119
|
+
remote.chunks.push(item);
|
|
120
|
+
remote.wake?.();
|
|
121
|
+
};
|
|
122
|
+
const streams = (id2, remote) => ({
|
|
123
|
+
readable: new ReadableStream(
|
|
124
|
+
{
|
|
125
|
+
pull: async (controller) => {
|
|
126
|
+
if (remote.chunks.length === 0) {
|
|
127
|
+
post({ id: id2, pull: true });
|
|
128
|
+
await new Promise((resolve2) => {
|
|
129
|
+
remote.wake = resolve2;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const item = remote.chunks.shift();
|
|
133
|
+
if (item instanceof Uint8Array) controller.enqueue(item);
|
|
134
|
+
else if (item === null || item === void 0) controller.close();
|
|
135
|
+
else controller.error(raised(item));
|
|
136
|
+
},
|
|
137
|
+
cancel: (reason) => {
|
|
138
|
+
post({ id: id2, cancel: String(reason ?? "closed") });
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{ highWaterMark: 0 }
|
|
142
|
+
),
|
|
143
|
+
writable: new WritableStream({
|
|
144
|
+
write: (chunk2) => {
|
|
145
|
+
const seq2 = ++remote.seq;
|
|
146
|
+
const done4 = Promise.withResolvers();
|
|
147
|
+
remote.writes.set(seq2, done4);
|
|
148
|
+
const bytes = fresh2(chunk2);
|
|
149
|
+
post({ id: id2, write: bytes, seq: seq2 }, [bytes.buffer]);
|
|
150
|
+
return done4.promise;
|
|
151
|
+
},
|
|
152
|
+
abort: (reason) => {
|
|
153
|
+
post({ id: id2, cancel: String(reason ?? "closed") });
|
|
154
|
+
}
|
|
155
|
+
}),
|
|
156
|
+
control: (op, args) => {
|
|
157
|
+
const seq2 = ++remote.seq;
|
|
158
|
+
const done4 = Promise.withResolvers();
|
|
159
|
+
remote.controls.set(seq2, done4);
|
|
160
|
+
post({ id: id2, control: { seq: seq2, op, ...args ? { args } : {} } });
|
|
161
|
+
if (op === "close") void done4.promise.finally(() => remotes.delete(id2)).catch(quiet);
|
|
162
|
+
return done4.promise;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
const hookFor = (client) => ({
|
|
166
|
+
open: async (request) => {
|
|
167
|
+
const id2 = ++next3;
|
|
168
|
+
const remote = {
|
|
169
|
+
opened: Promise.withResolvers(),
|
|
170
|
+
chunks: [],
|
|
171
|
+
wake: void 0,
|
|
172
|
+
writes: /* @__PURE__ */ new Map(),
|
|
173
|
+
controls: /* @__PURE__ */ new Map(),
|
|
174
|
+
seq: 0
|
|
175
|
+
};
|
|
176
|
+
remotes.set(id2, remote);
|
|
177
|
+
const abort = () => {
|
|
178
|
+
remotes.delete(id2);
|
|
179
|
+
post({ id: id2, cancel: "interrupted" });
|
|
180
|
+
remote.opened.reject(request.signal.reason);
|
|
181
|
+
};
|
|
182
|
+
request.signal.addEventListener("abort", abort, { once: true });
|
|
183
|
+
const { path, flags: flags2, stamp } = request;
|
|
184
|
+
post({ id: id2, open: { client, path, flags: flags2, stamp } });
|
|
185
|
+
try {
|
|
186
|
+
await remote.opened.promise;
|
|
187
|
+
} finally {
|
|
188
|
+
request.signal.removeEventListener("abort", abort);
|
|
189
|
+
}
|
|
190
|
+
return streams(id2, remote);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
const answer2 = (message3) => {
|
|
194
|
+
const remote = remotes.get(message3.id);
|
|
195
|
+
if (!remote) return;
|
|
196
|
+
if ("opened" in message3) remote.opened.resolve();
|
|
197
|
+
else if ("failed" in message3) {
|
|
198
|
+
remotes.delete(message3.id);
|
|
199
|
+
remote.opened.reject(raised(message3.failed));
|
|
200
|
+
} else if ("chunk" in message3) push(remote, message3.chunk);
|
|
201
|
+
else if ("done" in message3) push(remote, null);
|
|
202
|
+
else if ("readError" in message3) push(remote, message3.readError);
|
|
203
|
+
else if ("wrote" in message3) {
|
|
204
|
+
remote.writes.get(message3.wrote)?.resolve();
|
|
205
|
+
remote.writes.delete(message3.wrote);
|
|
206
|
+
} else if ("writeError" in message3) {
|
|
207
|
+
remote.writes.get(message3.seq)?.reject(raised(message3.writeError));
|
|
208
|
+
remote.writes.delete(message3.seq);
|
|
209
|
+
} else if ("answered" in message3) {
|
|
210
|
+
remote.controls.get(message3.answered)?.resolve(message3.value);
|
|
211
|
+
remote.controls.delete(message3.answered);
|
|
212
|
+
} else {
|
|
213
|
+
remote.controls.get(message3.refused)?.reject(raised(message3.error));
|
|
214
|
+
remote.controls.delete(message3.refused);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
return { hookFor, answer: answer2 };
|
|
218
|
+
}
|
|
219
|
+
function devicesAnswerer(port, hookFor) {
|
|
220
|
+
const locals = /* @__PURE__ */ new Map();
|
|
221
|
+
const post = (message3, transfer = []) => port.postMessage({ devicesReply: message3 }, transfer);
|
|
222
|
+
const open = async (id2, asked) => {
|
|
223
|
+
const local2 = { abort: new AbortController() };
|
|
224
|
+
locals.set(id2, local2);
|
|
225
|
+
try {
|
|
226
|
+
const hook = hookFor(asked.client);
|
|
227
|
+
if (!hook)
|
|
228
|
+
throw Object.assign(new Error("this page has no devices hook"), {
|
|
229
|
+
name: "NotSupportedError"
|
|
230
|
+
});
|
|
231
|
+
const streams = await hook.open({
|
|
232
|
+
path: asked.path,
|
|
233
|
+
flags: asked.flags,
|
|
234
|
+
stamp: asked.stamp,
|
|
235
|
+
signal: local2.abort.signal
|
|
236
|
+
});
|
|
237
|
+
if (!locals.has(id2)) {
|
|
238
|
+
await streams?.readable?.cancel("closed").catch(quiet);
|
|
239
|
+
await streams?.writable?.abort("closed").catch(quiet);
|
|
240
|
+
await Promise.resolve(streams?.control?.("close")).catch(quiet);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
local2.streams = streams;
|
|
244
|
+
local2.reader = streams.readable.getReader();
|
|
245
|
+
local2.writer = streams.writable.getWriter();
|
|
246
|
+
post({ id: id2, opened: true });
|
|
247
|
+
} catch (err) {
|
|
248
|
+
locals.delete(id2);
|
|
249
|
+
post({ id: id2, failed: errorOf(err) });
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const pull = async (id2, local2) => {
|
|
253
|
+
try {
|
|
254
|
+
const got = await local2.reader.read();
|
|
255
|
+
if (got.done) return post({ id: id2, done: true });
|
|
256
|
+
const bytes = fresh2(got.value);
|
|
257
|
+
post({ id: id2, chunk: bytes }, [bytes.buffer]);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
post({ id: id2, readError: errorOf(err) });
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const write = async (id2, local2, bytes, seq2) => {
|
|
263
|
+
try {
|
|
264
|
+
await local2.writer.write(bytes);
|
|
265
|
+
post({ id: id2, wrote: seq2 });
|
|
266
|
+
} catch (err) {
|
|
267
|
+
post({ id: id2, writeError: errorOf(err), seq: seq2 });
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const control = async (id2, local2, asked) => {
|
|
271
|
+
try {
|
|
272
|
+
await local2.closing;
|
|
273
|
+
const value2 = await local2.streams.control(asked.op, asked.args);
|
|
274
|
+
post({ id: id2, answered: asked.seq, ...value2 !== void 0 ? { value: value2 } : {} });
|
|
275
|
+
} catch (err) {
|
|
276
|
+
post({ id: id2, refused: asked.seq, error: errorOf(err) });
|
|
277
|
+
}
|
|
278
|
+
if (asked.op === "close") locals.delete(id2);
|
|
279
|
+
};
|
|
280
|
+
const release = async (local2, reason) => {
|
|
281
|
+
const { reader, writer } = local2;
|
|
282
|
+
local2.reader = void 0;
|
|
283
|
+
local2.writer = void 0;
|
|
284
|
+
await reader?.cancel(reason).catch(quiet);
|
|
285
|
+
reader?.releaseLock();
|
|
286
|
+
await writer?.abort(reason).catch(quiet);
|
|
287
|
+
writer?.releaseLock();
|
|
288
|
+
};
|
|
289
|
+
const cancel = (id2, local2, reason) => {
|
|
290
|
+
local2.abort.abort(new DOMException(reason, "AbortError"));
|
|
291
|
+
if (!local2.streams) {
|
|
292
|
+
locals.delete(id2);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
local2.closing = Promise.resolve(local2.closing).then(() => release(local2, reason));
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
handle(data) {
|
|
299
|
+
const message3 = data?.devices;
|
|
300
|
+
if (!message3) return false;
|
|
301
|
+
if ("open" in message3) {
|
|
302
|
+
void open(message3.id, message3.open);
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
const local2 = locals.get(message3.id);
|
|
306
|
+
if (!local2) return true;
|
|
307
|
+
if ("pull" in message3) void pull(message3.id, local2);
|
|
308
|
+
else if ("write" in message3) void write(message3.id, local2, message3.write, message3.seq);
|
|
309
|
+
else if ("control" in message3) void control(message3.id, local2, message3.control);
|
|
310
|
+
else cancel(message3.id, local2, message3.cancel);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function devicesReplyOf(data) {
|
|
316
|
+
return data?.devicesReply;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/embedder-relay.ts
|
|
320
|
+
function fresh3(bytes) {
|
|
321
|
+
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
|
|
322
|
+
}
|
|
323
|
+
function errorOf2(err) {
|
|
104
324
|
const name2 = err?.name;
|
|
105
325
|
const message3 = err instanceof Error ? err.message : String(err);
|
|
106
326
|
return { name: typeof name2 === "string" ? name2 : "Error", message: message3 };
|
|
@@ -143,7 +363,7 @@ function embedderAsker(port) {
|
|
|
143
363
|
const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
|
|
144
364
|
if (got.done) post({ id: id2, bodyEnd: true });
|
|
145
365
|
else {
|
|
146
|
-
const bytes =
|
|
366
|
+
const bytes = fresh3(got.value);
|
|
147
367
|
post({ id: id2, body: bytes }, [bytes.buffer]);
|
|
148
368
|
}
|
|
149
369
|
};
|
|
@@ -228,7 +448,7 @@ function embedderAnswerer(port, hookFor) {
|
|
|
228
448
|
const post = (message3, transfer = []) => port.postMessage({ embedderReply: message3 }, transfer);
|
|
229
449
|
const fail3 = (id2, err) => {
|
|
230
450
|
running.delete(id2);
|
|
231
|
-
post({ id: id2, error:
|
|
451
|
+
post({ id: id2, error: errorOf2(err) });
|
|
232
452
|
};
|
|
233
453
|
const start2 = async (ask) => {
|
|
234
454
|
const state = { abort: new AbortController(), body: [], wakeBody: void 0 };
|
|
@@ -263,7 +483,7 @@ function embedderAnswerer(port, hookFor) {
|
|
|
263
483
|
post({ id: id2, done: true });
|
|
264
484
|
return;
|
|
265
485
|
}
|
|
266
|
-
const bytes =
|
|
486
|
+
const bytes = fresh3(got.value);
|
|
267
487
|
post({ id: id2, chunk: bytes }, [bytes.buffer]);
|
|
268
488
|
} catch (err) {
|
|
269
489
|
fail3(id2, err);
|
|
@@ -574,11 +794,11 @@ function permOf(st, link2) {
|
|
|
574
794
|
...link2 !== void 0 ? { link: link2 } : {}
|
|
575
795
|
};
|
|
576
796
|
}
|
|
577
|
-
function permits(cred, perm,
|
|
797
|
+
function permits(cred, perm, want2, directory = true) {
|
|
578
798
|
const mode = perm.mode ?? (directory ? 493 : 420);
|
|
579
|
-
if (cred.euid === 0) return (
|
|
799
|
+
if (cred.euid === 0) return (want2 & X_OK) === 0 || directory || (mode & 73) !== 0;
|
|
580
800
|
const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
|
|
581
|
-
return (mode >> shift &
|
|
801
|
+
return (mode >> shift & want2) === want2;
|
|
582
802
|
}
|
|
583
803
|
function sticky(cred, dir, entry) {
|
|
584
804
|
if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
|
|
@@ -653,27 +873,27 @@ async function present(fs, path, directory) {
|
|
|
653
873
|
const st = await fs.lstat(path).catch(() => void 0);
|
|
654
874
|
return st !== void 0 && (!directory || st.isDirectory);
|
|
655
875
|
}
|
|
656
|
-
async function finalOf(fs, walked,
|
|
876
|
+
async function finalOf(fs, walked, want2, options) {
|
|
657
877
|
const perm = walked.perm;
|
|
658
878
|
if (options.follow === false && perm.link !== void 0) {
|
|
659
879
|
return [{ ...perm, mode: 511 }, false];
|
|
660
880
|
}
|
|
661
|
-
if ((
|
|
881
|
+
if ((want2 & X_OK) === 0 || options.directory !== void 0) {
|
|
662
882
|
return [perm, options.directory !== false];
|
|
663
883
|
}
|
|
664
884
|
const look = options.follow === false ? fs.lstat : fs.stat;
|
|
665
885
|
const st = await look.call(fs, walked.path).catch(() => void 0);
|
|
666
886
|
return st ? [permOf(st), st.isDirectory] : [perm, false];
|
|
667
887
|
}
|
|
668
|
-
async function access(fs, cred, path,
|
|
888
|
+
async function access(fs, cred, path, want2, options = {}) {
|
|
669
889
|
const walked = await walk(fs, cred, path, options.follow !== false, options.links);
|
|
670
890
|
if (walked.denied !== void 0) {
|
|
671
891
|
if (walked.link || await present(fs, walked.denied, true)) return walked;
|
|
672
892
|
return { path, perm: void 0, missing: true };
|
|
673
893
|
}
|
|
674
894
|
if (!walked.perm) return { ...walked, missing: true };
|
|
675
|
-
const [perm, directory] = await finalOf(fs, walked,
|
|
676
|
-
if (
|
|
895
|
+
const [perm, directory] = await finalOf(fs, walked, want2, options);
|
|
896
|
+
if (want2 === 0 || permits(cred, perm, want2, directory)) return walked;
|
|
677
897
|
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
678
898
|
return { ...walked, missing: true };
|
|
679
899
|
}
|
|
@@ -779,11 +999,11 @@ function ttyOpenable(g, tty) {
|
|
|
779
999
|
if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
|
|
780
1000
|
deny(g, "open", tty.name ?? "/dev/tty");
|
|
781
1001
|
}
|
|
782
|
-
async function need(g, op, path,
|
|
783
|
-
if (
|
|
784
|
-
const walked = await access(g.fs, g.cred, path,
|
|
1002
|
+
async function need(g, op, path, want2, options = {}, shown2 = path) {
|
|
1003
|
+
if (want2 & W_OK && shown2 === path) await fence(g, op, path);
|
|
1004
|
+
const walked = await access(g.fs, g.cred, path, want2, linked(g, options));
|
|
785
1005
|
if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown2);
|
|
786
|
-
else if (
|
|
1006
|
+
else if (want2 & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
|
|
787
1007
|
return walked;
|
|
788
1008
|
}
|
|
789
1009
|
async function readsAs(g, op, path, shown2) {
|
|
@@ -932,10 +1152,10 @@ async function creatable(g, op, path) {
|
|
|
932
1152
|
const target = await need(g, op, path, 0);
|
|
933
1153
|
if (target.denied === void 0) await need(g, op, parentOf(target.path), W_OK | X_OK, DIR, path);
|
|
934
1154
|
}
|
|
935
|
-
async function openable(g, op, path,
|
|
1155
|
+
async function openable(g, op, path, want2, create) {
|
|
936
1156
|
const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
|
|
937
1157
|
if (create && !st) return creatable(g, op, path);
|
|
938
|
-
await need(g, op, path,
|
|
1158
|
+
await need(g, op, path, want2, { directory: st?.isDirectory ?? false });
|
|
939
1159
|
}
|
|
940
1160
|
|
|
941
1161
|
// src/kernel/cred.ts
|
|
@@ -1376,8 +1596,8 @@ var FdTable = class _FdTable {
|
|
|
1376
1596
|
}
|
|
1377
1597
|
stdioTerminal() {
|
|
1378
1598
|
for (const fd of [0, 1, 2]) {
|
|
1379
|
-
const
|
|
1380
|
-
if (tty) return tty;
|
|
1599
|
+
const file = this.fds.get(fd)?.file;
|
|
1600
|
+
if (file?.tty && !file.device) return file.tty;
|
|
1381
1601
|
}
|
|
1382
1602
|
return void 0;
|
|
1383
1603
|
}
|
|
@@ -1678,13 +1898,13 @@ function addressBits(host) {
|
|
|
1678
1898
|
function parsePrefix(text) {
|
|
1679
1899
|
const parts2 = text.trim().split("/");
|
|
1680
1900
|
if (parts2.length > 2) return void 0;
|
|
1681
|
-
const [
|
|
1682
|
-
const
|
|
1683
|
-
if (!
|
|
1684
|
-
const max =
|
|
1901
|
+
const [address2 = "", size] = parts2;
|
|
1902
|
+
const parsed3 = addressBits(address2);
|
|
1903
|
+
if (!parsed3) return void 0;
|
|
1904
|
+
const max = parsed3.v6 ? 128 : 32;
|
|
1685
1905
|
const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
1686
1906
|
if (length2 < 0 || length2 > max) return void 0;
|
|
1687
|
-
return { ...
|
|
1907
|
+
return { ...parsed3, length: length2 };
|
|
1688
1908
|
}
|
|
1689
1909
|
function within(prefix, addr) {
|
|
1690
1910
|
if (prefix.v6 !== addr.v6) return false;
|
|
@@ -1710,10 +1930,10 @@ var Routes = class {
|
|
|
1710
1930
|
this.ipv6 = ipv62;
|
|
1711
1931
|
}
|
|
1712
1932
|
set(table) {
|
|
1713
|
-
const
|
|
1714
|
-
const bad = (table.prefixes ?? []).find((_, i) => !
|
|
1933
|
+
const parsed3 = (table.prefixes ?? []).map(parsePrefix);
|
|
1934
|
+
const bad = (table.prefixes ?? []).find((_, i) => !parsed3[i]);
|
|
1715
1935
|
if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
|
|
1716
|
-
this.table =
|
|
1936
|
+
this.table = parsed3;
|
|
1717
1937
|
this.exit = table.exit === true;
|
|
1718
1938
|
}
|
|
1719
1939
|
classify(host) {
|
|
@@ -2249,10 +2469,10 @@ var KernelSocket = class _KernelSocket {
|
|
|
2249
2469
|
}
|
|
2250
2470
|
};
|
|
2251
2471
|
let made2;
|
|
2252
|
-
const
|
|
2472
|
+
const abandon2 = () => {
|
|
2253
2473
|
if (!keeps(signal.reason)) view2.close();
|
|
2254
2474
|
};
|
|
2255
|
-
signal.addEventListener("abort",
|
|
2475
|
+
signal.addEventListener("abort", abandon2, { once: true });
|
|
2256
2476
|
try {
|
|
2257
2477
|
made2 = await make(view2);
|
|
2258
2478
|
} catch (err) {
|
|
@@ -2265,7 +2485,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
2265
2485
|
throw signal.aborted ? signal.reason : err;
|
|
2266
2486
|
} finally {
|
|
2267
2487
|
handshaking = false;
|
|
2268
|
-
signal.removeEventListener("abort",
|
|
2488
|
+
signal.removeEventListener("abort", abandon2);
|
|
2269
2489
|
}
|
|
2270
2490
|
const plain3 = uplinkLink(made2.conn, (err) => {
|
|
2271
2491
|
this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
|
|
@@ -2731,7 +2951,7 @@ function signalName(sig) {
|
|
|
2731
2951
|
const name2 = Object.entries(SIG).find(([, n]) => n === sig)?.[0];
|
|
2732
2952
|
return name2 === void 0 ? void 0 : `SIG${name2}`;
|
|
2733
2953
|
}
|
|
2734
|
-
function
|
|
2954
|
+
function quiet2(p) {
|
|
2735
2955
|
p.catch(() => void 0);
|
|
2736
2956
|
return p;
|
|
2737
2957
|
}
|
|
@@ -2965,21 +3185,21 @@ function latin1Bytes(text) {
|
|
|
2965
3185
|
return out;
|
|
2966
3186
|
}
|
|
2967
3187
|
function parseRequestHead(bytes) {
|
|
2968
|
-
const
|
|
2969
|
-
while (
|
|
2970
|
-
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(
|
|
3188
|
+
const lines3 = latin1(bytes).split(/\r?\n/);
|
|
3189
|
+
while (lines3.length > 0 && lines3[0] === "") lines3.shift();
|
|
3190
|
+
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines3.shift() ?? "");
|
|
2971
3191
|
if (!match) throw new HttpError(400, "malformed request line");
|
|
2972
3192
|
if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
|
|
2973
3193
|
return {
|
|
2974
3194
|
method: match[1],
|
|
2975
3195
|
target: match[2],
|
|
2976
3196
|
minor: Number(match[4]),
|
|
2977
|
-
headers: parseFields(
|
|
3197
|
+
headers: parseFields(lines3)
|
|
2978
3198
|
};
|
|
2979
3199
|
}
|
|
2980
|
-
function parseFields(
|
|
3200
|
+
function parseFields(lines3) {
|
|
2981
3201
|
const headers = [];
|
|
2982
|
-
for (const line of
|
|
3202
|
+
for (const line of lines3) {
|
|
2983
3203
|
if (line === "") continue;
|
|
2984
3204
|
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
2985
3205
|
throw new HttpError(400, "obsolete line folding");
|
|
@@ -3464,10 +3684,10 @@ var CdpFacade = class {
|
|
|
3464
3684
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
3465
3685
|
};
|
|
3466
3686
|
conn.onmessage = (message3) => {
|
|
3467
|
-
const
|
|
3468
|
-
if (
|
|
3687
|
+
const parsed3 = JSON.parse(message3);
|
|
3688
|
+
if (parsed3.id !== 1) return;
|
|
3469
3689
|
clearTimeout(timer);
|
|
3470
|
-
resolve2(
|
|
3690
|
+
resolve2(parsed3);
|
|
3471
3691
|
};
|
|
3472
3692
|
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
3473
3693
|
}
|
|
@@ -3801,7 +4021,7 @@ function satisfies(version, range2) {
|
|
|
3801
4021
|
}
|
|
3802
4022
|
|
|
3803
4023
|
// src/version.ts
|
|
3804
|
-
var KERNEL_VERSION = true ? "1.
|
|
4024
|
+
var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
|
|
3805
4025
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3806
4026
|
function procVersion() {
|
|
3807
4027
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -4093,10 +4313,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
|
|
|
4093
4313
|
const own = commandsOf(pkg, manifest);
|
|
4094
4314
|
const named = new Set(own.map((command) => command.name));
|
|
4095
4315
|
const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
|
|
4096
|
-
const
|
|
4316
|
+
const refused5 = engineRefusal(manifest, kernel);
|
|
4097
4317
|
const name2 = pkg.slice(root.length + 1);
|
|
4098
4318
|
const dependency = direct?.has(name2) === false;
|
|
4099
|
-
const marks = { ...
|
|
4319
|
+
const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
|
|
4100
4320
|
out.push({
|
|
4101
4321
|
name: name2,
|
|
4102
4322
|
pkg,
|
|
@@ -4128,7 +4348,7 @@ function winners(tier) {
|
|
|
4128
4348
|
}
|
|
4129
4349
|
return out;
|
|
4130
4350
|
}
|
|
4131
|
-
async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION,
|
|
4351
|
+
async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused5, global = /* @__PURE__ */ new Set()) {
|
|
4132
4352
|
const found = /* @__PURE__ */ new Map();
|
|
4133
4353
|
const physical = physicalResolver(fs);
|
|
4134
4354
|
const tiers = (typeof modules === "string" ? [modules] : modules).map(
|
|
@@ -4142,7 +4362,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
|
|
|
4142
4362
|
const dependencies = [];
|
|
4143
4363
|
for (const [index, roots] of tiers.entries()) {
|
|
4144
4364
|
const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
|
|
4145
|
-
if (
|
|
4365
|
+
if (refused5) refused5.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
|
|
4146
4366
|
await take(tier.filter((entry) => !entry.dependency));
|
|
4147
4367
|
dependencies.push(tier.filter((entry) => entry.dependency));
|
|
4148
4368
|
}
|
|
@@ -4746,9 +4966,9 @@ var TAGS_VERSION = 1;
|
|
|
4746
4966
|
async function readTags(fs) {
|
|
4747
4967
|
const text = await fs.readFile(TAGS_PATH).catch(() => "");
|
|
4748
4968
|
try {
|
|
4749
|
-
const
|
|
4750
|
-
if (!
|
|
4751
|
-
const { v: _, ...tables } =
|
|
4969
|
+
const parsed3 = JSON.parse(text);
|
|
4970
|
+
if (!parsed3 || typeof parsed3 !== "object") return {};
|
|
4971
|
+
const { v: _, ...tables } = parsed3;
|
|
4752
4972
|
return tables;
|
|
4753
4973
|
} catch {
|
|
4754
4974
|
return {};
|
|
@@ -4818,27 +5038,27 @@ var Crontabs = class {
|
|
|
4818
5038
|
const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
|
|
4819
5039
|
if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
|
|
4820
5040
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
4821
|
-
const
|
|
4822
|
-
await fs.writeFile(
|
|
4823
|
-
await fs.rename(
|
|
5041
|
+
const fresh4 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
|
|
5042
|
+
await fs.writeFile(fresh4, json5, owner);
|
|
5043
|
+
await fs.rename(fresh4, TAGS_PATH);
|
|
4824
5044
|
}
|
|
4825
5045
|
async install(path, target, text, caller) {
|
|
4826
5046
|
if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
|
|
4827
5047
|
const { errors } = parseCrontab(text, path);
|
|
4828
5048
|
if (errors.length > 0) {
|
|
4829
|
-
const
|
|
4830
|
-
return { error: ["crontab: errors in crontab file, cannot install", ...
|
|
5049
|
+
const lines3 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
|
|
5050
|
+
return { error: ["crontab: errors in crontab file, cannot install", ...lines3].join("\n") };
|
|
4831
5051
|
}
|
|
4832
5052
|
const { fs, owners } = this.o;
|
|
4833
5053
|
if (!await fs.exists(SPOOL_DIR)) {
|
|
4834
5054
|
await fs.mkdir(SPOOL_DIR, { recursive: true });
|
|
4835
5055
|
if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
|
|
4836
5056
|
}
|
|
4837
|
-
const
|
|
5057
|
+
const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
|
|
4838
5058
|
const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
|
|
4839
5059
|
await this.retag(path, text, caller);
|
|
4840
|
-
await fs.writeFile(
|
|
4841
|
-
await fs.rename(
|
|
5060
|
+
await fs.writeFile(fresh4, text, owner);
|
|
5061
|
+
await fs.rename(fresh4, path);
|
|
4842
5062
|
return { text: "" };
|
|
4843
5063
|
}
|
|
4844
5064
|
};
|
|
@@ -5172,15 +5392,15 @@ var UnlinkedKeeper = class {
|
|
|
5172
5392
|
const releases = [];
|
|
5173
5393
|
const take = async () => {
|
|
5174
5394
|
for (; ; ) {
|
|
5175
|
-
const
|
|
5176
|
-
if (
|
|
5177
|
-
|
|
5178
|
-
if (paths.some((path) => path >
|
|
5395
|
+
const fresh4 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
|
|
5396
|
+
if (fresh4.length === 0) return;
|
|
5397
|
+
fresh4.sort();
|
|
5398
|
+
if (paths.some((path) => path > fresh4[0])) {
|
|
5179
5399
|
for (const release of releases.splice(0)) release();
|
|
5180
|
-
|
|
5181
|
-
|
|
5400
|
+
fresh4.push(...paths.splice(0));
|
|
5401
|
+
fresh4.sort();
|
|
5182
5402
|
}
|
|
5183
|
-
for (const path of
|
|
5403
|
+
for (const path of fresh4) {
|
|
5184
5404
|
paths.push(path);
|
|
5185
5405
|
releases.push(await this.gate.acquire(path));
|
|
5186
5406
|
}
|
|
@@ -5576,10 +5796,10 @@ var Output = class {
|
|
|
5576
5796
|
};
|
|
5577
5797
|
function readStamp(text) {
|
|
5578
5798
|
try {
|
|
5579
|
-
const
|
|
5580
|
-
const jobs =
|
|
5581
|
-
const reboot =
|
|
5582
|
-
const when =
|
|
5799
|
+
const parsed3 = JSON.parse(text ?? "");
|
|
5800
|
+
const jobs = parsed3.jobs && typeof parsed3.jobs === "object" ? parsed3.jobs : {};
|
|
5801
|
+
const reboot = parsed3.reboot && typeof parsed3.reboot === "object" ? parsed3.reboot : {};
|
|
5802
|
+
const when = parsed3.when && typeof parsed3.when === "object" ? parsed3.when : {};
|
|
5583
5803
|
return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
|
|
5584
5804
|
} catch {
|
|
5585
5805
|
return { jobs: {}, reboot: {}, when: {} };
|
|
@@ -5699,10 +5919,10 @@ var CronService = class {
|
|
|
5699
5919
|
const text = JSON.stringify(stamp);
|
|
5700
5920
|
const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
|
|
5701
5921
|
if (old === text || old === void 0 && text === EMPTY_STAMP) return;
|
|
5702
|
-
const
|
|
5922
|
+
const fresh4 = `${STAMP_PATH}.new`;
|
|
5703
5923
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
5704
|
-
await fs.writeFile(
|
|
5705
|
-
await fs.rename(
|
|
5924
|
+
await fs.writeFile(fresh4, text, owner);
|
|
5925
|
+
await fs.rename(fresh4, STAMP_PATH);
|
|
5706
5926
|
}
|
|
5707
5927
|
async load() {
|
|
5708
5928
|
const { fs } = this.host;
|
|
@@ -5755,10 +5975,10 @@ var CronService = class {
|
|
|
5755
5975
|
await this.once(path, text, errors);
|
|
5756
5976
|
return table;
|
|
5757
5977
|
}
|
|
5758
|
-
async once(path, version,
|
|
5978
|
+
async once(path, version, lines3) {
|
|
5759
5979
|
if (this.reported.get(path) === version) return;
|
|
5760
5980
|
this.reported.set(path, version);
|
|
5761
|
-
for (const line of
|
|
5981
|
+
for (const line of lines3) await this.error(line);
|
|
5762
5982
|
}
|
|
5763
5983
|
async error(line) {
|
|
5764
5984
|
if (this.stopping.signal.aborted) return;
|
|
@@ -6060,8 +6280,8 @@ function canonicalHost2(host) {
|
|
|
6060
6280
|
function localV4(a, b) {
|
|
6061
6281
|
return a === 127 || a === 0 || a === 169 && b === 254;
|
|
6062
6282
|
}
|
|
6063
|
-
function hextets(
|
|
6064
|
-
const [head, tail2, extra] =
|
|
6283
|
+
function hextets(address2) {
|
|
6284
|
+
const [head, tail2, extra] = address2.split("::");
|
|
6065
6285
|
if (extra !== void 0) return void 0;
|
|
6066
6286
|
const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
6067
6287
|
const front = part(head);
|
|
@@ -6069,8 +6289,8 @@ function hextets(address) {
|
|
|
6069
6289
|
const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
6070
6290
|
return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
|
|
6071
6291
|
}
|
|
6072
|
-
function localV6(
|
|
6073
|
-
const g = hextets(
|
|
6292
|
+
function localV6(address2) {
|
|
6293
|
+
const g = hextets(address2);
|
|
6074
6294
|
if (!g) return true;
|
|
6075
6295
|
if ((g[0] & 65472) === 65152) return true;
|
|
6076
6296
|
const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
|
|
@@ -6443,8 +6663,8 @@ var RealmProxy = class {
|
|
|
6443
6663
|
}
|
|
6444
6664
|
async connect(conn, incoming, req, idle2) {
|
|
6445
6665
|
const target = tunnelTarget(req);
|
|
6446
|
-
const
|
|
6447
|
-
if (
|
|
6666
|
+
const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
6667
|
+
if (refused5) throw refused5;
|
|
6448
6668
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
6449
6669
|
try {
|
|
6450
6670
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -7000,15 +7220,15 @@ async function followLinks(fs, path) {
|
|
|
7000
7220
|
}
|
|
7001
7221
|
var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
|
|
7002
7222
|
var EMPTY2 = new Uint8Array(0);
|
|
7003
|
-
function
|
|
7223
|
+
function quiet3(promise) {
|
|
7004
7224
|
return promise.catch(() => void 0);
|
|
7005
7225
|
}
|
|
7006
7226
|
async function readsAs2(fs, cred, target) {
|
|
7007
|
-
const walked = await
|
|
7227
|
+
const walked = await quiet3(access(fs, cred, target, R_OK, { directory: false }));
|
|
7008
7228
|
return walked !== void 0 && walked.denied === void 0 && !walked.missing;
|
|
7009
7229
|
}
|
|
7010
7230
|
async function shownTarget(fs, target) {
|
|
7011
|
-
const st = await
|
|
7231
|
+
const st = await quiet3(fs.stat(target));
|
|
7012
7232
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
7013
7233
|
}
|
|
7014
7234
|
async function madeFor(owner, virtual) {
|
|
@@ -7331,10 +7551,10 @@ var FreshModules = class {
|
|
|
7331
7551
|
this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
|
|
7332
7552
|
}
|
|
7333
7553
|
forget(pid) {
|
|
7334
|
-
for (const [path,
|
|
7554
|
+
for (const [path, fresh4] of this.fresh) if (fresh4.pid === pid) this.fresh.delete(path);
|
|
7335
7555
|
}
|
|
7336
|
-
drop(
|
|
7337
|
-
for (const [key, note] of [...this.fresh]) if (note ===
|
|
7556
|
+
drop(fresh4) {
|
|
7557
|
+
for (const [key, note] of [...this.fresh]) if (note === fresh4) this.fresh.delete(key);
|
|
7338
7558
|
}
|
|
7339
7559
|
moved(from, to) {
|
|
7340
7560
|
const source = normalizePath(from);
|
|
@@ -7343,10 +7563,10 @@ var FreshModules = class {
|
|
|
7343
7563
|
for (const key of [...this.fresh.keys()]) {
|
|
7344
7564
|
if (key === target || within3(key, target)) this.fresh.delete(key);
|
|
7345
7565
|
}
|
|
7346
|
-
for (const [key,
|
|
7566
|
+
for (const [key, fresh4] of [...this.fresh]) {
|
|
7347
7567
|
if (key !== source && !within3(key, source)) continue;
|
|
7348
7568
|
this.fresh.delete(key);
|
|
7349
|
-
this.fresh.set(target + key.slice(source.length),
|
|
7569
|
+
this.fresh.set(target + key.slice(source.length), fresh4);
|
|
7350
7570
|
}
|
|
7351
7571
|
}
|
|
7352
7572
|
wrap(fs) {
|
|
@@ -7356,15 +7576,15 @@ var FreshModules = class {
|
|
|
7356
7576
|
};
|
|
7357
7577
|
const decide = async (given, known) => {
|
|
7358
7578
|
const path = normalizePath(given);
|
|
7359
|
-
const
|
|
7360
|
-
if (!
|
|
7579
|
+
const fresh4 = this.fresh.get(path);
|
|
7580
|
+
if (!fresh4) return;
|
|
7361
7581
|
const bytes = known && known.length >= MAGIC.length ? known : await head(path);
|
|
7362
|
-
const
|
|
7363
|
-
|
|
7364
|
-
if (
|
|
7582
|
+
const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
|
|
7583
|
+
fresh4.setup = false;
|
|
7584
|
+
if (want2 === fresh4.added) return;
|
|
7365
7585
|
const mode = (await fs.stat(path)).mode & 4095;
|
|
7366
|
-
await fs.chmod(path, mode & ~
|
|
7367
|
-
|
|
7586
|
+
await fs.chmod(path, mode & ~fresh4.added | want2);
|
|
7587
|
+
fresh4.added = want2;
|
|
7368
7588
|
};
|
|
7369
7589
|
const overrides = {
|
|
7370
7590
|
writeFile: async (path, content, owner) => {
|
|
@@ -7377,9 +7597,9 @@ var FreshModules = class {
|
|
|
7377
7597
|
}
|
|
7378
7598
|
},
|
|
7379
7599
|
chmod: async (path, mode) => {
|
|
7380
|
-
const
|
|
7381
|
-
if (
|
|
7382
|
-
else if (
|
|
7600
|
+
const fresh4 = this.fresh.get(normalizePath(path));
|
|
7601
|
+
if (fresh4?.setup && (mode & 4095) === (438 & ~fresh4.umask)) fresh4.setup = false;
|
|
7602
|
+
else if (fresh4) this.drop(fresh4);
|
|
7383
7603
|
await fs.chmod(path, mode);
|
|
7384
7604
|
},
|
|
7385
7605
|
rm: async (path, options) => {
|
|
@@ -7396,16 +7616,16 @@ var FreshModules = class {
|
|
|
7396
7616
|
link: async (from, to, follow2) => {
|
|
7397
7617
|
await fs.link(from, to, follow2);
|
|
7398
7618
|
const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
|
|
7399
|
-
const
|
|
7400
|
-
if (
|
|
7619
|
+
const fresh4 = this.fresh.get(normalizePath(source));
|
|
7620
|
+
if (fresh4) this.fresh.set(normalizePath(to), fresh4);
|
|
7401
7621
|
}
|
|
7402
7622
|
};
|
|
7403
7623
|
const { append } = fs;
|
|
7404
7624
|
if (append) {
|
|
7405
7625
|
overrides.append = async (path, bytes, options) => {
|
|
7406
|
-
const
|
|
7626
|
+
const fresh4 = this.fresh.has(normalizePath(path));
|
|
7407
7627
|
const end = await append.call(fs, path, bytes, options);
|
|
7408
|
-
if (
|
|
7628
|
+
if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
|
|
7409
7629
|
return end;
|
|
7410
7630
|
};
|
|
7411
7631
|
}
|
|
@@ -8260,7 +8480,7 @@ var FsWatchers = class {
|
|
|
8260
8480
|
};
|
|
8261
8481
|
const content = (run2, types) => async (...args) => {
|
|
8262
8482
|
const at = normalizePath(args[0]);
|
|
8263
|
-
const
|
|
8483
|
+
const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
|
|
8264
8484
|
let done4 = false;
|
|
8265
8485
|
try {
|
|
8266
8486
|
await run2(...args);
|
|
@@ -8269,11 +8489,11 @@ var FsWatchers = class {
|
|
|
8269
8489
|
const real = await realOf(at);
|
|
8270
8490
|
const paths = real === at ? [at] : [at, real];
|
|
8271
8491
|
this.report(paths);
|
|
8272
|
-
if (done4) for (const type of types(
|
|
8492
|
+
if (done4) for (const type of types(fresh4, args)) this.emit(type, paths);
|
|
8273
8493
|
}
|
|
8274
8494
|
};
|
|
8275
|
-
const written2 = (
|
|
8276
|
-
const overwritten = (
|
|
8495
|
+
const written2 = (fresh4) => fresh4 ? ["rename", "change"] : ["change"];
|
|
8496
|
+
const overwritten = (fresh4, args) => fresh4 || args[1].length === 0 ? written2(fresh4) : ["change", "change"];
|
|
8277
8497
|
const reshaping = (run2) => async (...args) => {
|
|
8278
8498
|
this.reshaped();
|
|
8279
8499
|
try {
|
|
@@ -8306,14 +8526,14 @@ var FsWatchers = class {
|
|
|
8306
8526
|
if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
|
|
8307
8527
|
}
|
|
8308
8528
|
}
|
|
8309
|
-
const
|
|
8529
|
+
const fresh4 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
|
|
8310
8530
|
let done4 = false;
|
|
8311
8531
|
try {
|
|
8312
8532
|
await fs.mkdir(path, options);
|
|
8313
8533
|
done4 = true;
|
|
8314
8534
|
} finally {
|
|
8315
8535
|
this.report([...created4, path]);
|
|
8316
|
-
if (done4) this.emit("rename",
|
|
8536
|
+
if (done4) this.emit("rename", fresh4 ? [...created4, path] : created4);
|
|
8317
8537
|
}
|
|
8318
8538
|
},
|
|
8319
8539
|
rm: reshaping(async (path, options) => {
|
|
@@ -8823,15 +9043,15 @@ function embedderFailure(err) {
|
|
|
8823
9043
|
return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
|
|
8824
9044
|
}
|
|
8825
9045
|
function headerOf(bytes) {
|
|
8826
|
-
let
|
|
9046
|
+
let parsed3;
|
|
8827
9047
|
try {
|
|
8828
|
-
|
|
9048
|
+
parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
8829
9049
|
} catch {
|
|
8830
9050
|
throw new KernelError("EINVAL", "the request header is not JSON");
|
|
8831
9051
|
}
|
|
8832
|
-
if (typeof
|
|
9052
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
8833
9053
|
throw new KernelError("EINVAL", "the request header is not an object");
|
|
8834
|
-
const { kind, ...options } =
|
|
9054
|
+
const { kind, ...options } = parsed3;
|
|
8835
9055
|
if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
|
|
8836
9056
|
throw new KernelError("EINVAL", "the request has no valid kind");
|
|
8837
9057
|
return { kind, options };
|
|
@@ -9272,6 +9492,970 @@ function kindsOf(hook) {
|
|
|
9272
9492
|
);
|
|
9273
9493
|
}
|
|
9274
9494
|
|
|
9495
|
+
// src/kernel/serial-ioctl.ts
|
|
9496
|
+
var IOCTL = {
|
|
9497
|
+
TCGETS: 21505,
|
|
9498
|
+
TCSETS: 21506,
|
|
9499
|
+
TCSETSW: 21507,
|
|
9500
|
+
TCSETSF: 21508,
|
|
9501
|
+
TCSBRK: 21513,
|
|
9502
|
+
TCFLSH: 21515,
|
|
9503
|
+
TIOCEXCL: 21516,
|
|
9504
|
+
TIOCNXCL: 21517,
|
|
9505
|
+
TIOCSCTTY: 21518,
|
|
9506
|
+
TIOCGPGRP: 21519,
|
|
9507
|
+
TIOCSPGRP: 21520,
|
|
9508
|
+
TIOCOUTQ: 21521,
|
|
9509
|
+
TIOCGWINSZ: 21523,
|
|
9510
|
+
TIOCSWINSZ: 21524,
|
|
9511
|
+
TIOCMGET: 21525,
|
|
9512
|
+
TIOCMBIS: 21526,
|
|
9513
|
+
TIOCMBIC: 21527,
|
|
9514
|
+
TIOCMSET: 21528,
|
|
9515
|
+
FIONREAD: 21531,
|
|
9516
|
+
TIOCNOTTY: 21538,
|
|
9517
|
+
TCSBRKP: 21541,
|
|
9518
|
+
TIOCSBRK: 21543,
|
|
9519
|
+
TIOCCBRK: 21544,
|
|
9520
|
+
TCGETS2: 2150388778,
|
|
9521
|
+
TCSETS2: 1076646955,
|
|
9522
|
+
TCSETSW2: 1076646956,
|
|
9523
|
+
TCSETSF2: 1076646957
|
|
9524
|
+
};
|
|
9525
|
+
var TERMIOS_GETS = /* @__PURE__ */ new Set([IOCTL.TCGETS, IOCTL.TCGETS2]);
|
|
9526
|
+
var TERMIOS_SETS = /* @__PURE__ */ new Set([
|
|
9527
|
+
IOCTL.TCSETS,
|
|
9528
|
+
IOCTL.TCSETSW,
|
|
9529
|
+
IOCTL.TCSETSF,
|
|
9530
|
+
IOCTL.TCSETS2,
|
|
9531
|
+
IOCTL.TCSETSW2,
|
|
9532
|
+
IOCTL.TCSETSF2
|
|
9533
|
+
]);
|
|
9534
|
+
var INT_OUT = /* @__PURE__ */ new Set([
|
|
9535
|
+
IOCTL.TIOCMGET,
|
|
9536
|
+
IOCTL.FIONREAD,
|
|
9537
|
+
IOCTL.TIOCOUTQ
|
|
9538
|
+
]);
|
|
9539
|
+
var INT_IN = /* @__PURE__ */ new Set([
|
|
9540
|
+
IOCTL.TIOCMBIS,
|
|
9541
|
+
IOCTL.TIOCMBIC,
|
|
9542
|
+
IOCTL.TIOCMSET
|
|
9543
|
+
]);
|
|
9544
|
+
var INT_ARG = /* @__PURE__ */ new Set([IOCTL.TCSBRK, IOCTL.TCSBRKP, IOCTL.TCFLSH]);
|
|
9545
|
+
var TIOCM = {
|
|
9546
|
+
DTR: 2,
|
|
9547
|
+
RTS: 4,
|
|
9548
|
+
CTS: 32,
|
|
9549
|
+
CD: 64,
|
|
9550
|
+
RI: 128,
|
|
9551
|
+
DSR: 256
|
|
9552
|
+
};
|
|
9553
|
+
var CBAUD = 4111;
|
|
9554
|
+
var BOTHER = 4096;
|
|
9555
|
+
var IBSHIFT = 16;
|
|
9556
|
+
var CSIZE = 48;
|
|
9557
|
+
var CSTOPB = 64;
|
|
9558
|
+
var PARENB = 256;
|
|
9559
|
+
var PARODD = 512;
|
|
9560
|
+
var CRTSCTS = 2147483648;
|
|
9561
|
+
var SPEEDS = [
|
|
9562
|
+
0,
|
|
9563
|
+
50,
|
|
9564
|
+
75,
|
|
9565
|
+
110,
|
|
9566
|
+
134,
|
|
9567
|
+
150,
|
|
9568
|
+
200,
|
|
9569
|
+
300,
|
|
9570
|
+
600,
|
|
9571
|
+
1200,
|
|
9572
|
+
1800,
|
|
9573
|
+
2400,
|
|
9574
|
+
4800,
|
|
9575
|
+
9600,
|
|
9576
|
+
19200,
|
|
9577
|
+
38400
|
|
9578
|
+
];
|
|
9579
|
+
var FAST = [
|
|
9580
|
+
0,
|
|
9581
|
+
57600,
|
|
9582
|
+
115200,
|
|
9583
|
+
230400,
|
|
9584
|
+
460800,
|
|
9585
|
+
5e5,
|
|
9586
|
+
576e3,
|
|
9587
|
+
921600,
|
|
9588
|
+
1e6,
|
|
9589
|
+
1152e3,
|
|
9590
|
+
15e5,
|
|
9591
|
+
2e6,
|
|
9592
|
+
25e5,
|
|
9593
|
+
3e6,
|
|
9594
|
+
35e5,
|
|
9595
|
+
4e6
|
|
9596
|
+
];
|
|
9597
|
+
function speedOf(code) {
|
|
9598
|
+
if (code === BOTHER) return void 0;
|
|
9599
|
+
return code & BOTHER ? FAST[code & 15] : SPEEDS[code & 15];
|
|
9600
|
+
}
|
|
9601
|
+
function speedsOf(termios, current, extended) {
|
|
9602
|
+
const out = termios.c_cflag & CBAUD;
|
|
9603
|
+
const ospeed = speedOf(out) ?? (extended ? (termios.c_ospeed ?? current.ospeed) >>> 0 : current.ospeed);
|
|
9604
|
+
const inCode = termios.c_cflag >>> IBSHIFT & CBAUD;
|
|
9605
|
+
if (inCode === 0) return { ispeed: ospeed, ospeed };
|
|
9606
|
+
const ispeed = speedOf(inCode) ?? (extended ? (termios.c_ispeed ?? current.ispeed) >>> 0 : current.ispeed);
|
|
9607
|
+
return { ispeed, ospeed };
|
|
9608
|
+
}
|
|
9609
|
+
function settingsOf(cflag, baudRate) {
|
|
9610
|
+
return {
|
|
9611
|
+
baudRate,
|
|
9612
|
+
dataBits: 5 + ((cflag & CSIZE) >> 4),
|
|
9613
|
+
parity: cflag & PARENB ? cflag & PARODD ? "odd" : "even" : "none",
|
|
9614
|
+
stopBits: cflag & CSTOPB ? 2 : 1,
|
|
9615
|
+
flowControl: cflag & CRTSCTS ? "hardware" : "none"
|
|
9616
|
+
};
|
|
9617
|
+
}
|
|
9618
|
+
function sameSettings(a, b) {
|
|
9619
|
+
return a.baudRate === b.baudRate && a.dataBits === b.dataBits && a.parity === b.parity && a.stopBits === b.stopBits && a.flowControl === b.flowControl;
|
|
9620
|
+
}
|
|
9621
|
+
var SERIAL_CFLAG = 13 | 48 | 128 | 1024 | 2048;
|
|
9622
|
+
var SERIAL_LFLAG = 16 | 32 | 512 | 2048;
|
|
9623
|
+
var SERIAL_OFLAG = 4;
|
|
9624
|
+
|
|
9625
|
+
// src/kernel/tty.ts
|
|
9626
|
+
var ICRNL = 256;
|
|
9627
|
+
var IUTF8 = 16384;
|
|
9628
|
+
var OPOST = 1;
|
|
9629
|
+
var ONLCR = 4;
|
|
9630
|
+
var ISIG = 1;
|
|
9631
|
+
var ICANON = 2;
|
|
9632
|
+
var ECHO = 8;
|
|
9633
|
+
var ECHOE = 16;
|
|
9634
|
+
var ECHOCTL = 512;
|
|
9635
|
+
var NOFLSH = 128;
|
|
9636
|
+
var IEXTEN = 32768;
|
|
9637
|
+
var VINTR = 0;
|
|
9638
|
+
var VQUIT = 1;
|
|
9639
|
+
var VERASE = 2;
|
|
9640
|
+
var VKILL = 3;
|
|
9641
|
+
var VEOF = 4;
|
|
9642
|
+
var VSUSP = 10;
|
|
9643
|
+
var VWERASE = 14;
|
|
9644
|
+
function defaultTermios() {
|
|
9645
|
+
const cCc = new Array(32).fill(0);
|
|
9646
|
+
Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
|
|
9647
|
+
Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
|
|
9648
|
+
Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
|
|
9649
|
+
return {
|
|
9650
|
+
c_iflag: ICRNL | 1024 | 8192 | IUTF8,
|
|
9651
|
+
c_oflag: OPOST | ONLCR,
|
|
9652
|
+
c_cflag: 15 | 48 | 128,
|
|
9653
|
+
c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
|
|
9654
|
+
c_cc: cCc
|
|
9655
|
+
};
|
|
9656
|
+
}
|
|
9657
|
+
var encoder3 = new TextEncoder();
|
|
9658
|
+
function isContinuation(byte) {
|
|
9659
|
+
return (byte & 192) === 128;
|
|
9660
|
+
}
|
|
9661
|
+
var KernelTty = class {
|
|
9662
|
+
termios = defaultTermios();
|
|
9663
|
+
cols = 80;
|
|
9664
|
+
rows = 24;
|
|
9665
|
+
readable = [];
|
|
9666
|
+
line = [];
|
|
9667
|
+
waiters = [];
|
|
9668
|
+
hungUp = false;
|
|
9669
|
+
name;
|
|
9670
|
+
owner;
|
|
9671
|
+
onRead;
|
|
9672
|
+
readers = 0;
|
|
9673
|
+
screen;
|
|
9674
|
+
signal;
|
|
9675
|
+
constructor(screen, signal) {
|
|
9676
|
+
this.screen = screen;
|
|
9677
|
+
this.signal = signal;
|
|
9678
|
+
}
|
|
9679
|
+
receive(bytes) {
|
|
9680
|
+
const { c_iflag, c_lflag } = this.termios;
|
|
9681
|
+
if (!(c_iflag & ICRNL) && !(c_lflag & (ISIG | ICANON | ECHO))) {
|
|
9682
|
+
if (bytes.length > 0) this.readable.push(bytes.slice());
|
|
9683
|
+
} else for (const byte of bytes) this.key(byte);
|
|
9684
|
+
this.wake();
|
|
9685
|
+
}
|
|
9686
|
+
resize(cols, rows) {
|
|
9687
|
+
if (cols === this.cols && rows === this.rows) return;
|
|
9688
|
+
this.setSize(cols, rows);
|
|
9689
|
+
this.signal(SIG.WINCH);
|
|
9690
|
+
}
|
|
9691
|
+
hangup() {
|
|
9692
|
+
this.hungUp = true;
|
|
9693
|
+
this.wake();
|
|
9694
|
+
}
|
|
9695
|
+
signalHangup() {
|
|
9696
|
+
this.signal(SIG.HUP);
|
|
9697
|
+
}
|
|
9698
|
+
setSize(cols, rows) {
|
|
9699
|
+
this.cols = cols;
|
|
9700
|
+
this.rows = rows;
|
|
9701
|
+
}
|
|
9702
|
+
tcgets() {
|
|
9703
|
+
return { ...this.termios, c_cc: [...this.termios.c_cc] };
|
|
9704
|
+
}
|
|
9705
|
+
tcsets(termios, flush = false) {
|
|
9706
|
+
const wasCanonical = this.canonical;
|
|
9707
|
+
this.termios = { ...termios, c_cc: [...termios.c_cc] };
|
|
9708
|
+
if (flush) {
|
|
9709
|
+
this.line = [];
|
|
9710
|
+
this.readable = [];
|
|
9711
|
+
}
|
|
9712
|
+
if (wasCanonical && !this.canonical && this.line.length > 0) {
|
|
9713
|
+
this.readable.push(Uint8Array.from(this.line));
|
|
9714
|
+
this.line = [];
|
|
9715
|
+
this.wake();
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9718
|
+
winsize() {
|
|
9719
|
+
return [this.rows, this.cols];
|
|
9720
|
+
}
|
|
9721
|
+
file() {
|
|
9722
|
+
return new OpenFile({
|
|
9723
|
+
read: (max, signal) => this.read(max, signal),
|
|
9724
|
+
write: async (bytes) => {
|
|
9725
|
+
if (this.hungUp) throw new KernelError("EIO");
|
|
9726
|
+
this.output(bytes);
|
|
9727
|
+
return bytes.length;
|
|
9728
|
+
},
|
|
9729
|
+
poll: () => ({
|
|
9730
|
+
readable: this.readable.length > 0 || this.hungUp,
|
|
9731
|
+
writable: true,
|
|
9732
|
+
hangup: this.hungUp,
|
|
9733
|
+
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
9734
|
+
}),
|
|
9735
|
+
changed: (signal) => this.changed(signal),
|
|
9736
|
+
close: () => {
|
|
9737
|
+
},
|
|
9738
|
+
tty: this
|
|
9739
|
+
});
|
|
9740
|
+
}
|
|
9741
|
+
get canonical() {
|
|
9742
|
+
return (this.termios.c_lflag & ICANON) !== 0;
|
|
9743
|
+
}
|
|
9744
|
+
cc(slot2) {
|
|
9745
|
+
return this.termios.c_cc[slot2] ?? 0;
|
|
9746
|
+
}
|
|
9747
|
+
key(input) {
|
|
9748
|
+
const { c_iflag, c_lflag } = this.termios;
|
|
9749
|
+
const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
|
|
9750
|
+
if (c_lflag & ISIG && this.signalKey(byte)) return;
|
|
9751
|
+
if (!this.canonical) {
|
|
9752
|
+
this.readable.push(Uint8Array.of(byte));
|
|
9753
|
+
if (c_lflag & ECHO) this.echo(byte);
|
|
9754
|
+
return;
|
|
9755
|
+
}
|
|
9756
|
+
this.edit(byte);
|
|
9757
|
+
}
|
|
9758
|
+
signalKey(byte) {
|
|
9759
|
+
const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
|
|
9760
|
+
if (!sig || byte === 0) return false;
|
|
9761
|
+
if (this.termios.c_lflag & ECHO) this.echo(byte);
|
|
9762
|
+
if (!(this.termios.c_lflag & NOFLSH)) {
|
|
9763
|
+
this.line = [];
|
|
9764
|
+
this.readable = [];
|
|
9765
|
+
}
|
|
9766
|
+
this.signal(sig);
|
|
9767
|
+
return true;
|
|
9768
|
+
}
|
|
9769
|
+
edit(byte) {
|
|
9770
|
+
const echo = (this.termios.c_lflag & ECHO) !== 0;
|
|
9771
|
+
if (byte === this.cc(VERASE) || byte === 8) {
|
|
9772
|
+
this.rubOut(this.lastCharLength());
|
|
9773
|
+
} else if (byte === this.cc(VKILL)) {
|
|
9774
|
+
this.rubOut(this.line.length);
|
|
9775
|
+
} else if (byte === this.cc(VWERASE)) {
|
|
9776
|
+
this.rubOut(this.lastWordLength());
|
|
9777
|
+
} else if (byte === this.cc(VEOF)) {
|
|
9778
|
+
this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
|
|
9779
|
+
this.line = [];
|
|
9780
|
+
} else if (byte === 10) {
|
|
9781
|
+
this.line.push(byte);
|
|
9782
|
+
this.readable.push(Uint8Array.from(this.line));
|
|
9783
|
+
this.line = [];
|
|
9784
|
+
if (echo) this.output(encoder3.encode("\n"));
|
|
9785
|
+
} else {
|
|
9786
|
+
this.line.push(byte);
|
|
9787
|
+
if (echo) this.echo(byte);
|
|
9788
|
+
}
|
|
9789
|
+
}
|
|
9790
|
+
lastWordLength() {
|
|
9791
|
+
let end = this.line.length;
|
|
9792
|
+
while (end > 0 && this.line[end - 1] === 32) end--;
|
|
9793
|
+
while (end > 0 && this.line[end - 1] !== 32) end--;
|
|
9794
|
+
return this.line.length - end;
|
|
9795
|
+
}
|
|
9796
|
+
lastCharLength() {
|
|
9797
|
+
const { line } = this;
|
|
9798
|
+
let n = Math.min(1, line.length);
|
|
9799
|
+
if (this.termios.c_iflag & IUTF8) {
|
|
9800
|
+
while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
|
|
9801
|
+
}
|
|
9802
|
+
return n;
|
|
9803
|
+
}
|
|
9804
|
+
rubOut(n) {
|
|
9805
|
+
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
9806
|
+
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
9807
|
+
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
9808
|
+
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
9809
|
+
this.output(encoder3.encode("\b \b".repeat(cells)), false);
|
|
9810
|
+
}
|
|
9811
|
+
}
|
|
9812
|
+
echo(byte) {
|
|
9813
|
+
if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
|
|
9814
|
+
this.output(Uint8Array.of(94, byte + 64), false);
|
|
9815
|
+
return;
|
|
9816
|
+
}
|
|
9817
|
+
this.output(Uint8Array.of(byte));
|
|
9818
|
+
}
|
|
9819
|
+
output(bytes, translate2 = true) {
|
|
9820
|
+
const { c_oflag } = this.termios;
|
|
9821
|
+
if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
|
|
9822
|
+
this.screen.write(bytes);
|
|
9823
|
+
return;
|
|
9824
|
+
}
|
|
9825
|
+
const out = [];
|
|
9826
|
+
for (const byte of bytes) {
|
|
9827
|
+
if (byte === 10) out.push(13);
|
|
9828
|
+
out.push(byte);
|
|
9829
|
+
}
|
|
9830
|
+
this.screen.write(Uint8Array.from(out));
|
|
9831
|
+
}
|
|
9832
|
+
get reading() {
|
|
9833
|
+
return this.readers > 0;
|
|
9834
|
+
}
|
|
9835
|
+
async read(max, signal) {
|
|
9836
|
+
this.onRead?.();
|
|
9837
|
+
this.readers++;
|
|
9838
|
+
try {
|
|
9839
|
+
return await this.take(max, signal);
|
|
9840
|
+
} finally {
|
|
9841
|
+
this.readers--;
|
|
9842
|
+
}
|
|
9843
|
+
}
|
|
9844
|
+
async take(max, signal) {
|
|
9845
|
+
while (this.readable.length === 0) {
|
|
9846
|
+
if (this.hungUp) return new Uint8Array(0);
|
|
9847
|
+
await this.changed(signal);
|
|
9848
|
+
}
|
|
9849
|
+
const head = this.readable[0];
|
|
9850
|
+
if (head === null) {
|
|
9851
|
+
this.readable.shift();
|
|
9852
|
+
return new Uint8Array(0);
|
|
9853
|
+
}
|
|
9854
|
+
if (head === void 0) return new Uint8Array(0);
|
|
9855
|
+
const chunks2 = [];
|
|
9856
|
+
let n = 0;
|
|
9857
|
+
while (this.readable.length > 0 && n < max) {
|
|
9858
|
+
const next3 = this.readable[0];
|
|
9859
|
+
if (next3 === null || next3 === void 0) break;
|
|
9860
|
+
const take = next3.subarray(0, max - n);
|
|
9861
|
+
chunks2.push(take);
|
|
9862
|
+
n += take.length;
|
|
9863
|
+
if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
|
|
9864
|
+
else this.readable.shift();
|
|
9865
|
+
if (this.canonical) break;
|
|
9866
|
+
}
|
|
9867
|
+
const out = new Uint8Array(n);
|
|
9868
|
+
let at = 0;
|
|
9869
|
+
for (const chunk2 of chunks2) {
|
|
9870
|
+
out.set(chunk2, at);
|
|
9871
|
+
at += chunk2.length;
|
|
9872
|
+
}
|
|
9873
|
+
return out;
|
|
9874
|
+
}
|
|
9875
|
+
changed(signal) {
|
|
9876
|
+
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
9877
|
+
return new Promise((resolve2, reject) => {
|
|
9878
|
+
const onAbort = () => {
|
|
9879
|
+
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
9880
|
+
reject(new KernelError("EINTR"));
|
|
9881
|
+
};
|
|
9882
|
+
const waiter = () => {
|
|
9883
|
+
signal?.removeEventListener("abort", onAbort);
|
|
9884
|
+
resolve2();
|
|
9885
|
+
};
|
|
9886
|
+
this.waiters.push(waiter);
|
|
9887
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
9888
|
+
});
|
|
9889
|
+
}
|
|
9890
|
+
wake() {
|
|
9891
|
+
const waiters = this.waiters;
|
|
9892
|
+
this.waiters = [];
|
|
9893
|
+
for (const resolve2 of waiters) resolve2();
|
|
9894
|
+
}
|
|
9895
|
+
};
|
|
9896
|
+
|
|
9897
|
+
// src/kernel/devices.ts
|
|
9898
|
+
var DEVICE_NAME = /^tty(USB|ACM)(\d{1,3})$/;
|
|
9899
|
+
var BY_ID = "/dev/serial/by-id";
|
|
9900
|
+
var LINK_NAME = /^[A-Za-z0-9._:+@=,-]{1,255}$/;
|
|
9901
|
+
var MAJORS = { USB: 188, ACM: 166 };
|
|
9902
|
+
var HIGH_WATER = 64 * 1024;
|
|
9903
|
+
var CLOSING_WAIT_MS = 3e4;
|
|
9904
|
+
var BREAK_MS = 250;
|
|
9905
|
+
var MAX_ID2 = 4294967294;
|
|
9906
|
+
var DEFAULT_SETTINGS = settingsOf(SERIAL_CFLAG, 9600);
|
|
9907
|
+
function refused2(code, text) {
|
|
9908
|
+
return Object.assign(new Error(`${code}: ${text}`), { code });
|
|
9909
|
+
}
|
|
9910
|
+
function invalid(what) {
|
|
9911
|
+
return refused2("EINVAL", what);
|
|
9912
|
+
}
|
|
9913
|
+
function nodePath(path) {
|
|
9914
|
+
if (typeof path !== "string") throw invalid("path must be a string");
|
|
9915
|
+
const name2 = path.startsWith("/dev/") ? path.slice(5) : path;
|
|
9916
|
+
const m = DEVICE_NAME.exec(name2);
|
|
9917
|
+
const minor = Number(m?.[2]);
|
|
9918
|
+
if (!m || minor > 255) throw invalid(`not a serial device name: ${path}`);
|
|
9919
|
+
return { path: `/dev/${name2}`, major: MAJORS[m[1]], minor };
|
|
9920
|
+
}
|
|
9921
|
+
function linkPath(link2) {
|
|
9922
|
+
if (typeof link2 !== "string") throw invalid("a link must be a string");
|
|
9923
|
+
const name2 = link2.startsWith(`${BY_ID}/`) ? link2.slice(BY_ID.length + 1) : link2;
|
|
9924
|
+
if (!LINK_NAME.test(name2) || name2 === "." || name2 === "..")
|
|
9925
|
+
throw invalid(`not a link name: ${link2}`);
|
|
9926
|
+
return `${BY_ID}/${name2}`;
|
|
9927
|
+
}
|
|
9928
|
+
function modeOf(mode) {
|
|
9929
|
+
if (mode === void 0) return 384;
|
|
9930
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 4095)
|
|
9931
|
+
throw invalid("mode must be permission bits");
|
|
9932
|
+
return mode;
|
|
9933
|
+
}
|
|
9934
|
+
async function idOf(value2, find) {
|
|
9935
|
+
if (value2 === void 0) return 0;
|
|
9936
|
+
if (typeof value2 === "number") {
|
|
9937
|
+
if (!Number.isInteger(value2) || value2 < 0 || value2 > MAX_ID2) throw invalid(`bad id ${value2}`);
|
|
9938
|
+
return value2;
|
|
9939
|
+
}
|
|
9940
|
+
const id2 = typeof value2 === "string" ? await find(value2) : void 0;
|
|
9941
|
+
if (id2 === void 0) throw invalid(`unknown owner or group ${String(value2)}`);
|
|
9942
|
+
return id2;
|
|
9943
|
+
}
|
|
9944
|
+
function quiet4() {
|
|
9945
|
+
return void 0;
|
|
9946
|
+
}
|
|
9947
|
+
function want(access2) {
|
|
9948
|
+
return access2 === "read" ? R_OK : access2 === "write" ? W_OK : R_OK | W_OK;
|
|
9949
|
+
}
|
|
9950
|
+
function devicesHook(hook) {
|
|
9951
|
+
if (typeof hook?.open !== "function") throw new TypeError("devices needs open");
|
|
9952
|
+
return { open: (request) => hook.open(request) };
|
|
9953
|
+
}
|
|
9954
|
+
function checkMeta(node, change, uid, gid, guard2) {
|
|
9955
|
+
const { cred } = guard2;
|
|
9956
|
+
const owner = cred.euid === 0 || cred.euid === node.uid;
|
|
9957
|
+
const member = cred.egid === gid || cred.groups.includes(gid);
|
|
9958
|
+
const chown2 = uid !== node.uid || gid !== node.gid;
|
|
9959
|
+
const times = (change.atimeMs ?? change.mtimeMs ?? null) !== null;
|
|
9960
|
+
if (change.mode !== void 0 && !owner) deny(guard2, "chmod", node.path, "EPERM");
|
|
9961
|
+
if (chown2 && cred.euid !== 0 && (uid !== node.uid || !owner || !member))
|
|
9962
|
+
deny(guard2, "chown", node.path, "EPERM");
|
|
9963
|
+
if (times && !owner && !(change.now && permits(cred, node, W_OK, false)))
|
|
9964
|
+
deny(guard2, "utimes", node.path, change.now ? "EACCES" : "EPERM");
|
|
9965
|
+
}
|
|
9966
|
+
var DeviceNodes = class {
|
|
9967
|
+
hooks = /* @__PURE__ */ new Map();
|
|
9968
|
+
nodes = /* @__PURE__ */ new Map();
|
|
9969
|
+
links = /* @__PURE__ */ new Map();
|
|
9970
|
+
lookup;
|
|
9971
|
+
users;
|
|
9972
|
+
next = 0;
|
|
9973
|
+
constructor(users, lookup4) {
|
|
9974
|
+
this.users = users;
|
|
9975
|
+
this.lookup = lookup4;
|
|
9976
|
+
}
|
|
9977
|
+
register(client, hook) {
|
|
9978
|
+
this.hooks.set(client, hook);
|
|
9979
|
+
return () => {
|
|
9980
|
+
if (this.hooks.get(client) !== hook) return;
|
|
9981
|
+
this.hooks.delete(client);
|
|
9982
|
+
for (const node of [...this.nodes.values()]) if (node.client === client) this.drop(node);
|
|
9983
|
+
};
|
|
9984
|
+
}
|
|
9985
|
+
async add(spec, client) {
|
|
9986
|
+
if (typeof spec !== "object" || spec === null) throw invalid("add needs a spec");
|
|
9987
|
+
const { path, major, minor } = nodePath(spec.path);
|
|
9988
|
+
const mode = modeOf(spec.mode);
|
|
9989
|
+
if (spec.links !== void 0 && !Array.isArray(spec.links))
|
|
9990
|
+
throw invalid("links must be a list");
|
|
9991
|
+
const links = (spec.links ?? []).map(linkPath);
|
|
9992
|
+
const uid = await idOf(spec.owner, (name2) => this.lookup.userId(name2));
|
|
9993
|
+
const gid = await idOf(spec.group, (name2) => this.lookup.groupId(name2));
|
|
9994
|
+
if (!this.hooks.has(client)) throw refused2("ENOTSUP", "this client has no devices hook");
|
|
9995
|
+
if (this.nodes.has(path)) throw refused2("EEXIST", `${path} exists`);
|
|
9996
|
+
const taken = links.find((l, i) => this.links.has(l) || links.indexOf(l) !== i);
|
|
9997
|
+
if (taken !== void 0) throw refused2("EEXIST", `${taken} exists`);
|
|
9998
|
+
const node = {
|
|
9999
|
+
id: ++this.next,
|
|
10000
|
+
client,
|
|
10001
|
+
path,
|
|
10002
|
+
uid,
|
|
10003
|
+
gid,
|
|
10004
|
+
mode,
|
|
10005
|
+
major,
|
|
10006
|
+
minor,
|
|
10007
|
+
links,
|
|
10008
|
+
opening: false,
|
|
10009
|
+
atimeMs: Date.now(),
|
|
10010
|
+
mtimeMs: Date.now(),
|
|
10011
|
+
ctimeMs: Date.now()
|
|
10012
|
+
};
|
|
10013
|
+
this.nodes.set(path, node);
|
|
10014
|
+
for (const link2 of links) this.links.set(link2, node);
|
|
10015
|
+
return node.id;
|
|
10016
|
+
}
|
|
10017
|
+
remove(id2) {
|
|
10018
|
+
const node = [...this.nodes.values()].find((n) => n.id === id2);
|
|
10019
|
+
if (!node) return false;
|
|
10020
|
+
this.drop(node);
|
|
10021
|
+
return true;
|
|
10022
|
+
}
|
|
10023
|
+
drop(node) {
|
|
10024
|
+
this.nodes.delete(node.path);
|
|
10025
|
+
for (const link2 of node.links) this.links.delete(link2);
|
|
10026
|
+
node.channel?.lost(new KernelError("EIO", `${node.path} was removed`));
|
|
10027
|
+
}
|
|
10028
|
+
list() {
|
|
10029
|
+
return [...this.nodes.values()].sort((a, b) => a.path < b.path ? -1 : 1).map(({ path, uid, gid, mode, major, minor, links, atimeMs, mtimeMs, ctimeMs }) => ({
|
|
10030
|
+
path,
|
|
10031
|
+
uid,
|
|
10032
|
+
gid,
|
|
10033
|
+
mode,
|
|
10034
|
+
major,
|
|
10035
|
+
minor,
|
|
10036
|
+
links: [...links],
|
|
10037
|
+
atimeMs,
|
|
10038
|
+
mtimeMs,
|
|
10039
|
+
ctimeMs
|
|
10040
|
+
}));
|
|
10041
|
+
}
|
|
10042
|
+
meta(path, change, guard2) {
|
|
10043
|
+
const node = this.find(path);
|
|
10044
|
+
if (!node) throw new KernelError("ENOENT");
|
|
10045
|
+
const uid = change.uid === void 0 || change.uid === -1 ? node.uid : change.uid >>> 0;
|
|
10046
|
+
const gid = change.gid === void 0 || change.gid === -1 ? node.gid : change.gid >>> 0;
|
|
10047
|
+
if (guard2) checkMeta(node, change, uid, gid, guard2);
|
|
10048
|
+
const now = Date.now();
|
|
10049
|
+
if (change.mode !== void 0) node.mode = change.mode & 4095;
|
|
10050
|
+
node.uid = uid;
|
|
10051
|
+
node.gid = gid;
|
|
10052
|
+
const at = (ms, kept) => ms === void 0 || ms === null ? kept : change.now ? now : ms;
|
|
10053
|
+
node.atimeMs = at(change.atimeMs, node.atimeMs);
|
|
10054
|
+
node.mtimeMs = at(change.mtimeMs, node.mtimeMs);
|
|
10055
|
+
node.ctimeMs = now;
|
|
10056
|
+
if (node.channel) node.channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
|
|
10057
|
+
}
|
|
10058
|
+
find(path) {
|
|
10059
|
+
return this.nodes.get(path) ?? this.links.get(path);
|
|
10060
|
+
}
|
|
10061
|
+
openTty(path) {
|
|
10062
|
+
return this.nodes.get(path)?.channel?.tty;
|
|
10063
|
+
}
|
|
10064
|
+
async open(path, flags2, opener) {
|
|
10065
|
+
const node = this.find(path);
|
|
10066
|
+
if (!node) throw new KernelError("ENOENT");
|
|
10067
|
+
const access2 = (flags2 & 3) === 0 ? "read" : (flags2 & 3) === 1 ? "write" : "both";
|
|
10068
|
+
if (opener.guard && !permits(opener.guard.cred, node, want(access2), false))
|
|
10069
|
+
deny(opener.guard, "open", node.path);
|
|
10070
|
+
if (node.channel || node.opening) throw new KernelError("EBUSY");
|
|
10071
|
+
const hook = this.hooks.get(node.client);
|
|
10072
|
+
node.opening = true;
|
|
10073
|
+
const asked = Promise.resolve().then(
|
|
10074
|
+
() => hook.open({ path: node.path, flags: flags2 & 3, stamp: opener.stamp, signal: opener.signal })
|
|
10075
|
+
);
|
|
10076
|
+
let streams;
|
|
10077
|
+
try {
|
|
10078
|
+
streams = await interruptible2(asked, opener.signal);
|
|
10079
|
+
} catch (err) {
|
|
10080
|
+
if (err instanceof KernelError && err.code === "EINTR") {
|
|
10081
|
+
void asked.then(abandon, quiet4).finally(() => node.opening = false);
|
|
10082
|
+
} else node.opening = false;
|
|
10083
|
+
throw embedderFailure(err);
|
|
10084
|
+
}
|
|
10085
|
+
node.opening = false;
|
|
10086
|
+
if (!isStreams(streams)) throw new KernelError("EIO", "the devices hook gave no streams");
|
|
10087
|
+
if (this.nodes.get(node.path) !== node) {
|
|
10088
|
+
void abandon(streams);
|
|
10089
|
+
throw new KernelError("ENOENT");
|
|
10090
|
+
}
|
|
10091
|
+
const channel = new DeviceChannel(node.path, streams, node.saved, (saved) => {
|
|
10092
|
+
node.saved = saved;
|
|
10093
|
+
if (node.channel === channel) delete node.channel;
|
|
10094
|
+
});
|
|
10095
|
+
channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
|
|
10096
|
+
node.channel = channel;
|
|
10097
|
+
try {
|
|
10098
|
+
await channel.restore();
|
|
10099
|
+
} catch (err) {
|
|
10100
|
+
await channel.close();
|
|
10101
|
+
throw err;
|
|
10102
|
+
}
|
|
10103
|
+
return channel.file(access2);
|
|
10104
|
+
}
|
|
10105
|
+
};
|
|
10106
|
+
function isStreams(value2) {
|
|
10107
|
+
const s = value2;
|
|
10108
|
+
return !!s && s.readable instanceof ReadableStream && s.writable instanceof WritableStream && typeof s.control === "function";
|
|
10109
|
+
}
|
|
10110
|
+
async function abandon(streams) {
|
|
10111
|
+
if (!isStreams(streams)) return;
|
|
10112
|
+
await streams.readable.cancel("closed").catch(quiet4);
|
|
10113
|
+
await streams.writable.abort("closed").catch(quiet4);
|
|
10114
|
+
await Promise.resolve().then(() => streams.control("close")).catch(quiet4);
|
|
10115
|
+
}
|
|
10116
|
+
function serialTermios() {
|
|
10117
|
+
const base = defaultTermios();
|
|
10118
|
+
return {
|
|
10119
|
+
c_iflag: 0,
|
|
10120
|
+
c_oflag: SERIAL_OFLAG,
|
|
10121
|
+
c_cflag: SERIAL_CFLAG,
|
|
10122
|
+
c_lflag: SERIAL_LFLAG,
|
|
10123
|
+
c_cc: base.c_cc
|
|
10124
|
+
};
|
|
10125
|
+
}
|
|
10126
|
+
function sleep(ms, signal) {
|
|
10127
|
+
return interruptible2(new Promise((resolve2) => setTimeout(resolve2, ms)), signal);
|
|
10128
|
+
}
|
|
10129
|
+
var DeviceChannel = class {
|
|
10130
|
+
tty;
|
|
10131
|
+
path;
|
|
10132
|
+
inner;
|
|
10133
|
+
reader;
|
|
10134
|
+
writer;
|
|
10135
|
+
streams;
|
|
10136
|
+
onClosed;
|
|
10137
|
+
gone = Promise.withResolvers();
|
|
10138
|
+
failure;
|
|
10139
|
+
pending = /* @__PURE__ */ new Set();
|
|
10140
|
+
queued = 0;
|
|
10141
|
+
resume;
|
|
10142
|
+
waiters = [];
|
|
10143
|
+
closed = false;
|
|
10144
|
+
ispeed = 9600;
|
|
10145
|
+
ospeed = 9600;
|
|
10146
|
+
dtr = true;
|
|
10147
|
+
rts = true;
|
|
10148
|
+
constructor(path, streams, saved, onClosed) {
|
|
10149
|
+
this.path = path;
|
|
10150
|
+
this.streams = streams;
|
|
10151
|
+
this.onClosed = onClosed;
|
|
10152
|
+
this.gone.promise.catch(quiet4);
|
|
10153
|
+
this.tty = new KernelTty({ write: (bytes) => this.send(bytes) }, quiet4);
|
|
10154
|
+
this.tty.name = path;
|
|
10155
|
+
this.tty.setSize(0, 0);
|
|
10156
|
+
this.tty.tcsets(saved?.termios ?? serialTermios());
|
|
10157
|
+
if (saved) {
|
|
10158
|
+
this.ispeed = saved.ispeed;
|
|
10159
|
+
this.ospeed = saved.ospeed;
|
|
10160
|
+
this.dtr = this.rts = saved.ospeed !== 0;
|
|
10161
|
+
}
|
|
10162
|
+
this.inner = this.tty.file();
|
|
10163
|
+
this.reader = streams.readable.getReader();
|
|
10164
|
+
this.writer = streams.writable.getWriter();
|
|
10165
|
+
void this.pump();
|
|
10166
|
+
}
|
|
10167
|
+
file(access2) {
|
|
10168
|
+
return new OpenFile({
|
|
10169
|
+
...access2 === "write" ? {} : { read: (max, signal) => this.read(max, signal) },
|
|
10170
|
+
...access2 === "read" ? {} : { write: (bytes, signal) => this.write(bytes, signal) },
|
|
10171
|
+
poll: () => this.poll(),
|
|
10172
|
+
changed: (signal) => this.changed(signal),
|
|
10173
|
+
close: () => this.close(),
|
|
10174
|
+
tty: this.tty,
|
|
10175
|
+
device: this
|
|
10176
|
+
});
|
|
10177
|
+
}
|
|
10178
|
+
lost(err) {
|
|
10179
|
+
if (this.failure) return;
|
|
10180
|
+
this.failure = err;
|
|
10181
|
+
this.gone.reject(err);
|
|
10182
|
+
this.tty.hangup();
|
|
10183
|
+
this.wake();
|
|
10184
|
+
}
|
|
10185
|
+
control(op, args) {
|
|
10186
|
+
return Promise.resolve().then(() => this.streams.control(op, args));
|
|
10187
|
+
}
|
|
10188
|
+
buffered() {
|
|
10189
|
+
return this.inner.file.poll?.().nread ?? 0;
|
|
10190
|
+
}
|
|
10191
|
+
async pump() {
|
|
10192
|
+
for (; ; ) {
|
|
10193
|
+
while (this.buffered() >= HIGH_WATER && !this.closed && !this.failure) {
|
|
10194
|
+
await new Promise((resolve2) => {
|
|
10195
|
+
this.resume = resolve2;
|
|
10196
|
+
});
|
|
10197
|
+
}
|
|
10198
|
+
if (this.closed || this.failure) return;
|
|
10199
|
+
let got;
|
|
10200
|
+
try {
|
|
10201
|
+
got = await this.reader.read();
|
|
10202
|
+
} catch (err) {
|
|
10203
|
+
if (!this.closed) this.lost(new KernelError("EIO", lineOf2(err)));
|
|
10204
|
+
return;
|
|
10205
|
+
}
|
|
10206
|
+
if (got.done) {
|
|
10207
|
+
this.tty.hangup();
|
|
10208
|
+
this.wake();
|
|
10209
|
+
return;
|
|
10210
|
+
}
|
|
10211
|
+
this.tty.receive(got.value);
|
|
10212
|
+
this.wake();
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
send(bytes) {
|
|
10216
|
+
if (this.closed || this.failure) return;
|
|
10217
|
+
const n = bytes.length;
|
|
10218
|
+
this.queued += n;
|
|
10219
|
+
const sent = this.writer.write(bytes.slice()).catch((err) => {
|
|
10220
|
+
this.lost(new KernelError("EIO", lineOf2(err)));
|
|
10221
|
+
});
|
|
10222
|
+
const tracked = sent.finally(() => {
|
|
10223
|
+
this.queued -= n;
|
|
10224
|
+
this.pending.delete(tracked);
|
|
10225
|
+
this.wake();
|
|
10226
|
+
});
|
|
10227
|
+
this.pending.add(tracked);
|
|
10228
|
+
}
|
|
10229
|
+
check() {
|
|
10230
|
+
if (this.failure) throw this.failure;
|
|
10231
|
+
}
|
|
10232
|
+
async read(max, signal) {
|
|
10233
|
+
this.check();
|
|
10234
|
+
const read = this.inner.file.read;
|
|
10235
|
+
const got = await read(max, signal);
|
|
10236
|
+
this.resume?.();
|
|
10237
|
+
this.resume = void 0;
|
|
10238
|
+
if (got.length === 0) this.check();
|
|
10239
|
+
return got;
|
|
10240
|
+
}
|
|
10241
|
+
async ready(signal) {
|
|
10242
|
+
this.check();
|
|
10243
|
+
try {
|
|
10244
|
+
await interruptible2(Promise.race([this.writer.ready, this.gone.promise]), signal);
|
|
10245
|
+
} catch (err) {
|
|
10246
|
+
if (err instanceof KernelError) throw err;
|
|
10247
|
+
throw new KernelError("EIO", lineOf2(err));
|
|
10248
|
+
}
|
|
10249
|
+
}
|
|
10250
|
+
async write(bytes, signal) {
|
|
10251
|
+
this.check();
|
|
10252
|
+
if (!((this.writer.desiredSize ?? 0) > 0)) await this.ready(signal);
|
|
10253
|
+
const write = this.inner.file.write;
|
|
10254
|
+
return write(bytes, signal);
|
|
10255
|
+
}
|
|
10256
|
+
poll() {
|
|
10257
|
+
if (this.failure)
|
|
10258
|
+
return { readable: true, writable: true, hangup: true, error: true, nread: 0 };
|
|
10259
|
+
const inner = this.inner.file.poll?.();
|
|
10260
|
+
const writable3 = (this.writer.desiredSize ?? 0) > 0;
|
|
10261
|
+
return { ...inner, writable: writable3 };
|
|
10262
|
+
}
|
|
10263
|
+
changed(signal) {
|
|
10264
|
+
const mine = new Promise((resolve2) => this.waiters.push(resolve2));
|
|
10265
|
+
const theirs = this.inner.file.changed?.(signal);
|
|
10266
|
+
return interruptible2(Promise.race([mine, theirs]), signal);
|
|
10267
|
+
}
|
|
10268
|
+
wake() {
|
|
10269
|
+
const waiters = this.waiters;
|
|
10270
|
+
this.waiters = [];
|
|
10271
|
+
for (const resolve2 of waiters) resolve2();
|
|
10272
|
+
}
|
|
10273
|
+
async drain(signal) {
|
|
10274
|
+
while (this.pending.size > 0) {
|
|
10275
|
+
this.check();
|
|
10276
|
+
await interruptible2(
|
|
10277
|
+
Promise.race([Promise.all([...this.pending]), this.gone.promise]),
|
|
10278
|
+
signal
|
|
10279
|
+
);
|
|
10280
|
+
}
|
|
10281
|
+
this.check();
|
|
10282
|
+
}
|
|
10283
|
+
async restore() {
|
|
10284
|
+
const settings = this.settings();
|
|
10285
|
+
if (settings.baudRate === 0)
|
|
10286
|
+
return this.signals({ dataTerminalReady: false, requestToSend: false });
|
|
10287
|
+
if (sameSettings(settings, DEFAULT_SETTINGS)) return;
|
|
10288
|
+
try {
|
|
10289
|
+
await this.control("configure", settings);
|
|
10290
|
+
} catch (err) {
|
|
10291
|
+
throw embedderFailure(err);
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
settings() {
|
|
10295
|
+
return settingsOf(this.tty.tcgets().c_cflag, this.ospeed);
|
|
10296
|
+
}
|
|
10297
|
+
termios() {
|
|
10298
|
+
return { ...this.tty.tcgets(), c_ispeed: this.ispeed, c_ospeed: this.ospeed };
|
|
10299
|
+
}
|
|
10300
|
+
async setTermios(termios, how, signal) {
|
|
10301
|
+
this.check();
|
|
10302
|
+
if (how.drain) await this.drain(signal);
|
|
10303
|
+
const before = this.settings();
|
|
10304
|
+
const old = { termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed };
|
|
10305
|
+
const speeds = speedsOf(termios, this, how.extended === true);
|
|
10306
|
+
this.tty.tcsets(termios, how.flush === true);
|
|
10307
|
+
this.ispeed = speeds.ispeed;
|
|
10308
|
+
this.ospeed = speeds.ospeed;
|
|
10309
|
+
try {
|
|
10310
|
+
if (speeds.ospeed === 0) await this.setModem(this.bits() & ~(TIOCM.DTR | TIOCM.RTS));
|
|
10311
|
+
else {
|
|
10312
|
+
if (!sameSettings(before, this.settings()))
|
|
10313
|
+
await this.control("configure", this.settings());
|
|
10314
|
+
if (old.ospeed === 0) await this.setModem(this.bits() | TIOCM.DTR | TIOCM.RTS);
|
|
10315
|
+
}
|
|
10316
|
+
if (how.flush) await this.control("flush", { input: true, output: false });
|
|
10317
|
+
} catch (err) {
|
|
10318
|
+
this.tty.tcsets(old.termios);
|
|
10319
|
+
this.ispeed = old.ispeed;
|
|
10320
|
+
this.ospeed = old.ospeed;
|
|
10321
|
+
throw embedderFailure(err);
|
|
10322
|
+
}
|
|
10323
|
+
}
|
|
10324
|
+
bits() {
|
|
10325
|
+
return (this.dtr ? TIOCM.DTR : 0) | (this.rts ? TIOCM.RTS : 0);
|
|
10326
|
+
}
|
|
10327
|
+
async modem() {
|
|
10328
|
+
this.check();
|
|
10329
|
+
const s = await this.control("signals").catch((err) => {
|
|
10330
|
+
throw embedderFailure(err);
|
|
10331
|
+
});
|
|
10332
|
+
return this.bits() | (s?.clearToSend ? TIOCM.CTS : 0) | (s?.dataSetReady ? TIOCM.DSR : 0) | (s?.dataCarrierDetect ? TIOCM.CD : 0) | (s?.ringIndicator ? TIOCM.RI : 0);
|
|
10333
|
+
}
|
|
10334
|
+
async setModem(bits2) {
|
|
10335
|
+
this.check();
|
|
10336
|
+
const dataTerminalReady = (bits2 & TIOCM.DTR) !== 0;
|
|
10337
|
+
const requestToSend = (bits2 & TIOCM.RTS) !== 0;
|
|
10338
|
+
await this.signals({ dataTerminalReady, requestToSend });
|
|
10339
|
+
this.dtr = dataTerminalReady;
|
|
10340
|
+
this.rts = requestToSend;
|
|
10341
|
+
}
|
|
10342
|
+
async signals(args) {
|
|
10343
|
+
try {
|
|
10344
|
+
await this.control("setSignals", args);
|
|
10345
|
+
} catch (err) {
|
|
10346
|
+
throw embedderFailure(err);
|
|
10347
|
+
}
|
|
10348
|
+
}
|
|
10349
|
+
async sendBreak(ms, signal) {
|
|
10350
|
+
await this.drain(signal);
|
|
10351
|
+
await this.signals({ break: true });
|
|
10352
|
+
try {
|
|
10353
|
+
await sleep(ms, signal);
|
|
10354
|
+
} finally {
|
|
10355
|
+
await this.signals({ break: false });
|
|
10356
|
+
}
|
|
10357
|
+
}
|
|
10358
|
+
async flush(input, output) {
|
|
10359
|
+
this.check();
|
|
10360
|
+
if (input) this.tty.tcsets(this.tty.tcgets(), true);
|
|
10361
|
+
try {
|
|
10362
|
+
await this.control("flush", { input, output });
|
|
10363
|
+
} catch (err) {
|
|
10364
|
+
throw embedderFailure(err);
|
|
10365
|
+
}
|
|
10366
|
+
}
|
|
10367
|
+
async ioctl(request, arg, signal) {
|
|
10368
|
+
this.check();
|
|
10369
|
+
switch (request) {
|
|
10370
|
+
case IOCTL.TCGETS:
|
|
10371
|
+
case IOCTL.TCGETS2:
|
|
10372
|
+
return this.termios();
|
|
10373
|
+
case IOCTL.TCSETS:
|
|
10374
|
+
case IOCTL.TCSETSW:
|
|
10375
|
+
case IOCTL.TCSETSF:
|
|
10376
|
+
case IOCTL.TCSETS2:
|
|
10377
|
+
case IOCTL.TCSETSW2:
|
|
10378
|
+
case IOCTL.TCSETSF2: {
|
|
10379
|
+
const extended = request > 65535;
|
|
10380
|
+
const kind = (request & 255) - (extended ? 43 : 2);
|
|
10381
|
+
await this.setTermios(
|
|
10382
|
+
arg,
|
|
10383
|
+
{ drain: kind > 0, flush: kind === 2, extended },
|
|
10384
|
+
signal
|
|
10385
|
+
);
|
|
10386
|
+
return null;
|
|
10387
|
+
}
|
|
10388
|
+
case IOCTL.TIOCMGET:
|
|
10389
|
+
return this.modem();
|
|
10390
|
+
case IOCTL.TIOCMSET:
|
|
10391
|
+
await this.setModem(Number(arg));
|
|
10392
|
+
return null;
|
|
10393
|
+
case IOCTL.TIOCMBIS:
|
|
10394
|
+
await this.setModem(this.bits() | Number(arg));
|
|
10395
|
+
return null;
|
|
10396
|
+
case IOCTL.TIOCMBIC:
|
|
10397
|
+
await this.setModem(this.bits() & ~Number(arg));
|
|
10398
|
+
return null;
|
|
10399
|
+
case IOCTL.TIOCSBRK:
|
|
10400
|
+
case IOCTL.TIOCCBRK:
|
|
10401
|
+
await this.signals({ break: request === IOCTL.TIOCSBRK });
|
|
10402
|
+
return null;
|
|
10403
|
+
case IOCTL.TCSBRK:
|
|
10404
|
+
if (Number(arg) === 0) await this.sendBreak(BREAK_MS, signal);
|
|
10405
|
+
else await this.drain(signal);
|
|
10406
|
+
return null;
|
|
10407
|
+
case IOCTL.TCSBRKP:
|
|
10408
|
+
await this.sendBreak(Number(arg) > 0 ? Number(arg) * 100 : BREAK_MS, signal);
|
|
10409
|
+
return null;
|
|
10410
|
+
case IOCTL.TCFLSH: {
|
|
10411
|
+
const queue = Number(arg);
|
|
10412
|
+
if (queue < 0 || queue > 2) throw new KernelError("EINVAL");
|
|
10413
|
+
await this.flush(queue !== 1, queue !== 0);
|
|
10414
|
+
return null;
|
|
10415
|
+
}
|
|
10416
|
+
case IOCTL.FIONREAD:
|
|
10417
|
+
return this.buffered();
|
|
10418
|
+
case IOCTL.TIOCOUTQ:
|
|
10419
|
+
return this.queued;
|
|
10420
|
+
case IOCTL.TIOCEXCL:
|
|
10421
|
+
case IOCTL.TIOCNXCL:
|
|
10422
|
+
return null;
|
|
10423
|
+
case IOCTL.TIOCGWINSZ: {
|
|
10424
|
+
const [rows, cols] = this.tty.winsize();
|
|
10425
|
+
return { rows, cols };
|
|
10426
|
+
}
|
|
10427
|
+
case IOCTL.TIOCSWINSZ: {
|
|
10428
|
+
const size = arg;
|
|
10429
|
+
this.tty.setSize(Number(size?.cols) & 65535, Number(size?.rows) & 65535);
|
|
10430
|
+
return null;
|
|
10431
|
+
}
|
|
10432
|
+
case IOCTL.TIOCSCTTY:
|
|
10433
|
+
throw new KernelError("EPERM");
|
|
10434
|
+
default:
|
|
10435
|
+
throw new KernelError("ENOTTY");
|
|
10436
|
+
}
|
|
10437
|
+
}
|
|
10438
|
+
async close() {
|
|
10439
|
+
if (this.closed) return;
|
|
10440
|
+
try {
|
|
10441
|
+
if (!this.failure) {
|
|
10442
|
+
await interruptible2(this.drain(), AbortSignal.timeout(CLOSING_WAIT_MS)).catch(quiet4);
|
|
10443
|
+
}
|
|
10444
|
+
this.closed = true;
|
|
10445
|
+
this.resume?.();
|
|
10446
|
+
this.wake();
|
|
10447
|
+
await this.reader.cancel("closed").catch(quiet4);
|
|
10448
|
+
this.reader.releaseLock();
|
|
10449
|
+
await this.writer.abort("closed").catch(quiet4);
|
|
10450
|
+
this.writer.releaseLock();
|
|
10451
|
+
await this.control("close").catch(quiet4);
|
|
10452
|
+
} finally {
|
|
10453
|
+
this.closed = true;
|
|
10454
|
+
this.onClosed({ termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed });
|
|
10455
|
+
}
|
|
10456
|
+
}
|
|
10457
|
+
};
|
|
10458
|
+
|
|
9275
10459
|
// src/kernel/exec-ticket.ts
|
|
9276
10460
|
var TICKET_ENV = "SLICC_SEALED_TICKET";
|
|
9277
10461
|
var TICKET_TTL_MS = 3e4;
|
|
@@ -9661,8 +10845,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
9661
10845
|
} finally {
|
|
9662
10846
|
clearTimeout(timer);
|
|
9663
10847
|
}
|
|
9664
|
-
const
|
|
9665
|
-
if (
|
|
10848
|
+
const refused5 = driverVersionError(hello.protocol);
|
|
10849
|
+
if (refused5) throw errnoError("EPROTO", refused5);
|
|
9666
10850
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
9667
10851
|
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
|
|
9668
10852
|
}
|
|
@@ -9708,7 +10892,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9708
10892
|
var S_ISGID3 = 1024;
|
|
9709
10893
|
var ID = /^\d{1,10}$/;
|
|
9710
10894
|
var MASK = /^0*[0-7]{1,4}$/;
|
|
9711
|
-
var
|
|
10895
|
+
var MAX_ID3 = 4294967294;
|
|
9712
10896
|
function idOption(value2) {
|
|
9713
10897
|
return value2 === void 0 ? 0 : Number(value2);
|
|
9714
10898
|
}
|
|
@@ -9720,7 +10904,7 @@ function checkIds(options) {
|
|
|
9720
10904
|
for (const key of ["uid", "gid"]) {
|
|
9721
10905
|
const value2 = options[key];
|
|
9722
10906
|
if (value2 === void 0) continue;
|
|
9723
|
-
if (!ID.test(value2) || Number(value2) >
|
|
10907
|
+
if (!ID.test(value2) || Number(value2) > MAX_ID3) throw errnoError("EINVAL", `${key}=${value2}`);
|
|
9724
10908
|
}
|
|
9725
10909
|
}
|
|
9726
10910
|
function checkMasks(options) {
|
|
@@ -9732,7 +10916,7 @@ function checkMasks(options) {
|
|
|
9732
10916
|
}
|
|
9733
10917
|
}
|
|
9734
10918
|
}
|
|
9735
|
-
function
|
|
10919
|
+
function modeOf2(attr2, mount) {
|
|
9736
10920
|
const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
|
|
9737
10921
|
const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
|
|
9738
10922
|
if (mount.caps.owners) return attr2.mode ?? fallback;
|
|
@@ -9893,9 +11077,9 @@ var HeldPaths = class extends Map {
|
|
|
9893
11077
|
this.unlinked.set(path, file);
|
|
9894
11078
|
}
|
|
9895
11079
|
keep(path, file) {
|
|
9896
|
-
const
|
|
9897
|
-
for (const id2 of
|
|
9898
|
-
this.uses.set(file, Math.max(
|
|
11080
|
+
const fresh4 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
11081
|
+
for (const id2 of fresh4) this.keptHandles.set(id2, file);
|
|
11082
|
+
this.uses.set(file, Math.max(fresh4.length, 1));
|
|
9899
11083
|
this.unlinked.set(path, file);
|
|
9900
11084
|
}
|
|
9901
11085
|
unkeep(path, file) {
|
|
@@ -10016,7 +11200,7 @@ function statOf2(attr2, path, mount) {
|
|
|
10016
11200
|
isSymbolicLink: attr2.kind === "symlink",
|
|
10017
11201
|
...attr2.kind === "fifo" ? { isFifo: true } : {},
|
|
10018
11202
|
size: attr2.size,
|
|
10019
|
-
mode:
|
|
11203
|
+
mode: modeOf2(attr2, mount),
|
|
10020
11204
|
mtime,
|
|
10021
11205
|
atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
|
|
10022
11206
|
ctime: mtime,
|
|
@@ -11107,8 +12291,8 @@ var MountFs = class _MountFs {
|
|
|
11107
12291
|
}
|
|
11108
12292
|
this.writable(mount, real);
|
|
11109
12293
|
const { uid, gid, mode } = change;
|
|
11110
|
-
const
|
|
11111
|
-
const set3 =
|
|
12294
|
+
const fresh4 = change.kind === "new" && { uid, gid, mode };
|
|
12295
|
+
const set3 = fresh4 ? await this.driverOwner(found, real, fresh4, false) : change;
|
|
11112
12296
|
this.table.forget(mount, real);
|
|
11113
12297
|
const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
|
|
11114
12298
|
await mount.conn.call({
|
|
@@ -11385,10 +12569,10 @@ async function runMkdir(entry, path, req) {
|
|
|
11385
12569
|
const plain3 = plainDirMode(entry, req.mode);
|
|
11386
12570
|
const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
|
|
11387
12571
|
const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
|
|
11388
|
-
const
|
|
12572
|
+
const fresh4 = plain3 !== void 0 && !await fs.exists(path);
|
|
11389
12573
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
11390
12574
|
await fixParents?.();
|
|
11391
|
-
if (
|
|
12575
|
+
if (fresh4) await fs.chmod(path, plain3);
|
|
11392
12576
|
return done;
|
|
11393
12577
|
}
|
|
11394
12578
|
async function shownAs(entry, path, st) {
|
|
@@ -11600,10 +12784,10 @@ async function guard(g, path, req, cwd) {
|
|
|
11600
12784
|
}
|
|
11601
12785
|
async function opening(entry, path, mode) {
|
|
11602
12786
|
const g = guardOf(entry);
|
|
11603
|
-
const
|
|
12787
|
+
const want2 = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
|
|
11604
12788
|
const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
|
|
11605
|
-
if (g &&
|
|
11606
|
-
await need(g, "open", path,
|
|
12789
|
+
if (g && want2 !== 0 && !created4) {
|
|
12790
|
+
await need(g, "open", path, want2, { directory: false });
|
|
11607
12791
|
}
|
|
11608
12792
|
}
|
|
11609
12793
|
async function stillThere(entry, path, req) {
|
|
@@ -11627,7 +12811,7 @@ async function holdOp(entry, path, req) {
|
|
|
11627
12811
|
await entry.source?.open(path, real);
|
|
11628
12812
|
await stillThere(entry, path, req);
|
|
11629
12813
|
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
11630
|
-
const fid = req.ino === void 0 ? void 0 : await
|
|
12814
|
+
const fid = req.ino === void 0 ? void 0 : await idOf2(entry, real ?? path);
|
|
11631
12815
|
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
11632
12816
|
return json2(await heldOpen(entry, path, real, req, fid));
|
|
11633
12817
|
}
|
|
@@ -11641,7 +12825,7 @@ async function heldOpen(entry, path, real, req, fid) {
|
|
|
11641
12825
|
await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
|
|
11642
12826
|
return real !== void 0 && real !== path ? { handle, real } : handle;
|
|
11643
12827
|
}
|
|
11644
|
-
async function
|
|
12828
|
+
async function idOf2(entry, path) {
|
|
11645
12829
|
return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
|
|
11646
12830
|
}
|
|
11647
12831
|
async function ownEntry(entry, path, real) {
|
|
@@ -11713,8 +12897,8 @@ async function guarded(entry, path, req) {
|
|
|
11713
12897
|
return { paths };
|
|
11714
12898
|
} catch (err) {
|
|
11715
12899
|
const refusal3 = err.code === "EACCES";
|
|
11716
|
-
const
|
|
11717
|
-
return { refused:
|
|
12900
|
+
const refused5 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
|
|
12901
|
+
return { refused: refused5 };
|
|
11718
12902
|
}
|
|
11719
12903
|
}
|
|
11720
12904
|
async function copyOp(entry, path, req) {
|
|
@@ -11815,7 +12999,7 @@ async function bound(entry, path, handle) {
|
|
|
11815
12999
|
const now = await entry.fs.stat(path).catch(() => void 0);
|
|
11816
13000
|
if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
|
|
11817
13001
|
throw syncError("ENOENT", path);
|
|
11818
|
-
const same2 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await
|
|
13002
|
+
const same2 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf2(entry, path) === bound3.fid;
|
|
11819
13003
|
if (same2) {
|
|
11820
13004
|
entry.trust?.(handle, bound3.doubt);
|
|
11821
13005
|
return;
|
|
@@ -11910,13 +13094,13 @@ async function created(entry, req) {
|
|
|
11910
13094
|
return seen === void 0 ? looked : json2({ ...looked.json, seen });
|
|
11911
13095
|
}
|
|
11912
13096
|
var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
|
|
11913
|
-
async function rmPermitted(entry, path, req,
|
|
13097
|
+
async function rmPermitted(entry, path, req, refused5) {
|
|
11914
13098
|
const st = await entry.fs.lstat(path).catch(() => void 0);
|
|
11915
|
-
if (!st?.isDirectory) return
|
|
13099
|
+
if (!st?.isDirectory) return refused5;
|
|
11916
13100
|
const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
|
|
11917
13101
|
if ("refused" in unlisted) return unlisted.refused;
|
|
11918
13102
|
const names2 = await entry.fs.readdir(path).catch(() => void 0);
|
|
11919
|
-
if (!names2) return
|
|
13103
|
+
if (!names2) return refused5;
|
|
11920
13104
|
let first;
|
|
11921
13105
|
for (const name2 of names2) {
|
|
11922
13106
|
const child = await dispatchOne(entry, {
|
|
@@ -11967,14 +13151,14 @@ async function newDevName(entry, path, req) {
|
|
|
11967
13151
|
const named = NAMING.has(req.op);
|
|
11968
13152
|
if (!named && !CREATIONS.has(req.op)) return void 0;
|
|
11969
13153
|
const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
|
|
11970
|
-
const
|
|
11971
|
-
if (!
|
|
13154
|
+
const refused5 = devCreation(target, entry.cred?.());
|
|
13155
|
+
if (!refused5) return void 0;
|
|
11972
13156
|
const there = await entry.fs.lstat(target).then(
|
|
11973
13157
|
() => true,
|
|
11974
13158
|
() => false
|
|
11975
13159
|
);
|
|
11976
13160
|
if (there) return void 0;
|
|
11977
|
-
return { ok: false, errno:
|
|
13161
|
+
return { ok: false, errno: refused5, message: `${target}: no new names in /dev or /proc` };
|
|
11978
13162
|
}
|
|
11979
13163
|
var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
|
|
11980
13164
|
async function linkFree(fs, path) {
|
|
@@ -12085,13 +13269,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
|
12085
13269
|
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
12086
13270
|
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
12087
13271
|
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
12088
|
-
var
|
|
13272
|
+
var encoder4 = new TextEncoder();
|
|
12089
13273
|
var decoder2 = new TextDecoder();
|
|
12090
13274
|
function encodeSabResult(result) {
|
|
12091
13275
|
if (!result.ok) {
|
|
12092
13276
|
return {
|
|
12093
13277
|
status: SAB_STATUS_ERRNO,
|
|
12094
|
-
payload:
|
|
13278
|
+
payload: encoder4.encode(
|
|
12095
13279
|
JSON.stringify({
|
|
12096
13280
|
errno: result.errno,
|
|
12097
13281
|
message: result.message,
|
|
@@ -12106,7 +13290,7 @@ function encodeSabResult(result) {
|
|
|
12106
13290
|
case "bytes":
|
|
12107
13291
|
return { status: SAB_STATUS_BYTES, payload: result.bytes };
|
|
12108
13292
|
case "json":
|
|
12109
|
-
return { status: SAB_STATUS_JSON, payload:
|
|
13293
|
+
return { status: SAB_STATUS_JSON, payload: encoder4.encode(JSON.stringify(result.json)) };
|
|
12110
13294
|
default:
|
|
12111
13295
|
return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
|
|
12112
13296
|
}
|
|
@@ -12433,9 +13617,9 @@ var Pages = class {
|
|
|
12433
13617
|
const page = (start2 + at) / PAGE;
|
|
12434
13618
|
if (this.pages.has(page)) continue;
|
|
12435
13619
|
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
12436
|
-
const
|
|
12437
|
-
|
|
12438
|
-
this.pages.set(page,
|
|
13620
|
+
const fresh4 = this.take(part.length);
|
|
13621
|
+
fresh4.set(part);
|
|
13622
|
+
this.pages.set(page, fresh4);
|
|
12439
13623
|
}
|
|
12440
13624
|
}
|
|
12441
13625
|
read(at, length2) {
|
|
@@ -12646,16 +13830,16 @@ var pinnedRoot;
|
|
|
12646
13830
|
var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
|
|
12647
13831
|
function mountTable(mounts = []) {
|
|
12648
13832
|
const root = pinnedRoot ?? rootKind();
|
|
12649
|
-
const
|
|
13833
|
+
const lines3 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
|
|
12650
13834
|
for (const m of mounts) {
|
|
12651
13835
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
12652
13836
|
if (m.state !== "ok") options.push(m.state);
|
|
12653
13837
|
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
12654
|
-
|
|
13838
|
+
lines3.push(
|
|
12655
13839
|
`${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
12656
13840
|
);
|
|
12657
13841
|
}
|
|
12658
|
-
return `${
|
|
13842
|
+
return `${lines3.join("\n")}
|
|
12659
13843
|
`;
|
|
12660
13844
|
}
|
|
12661
13845
|
|
|
@@ -13690,7 +14874,7 @@ function recordRefusal(opening2) {
|
|
|
13690
14874
|
const found = headerClass(opening2);
|
|
13691
14875
|
return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
|
|
13692
14876
|
}
|
|
13693
|
-
function
|
|
14877
|
+
function refused3(engine, session, err, opening2) {
|
|
13694
14878
|
const flags2 = session.verifyResult;
|
|
13695
14879
|
const code = verifyCode(flags2);
|
|
13696
14880
|
if (code && flags2 !== -1)
|
|
@@ -13711,7 +14895,7 @@ async function handshake(conn, o, session, inbox) {
|
|
|
13711
14895
|
done4 = session.handshake();
|
|
13712
14896
|
} catch (err) {
|
|
13713
14897
|
await send(conn, session).catch(() => void 0);
|
|
13714
|
-
throw
|
|
14898
|
+
throw refused3(o.engine, session, err, inbox.opening);
|
|
13715
14899
|
}
|
|
13716
14900
|
await send(conn, session);
|
|
13717
14901
|
if (done4) return;
|
|
@@ -14162,10 +15346,10 @@ async function anchorFor(engine, chain2, host, found) {
|
|
|
14162
15346
|
var FRAGMENT = 16;
|
|
14163
15347
|
var MIN_VALUE = 8;
|
|
14164
15348
|
var MIN_BASE64 = 8;
|
|
14165
|
-
var
|
|
15349
|
+
var encoder5 = new TextEncoder();
|
|
14166
15350
|
var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
14167
15351
|
function utf8(text) {
|
|
14168
|
-
return
|
|
15352
|
+
return encoder5.encode(text);
|
|
14169
15353
|
}
|
|
14170
15354
|
function base64(bytes, padded = true) {
|
|
14171
15355
|
let out = "";
|
|
@@ -14239,14 +15423,73 @@ function encodings(bytes) {
|
|
|
14239
15423
|
}
|
|
14240
15424
|
return [...standard, ...standard.map(urlSafe)];
|
|
14241
15425
|
}
|
|
14242
|
-
function
|
|
15426
|
+
function windows(length2) {
|
|
15427
|
+
const out = [];
|
|
15428
|
+
for (let offset = 0; offset + FRAGMENT <= length2; offset++) out.push(offset);
|
|
15429
|
+
return out;
|
|
15430
|
+
}
|
|
15431
|
+
function strings(document) {
|
|
15432
|
+
const out = [];
|
|
15433
|
+
const stack = [document];
|
|
15434
|
+
while (stack.length > 0) {
|
|
15435
|
+
const item = stack.pop();
|
|
15436
|
+
if (typeof item === "string") out.push(item);
|
|
15437
|
+
if (typeof item !== "object" || item === null) continue;
|
|
15438
|
+
for (const child of Object.values(item)) stack.push(child);
|
|
15439
|
+
}
|
|
15440
|
+
return out;
|
|
15441
|
+
}
|
|
15442
|
+
var DOMAIN = "(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}";
|
|
15443
|
+
var HOST = new RegExp(`^${DOMAIN}$`);
|
|
15444
|
+
var EMAIL = new RegExp(`^[^\\s@]+@${DOMAIN}$`);
|
|
15445
|
+
var URL_SHAPE = new RegExp(`^https?://${DOMAIN}(?::[0-9]{1,5})?((?:/[^/?#\\s]*)*)$`);
|
|
15446
|
+
var ARMOR = /^-----(?:BEGIN|END) [A-Z0-9 ]+-----$/;
|
|
15447
|
+
function unescaped(segment) {
|
|
15448
|
+
try {
|
|
15449
|
+
return decodeURIComponent(segment);
|
|
15450
|
+
} catch {
|
|
15451
|
+
return segment;
|
|
15452
|
+
}
|
|
15453
|
+
}
|
|
15454
|
+
function address(leaf) {
|
|
15455
|
+
if (HOST.test(leaf) || EMAIL.test(leaf)) return true;
|
|
15456
|
+
const url = URL_SHAPE.exec(leaf);
|
|
15457
|
+
if (!url) return false;
|
|
15458
|
+
return url[1].split("/").every((segment) => utf8(segment).length < FRAGMENT || EMAIL.test(unescaped(segment)));
|
|
15459
|
+
}
|
|
15460
|
+
function lines(leaf) {
|
|
15461
|
+
if (address(leaf)) return [];
|
|
15462
|
+
return leaf.split(/[\r\n"\\]/).filter((line) => utf8(line).length >= FRAGMENT && !ARMOR.test(line));
|
|
15463
|
+
}
|
|
15464
|
+
function parsed(value2) {
|
|
15465
|
+
try {
|
|
15466
|
+
const document = JSON.parse(value2);
|
|
15467
|
+
return typeof document === "object" && document !== null ? document : null;
|
|
15468
|
+
} catch {
|
|
15469
|
+
return null;
|
|
15470
|
+
}
|
|
15471
|
+
}
|
|
15472
|
+
function fragments(value2) {
|
|
15473
|
+
const raw2 = utf8(value2);
|
|
15474
|
+
const document = parsed(value2);
|
|
15475
|
+
if (!document) return { pieces: [raw2], whole: true };
|
|
15476
|
+
return { pieces: [...new Set(strings(document).flatMap(lines))].map(utf8), whole: false };
|
|
15477
|
+
}
|
|
15478
|
+
function shapes(value2, split3 = fragments(value2)) {
|
|
14243
15479
|
const raw2 = utf8(value2);
|
|
14244
15480
|
const seen = /* @__PURE__ */ new Set([value2]);
|
|
14245
15481
|
const out = [];
|
|
14246
|
-
if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
|
|
14247
|
-
|
|
14248
|
-
|
|
14249
|
-
|
|
15482
|
+
if (raw2.length < FRAGMENT || !split3.whole) out.push({ bytes: raw2, form: "raw" });
|
|
15483
|
+
split3.pieces.forEach((bytes, piece) => {
|
|
15484
|
+
for (const offset of windows(bytes.length)) {
|
|
15485
|
+
out.push({
|
|
15486
|
+
bytes: bytes.subarray(offset, offset + FRAGMENT),
|
|
15487
|
+
form: "fragment",
|
|
15488
|
+
offset,
|
|
15489
|
+
piece
|
|
15490
|
+
});
|
|
15491
|
+
}
|
|
15492
|
+
});
|
|
14250
15493
|
const add = (text, form) => {
|
|
14251
15494
|
if (seen.has(text)) return;
|
|
14252
15495
|
seen.add(text);
|
|
@@ -14379,12 +15622,19 @@ function prepare(secrets, sameLength) {
|
|
|
14379
15622
|
const ids = /* @__PURE__ */ new Map();
|
|
14380
15623
|
const wrapIds = /* @__PURE__ */ new Map();
|
|
14381
15624
|
const values = secrets.map((secret) => utf8(secret.value));
|
|
15625
|
+
const splits = [];
|
|
14382
15626
|
secrets.forEach((secret, index) => {
|
|
14383
15627
|
if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
|
|
14384
15628
|
throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
|
|
14385
15629
|
}
|
|
14386
|
-
|
|
14387
|
-
|
|
15630
|
+
splits.push(fragments(secret.value));
|
|
15631
|
+
for (const shape of shapes(secret.value, splits[index])) {
|
|
15632
|
+
const source = {
|
|
15633
|
+
secret: index,
|
|
15634
|
+
form: shape.form,
|
|
15635
|
+
offset: shape.offset,
|
|
15636
|
+
piece: shape.piece
|
|
15637
|
+
};
|
|
14388
15638
|
register(main, sources, ids, shape.bytes, source);
|
|
14389
15639
|
if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
|
|
14390
15640
|
}
|
|
@@ -14395,7 +15645,16 @@ function prepare(secrets, sameLength) {
|
|
|
14395
15645
|
utf8(secret.placeholder),
|
|
14396
15646
|
utf8(`[redacted:${secret.name}]`)
|
|
14397
15647
|
]);
|
|
14398
|
-
return {
|
|
15648
|
+
return {
|
|
15649
|
+
secrets,
|
|
15650
|
+
splits,
|
|
15651
|
+
main,
|
|
15652
|
+
wrap,
|
|
15653
|
+
sources,
|
|
15654
|
+
wrapSources,
|
|
15655
|
+
replacements,
|
|
15656
|
+
sameLength
|
|
15657
|
+
};
|
|
14399
15658
|
}
|
|
14400
15659
|
function filled(input, start2, end) {
|
|
14401
15660
|
const out = new Uint8Array(end - start2);
|
|
@@ -14426,14 +15685,16 @@ function resolve(c, input, start2, end, source) {
|
|
|
14426
15685
|
growing: false
|
|
14427
15686
|
};
|
|
14428
15687
|
}
|
|
14429
|
-
const
|
|
15688
|
+
const split3 = c.splits[source.secret];
|
|
15689
|
+
const value2 = split3.pieces[source.piece];
|
|
14430
15690
|
let stop = end;
|
|
14431
15691
|
let at = source.offset + FRAGMENT;
|
|
14432
15692
|
while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
|
|
14433
15693
|
stop++;
|
|
14434
15694
|
at++;
|
|
14435
15695
|
}
|
|
14436
|
-
const
|
|
15696
|
+
const whole = split3.whole && source.offset === 0 && at === value2.length;
|
|
15697
|
+
const form = whole ? "raw" : "fragment";
|
|
14437
15698
|
const growing = stop === input.length && at < value2.length;
|
|
14438
15699
|
return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
|
|
14439
15700
|
}
|
|
@@ -14587,9 +15848,9 @@ function compile(secrets, options = {}) {
|
|
|
14587
15848
|
// src/kernel/scrub-file.ts
|
|
14588
15849
|
var SCRUB_IDLE_MS = 50;
|
|
14589
15850
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
14590
|
-
var
|
|
15851
|
+
var encoder6 = new TextEncoder();
|
|
14591
15852
|
function scrubberOf(secrets, sameLength = false) {
|
|
14592
|
-
const usable3 = secrets.filter((s) =>
|
|
15853
|
+
const usable3 = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
|
|
14593
15854
|
return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
|
|
14594
15855
|
}
|
|
14595
15856
|
async function writeAll(file, bytes) {
|
|
@@ -14613,7 +15874,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14613
15874
|
let timer;
|
|
14614
15875
|
let failed5;
|
|
14615
15876
|
const waiters = /* @__PURE__ */ new Set();
|
|
14616
|
-
const
|
|
15877
|
+
const quiet5 = () => !busy && pending === 0;
|
|
14617
15878
|
const wake = () => {
|
|
14618
15879
|
for (const waiter of [...waiters]) waiter();
|
|
14619
15880
|
};
|
|
@@ -14630,7 +15891,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14630
15891
|
if (failed5 === void 0) failed5 = err;
|
|
14631
15892
|
}).finally(() => {
|
|
14632
15893
|
pending -= 1;
|
|
14633
|
-
if (
|
|
15894
|
+
if (quiet5()) wake();
|
|
14634
15895
|
});
|
|
14635
15896
|
};
|
|
14636
15897
|
const settling = (signal) => new Promise((resolve2, reject) => {
|
|
@@ -14648,7 +15909,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14648
15909
|
signal?.addEventListener("abort", stop, { once: true });
|
|
14649
15910
|
});
|
|
14650
15911
|
const settled = async () => {
|
|
14651
|
-
while (!
|
|
15912
|
+
while (!quiet5()) await settling();
|
|
14652
15913
|
};
|
|
14653
15914
|
const changed = async (signal) => {
|
|
14654
15915
|
const round = new AbortController();
|
|
@@ -14664,7 +15925,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14664
15925
|
};
|
|
14665
15926
|
const turn = async (signal) => {
|
|
14666
15927
|
for (let waited = false; ; waited = true) {
|
|
14667
|
-
if (
|
|
15928
|
+
if (quiet5() && (pollFile(file).writable || !file.changed)) {
|
|
14668
15929
|
busy = true;
|
|
14669
15930
|
return;
|
|
14670
15931
|
}
|
|
@@ -14693,13 +15954,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14693
15954
|
...bound2(file, "stat"),
|
|
14694
15955
|
poll: () => {
|
|
14695
15956
|
const state = pollFile(file);
|
|
14696
|
-
return
|
|
15957
|
+
return quiet5() ? state : { ...state, writable: false };
|
|
14697
15958
|
},
|
|
14698
15959
|
changed,
|
|
14699
15960
|
...file.tty ? { tty: file.tty } : {},
|
|
14700
15961
|
identity: file.identity ?? file,
|
|
14701
15962
|
...file.clipboard ? { clipboard: file.clipboard } : {},
|
|
14702
15963
|
...file.embedder ? { embedder: file.embedder } : {},
|
|
15964
|
+
...file.device ? { device: file.device } : {},
|
|
14703
15965
|
...file.write ? {
|
|
14704
15966
|
write: async (bytes, signal) => {
|
|
14705
15967
|
await turn(signal);
|
|
@@ -14711,7 +15973,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14711
15973
|
await send2(stream2.push(taken), signal);
|
|
14712
15974
|
} finally {
|
|
14713
15975
|
busy = false;
|
|
14714
|
-
if (
|
|
15976
|
+
if (quiet5()) wake();
|
|
14715
15977
|
}
|
|
14716
15978
|
timer = setTimeout(release, idleMs);
|
|
14717
15979
|
return taken.length;
|
|
@@ -14770,9 +16032,9 @@ function scrubStdio(fds, scrubber) {
|
|
|
14770
16032
|
// src/kernel/net/sealed-scrubs.ts
|
|
14771
16033
|
function addTo(tree2, host, secrets) {
|
|
14772
16034
|
const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
|
|
14773
|
-
const
|
|
14774
|
-
if (
|
|
14775
|
-
entry.secrets = [...entry.secrets, ...
|
|
16035
|
+
const fresh4 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
|
|
16036
|
+
if (fresh4.length === 0) return;
|
|
16037
|
+
entry.secrets = [...entry.secrets, ...fresh4];
|
|
14776
16038
|
entry.version += 1;
|
|
14777
16039
|
entry.scrubber = void 0;
|
|
14778
16040
|
tree2.set(host, entry);
|
|
@@ -14935,7 +16197,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
|
|
|
14935
16197
|
var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
14936
16198
|
var TARGET = /^[\x21-\x7e]+$/;
|
|
14937
16199
|
var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
|
|
14938
|
-
var
|
|
16200
|
+
var HOST2 = /^[a-z0-9.:-]+$/;
|
|
14939
16201
|
var UNSAFE = /[\r\n\0]/;
|
|
14940
16202
|
var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
|
|
14941
16203
|
function verifiedByOk(f) {
|
|
@@ -14989,7 +16251,7 @@ function scramOk(f) {
|
|
|
14989
16251
|
return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
|
|
14990
16252
|
}
|
|
14991
16253
|
function hostOk(host) {
|
|
14992
|
-
return typeof host === "string" &&
|
|
16254
|
+
return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
|
|
14993
16255
|
}
|
|
14994
16256
|
function sessionAskable(f, now) {
|
|
14995
16257
|
if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
|
|
@@ -15079,20 +16341,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
|
|
|
15079
16341
|
"caller",
|
|
15080
16342
|
"requester"
|
|
15081
16343
|
]);
|
|
15082
|
-
var
|
|
16344
|
+
var encoder7 = new TextEncoder();
|
|
15083
16345
|
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
15084
16346
|
function parseSealed(bytes) {
|
|
15085
16347
|
try {
|
|
15086
16348
|
if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
|
|
15087
|
-
let
|
|
16349
|
+
let parsed3;
|
|
15088
16350
|
try {
|
|
15089
|
-
|
|
16351
|
+
parsed3 = JSON.parse(decoder3.decode(bytes));
|
|
15090
16352
|
} catch {
|
|
15091
16353
|
throw new KernelError("EINVAL");
|
|
15092
16354
|
}
|
|
15093
|
-
if (typeof
|
|
16355
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
15094
16356
|
throw new KernelError("EINVAL");
|
|
15095
|
-
const req =
|
|
16357
|
+
const req = parsed3;
|
|
15096
16358
|
if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
|
|
15097
16359
|
throw new KernelError("EINVAL");
|
|
15098
16360
|
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
@@ -15188,7 +16450,7 @@ var SealedChannel = class {
|
|
|
15188
16450
|
await this.broker.tickets.record(req, reply, caller.pid);
|
|
15189
16451
|
if (this.inflight !== flight) return;
|
|
15190
16452
|
this.inflight = void 0;
|
|
15191
|
-
if (reply) this.reply =
|
|
16453
|
+
if (reply) this.reply = encoder7.encode(serialized(reply));
|
|
15192
16454
|
});
|
|
15193
16455
|
this.inflight = flight;
|
|
15194
16456
|
return length2;
|
|
@@ -15464,7 +16726,7 @@ function shown(text) {
|
|
|
15464
16726
|
const chars = [...safe];
|
|
15465
16727
|
return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
|
|
15466
16728
|
}
|
|
15467
|
-
function
|
|
16729
|
+
function unescaped2(double) {
|
|
15468
16730
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
15469
16731
|
}
|
|
15470
16732
|
function splitWords(text) {
|
|
@@ -15480,7 +16742,7 @@ function splitWords(text) {
|
|
|
15480
16742
|
word = void 0;
|
|
15481
16743
|
continue;
|
|
15482
16744
|
}
|
|
15483
|
-
word = (word ?? "") + (single ?? (double !== void 0 ?
|
|
16745
|
+
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
|
|
15484
16746
|
}
|
|
15485
16747
|
if (word !== void 0) words3.push(word);
|
|
15486
16748
|
return words3;
|
|
@@ -15517,7 +16779,7 @@ function splitPatterns(text) {
|
|
|
15517
16779
|
}
|
|
15518
16780
|
word ??= [];
|
|
15519
16781
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
15520
|
-
else addPart(word, single ?? (double !== void 0 ?
|
|
16782
|
+
else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
|
|
15521
16783
|
}
|
|
15522
16784
|
if (word !== void 0) words3.push(word);
|
|
15523
16785
|
return words3;
|
|
@@ -15568,13 +16830,13 @@ function commandMatches(cmnd, argv) {
|
|
|
15568
16830
|
if (cmnd === "*") return true;
|
|
15569
16831
|
const [path, ...args] = argv;
|
|
15570
16832
|
if (path !== cmnd.path) return false;
|
|
15571
|
-
const
|
|
15572
|
-
if (
|
|
16833
|
+
const want2 = cmnd.args;
|
|
16834
|
+
if (want2 === void 0) return true;
|
|
15573
16835
|
return globMatch(
|
|
15574
|
-
|
|
16836
|
+
want2.length,
|
|
15575
16837
|
args.length,
|
|
15576
|
-
(i) => isAny(
|
|
15577
|
-
(i, j) => wordMatch(
|
|
16838
|
+
(i) => isAny(want2[i]),
|
|
16839
|
+
(i, j) => wordMatch(want2[i], args[j])
|
|
15578
16840
|
);
|
|
15579
16841
|
}
|
|
15580
16842
|
function commandText(cmnd) {
|
|
@@ -15871,11 +17133,11 @@ function parsePolicy(text, file) {
|
|
|
15871
17133
|
const defaults2 = [];
|
|
15872
17134
|
const errors = [];
|
|
15873
17135
|
for (const [n, line] of text.split("\n").entries()) {
|
|
15874
|
-
const
|
|
15875
|
-
if (
|
|
15876
|
-
if ("error" in
|
|
15877
|
-
else if ("defaults" in
|
|
15878
|
-
else rules.push(...
|
|
17136
|
+
const parsed3 = parseLine(line, file);
|
|
17137
|
+
if (parsed3 === null) continue;
|
|
17138
|
+
if ("error" in parsed3) errors.push({ line: n + 1, message: parsed3.error });
|
|
17139
|
+
else if ("defaults" in parsed3) defaults2.push(parsed3.defaults);
|
|
17140
|
+
else rules.push(...parsed3.rules);
|
|
15879
17141
|
}
|
|
15880
17142
|
return { rules, defaults: defaults2, errors };
|
|
15881
17143
|
}
|
|
@@ -15895,18 +17157,18 @@ var HOME_DIR = /^\/home\/[^/]+$/;
|
|
|
15895
17157
|
var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
|
|
15896
17158
|
var PNPM_CONFIG = "minimumReleaseAge: 1440\n";
|
|
15897
17159
|
var isId = (text) => text !== void 0 && /^\d+$/.test(text);
|
|
15898
|
-
function
|
|
17160
|
+
function lines2(text) {
|
|
15899
17161
|
return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
|
|
15900
17162
|
}
|
|
15901
17163
|
function parsePasswd(text) {
|
|
15902
|
-
return
|
|
17164
|
+
return lines2(text).map((line) => {
|
|
15903
17165
|
const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
|
|
15904
17166
|
if (!name2 || !isId(uid) || !isId(gid)) return line;
|
|
15905
17167
|
return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
|
|
15906
17168
|
});
|
|
15907
17169
|
}
|
|
15908
17170
|
function parseGroup(text) {
|
|
15909
|
-
return
|
|
17171
|
+
return lines2(text).map((line) => {
|
|
15910
17172
|
const [name2, , gid, members = ""] = line.split(":");
|
|
15911
17173
|
if (!name2 || !isId(gid)) return line;
|
|
15912
17174
|
return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
|
|
@@ -15922,7 +17184,7 @@ function formatGroup(rows) {
|
|
|
15922
17184
|
return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
|
|
15923
17185
|
`;
|
|
15924
17186
|
}
|
|
15925
|
-
var
|
|
17187
|
+
var parsed2 = (rows) => rows.filter((r) => typeof r !== "string");
|
|
15926
17188
|
function homeOf(home) {
|
|
15927
17189
|
if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
|
|
15928
17190
|
throw fsError("EINVAL", `not a home directory: ${String(home)}`);
|
|
@@ -16115,20 +17377,20 @@ var UserDb = class {
|
|
|
16115
17377
|
return (await this.lookupTerminal(user))?.account;
|
|
16116
17378
|
}
|
|
16117
17379
|
async ttyGroup() {
|
|
16118
|
-
return
|
|
17380
|
+
return parsed2(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
|
|
16119
17381
|
}
|
|
16120
17382
|
async lookupTerminal(user) {
|
|
16121
17383
|
const { passwd, group } = await this.tables();
|
|
16122
17384
|
const key = typeof user === "number" ? "uid" : "name";
|
|
16123
|
-
const row =
|
|
16124
|
-
const groups =
|
|
17385
|
+
const row = parsed2(passwd).find((r) => r[key] === user);
|
|
17386
|
+
const groups = parsed2(group);
|
|
16125
17387
|
const tty = groups.find((g) => g.name === "tty")?.gid;
|
|
16126
17388
|
return row && { account: this.account(row, groups), tty };
|
|
16127
17389
|
}
|
|
16128
17390
|
async list() {
|
|
16129
17391
|
const { passwd, group } = await this.tables();
|
|
16130
|
-
const groups =
|
|
16131
|
-
return
|
|
17392
|
+
const groups = parsed2(group);
|
|
17393
|
+
return parsed2(passwd).map((row) => this.account(row, groups));
|
|
16132
17394
|
}
|
|
16133
17395
|
add(options) {
|
|
16134
17396
|
return this.serial(async () => {
|
|
@@ -16140,8 +17402,8 @@ var UserDb = class {
|
|
|
16140
17402
|
if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
|
|
16141
17403
|
};
|
|
16142
17404
|
const { passwd, group } = await this.tables();
|
|
16143
|
-
const users =
|
|
16144
|
-
const groups =
|
|
17405
|
+
const users = parsed2(passwd);
|
|
17406
|
+
const groups = parsed2(group);
|
|
16145
17407
|
const found = users.find((r) => r.name === name2);
|
|
16146
17408
|
const shared2 = homeGroupOf(groups, options.homeGroup);
|
|
16147
17409
|
const homeMode = homeModeOf(options.homeMode);
|
|
@@ -16166,7 +17428,7 @@ var UserDb = class {
|
|
|
16166
17428
|
joined3(name2, extra);
|
|
16167
17429
|
group.push({ name: name2, gid: uid, members: [] });
|
|
16168
17430
|
passwd.push(row);
|
|
16169
|
-
const
|
|
17431
|
+
const fresh4 = !await this.fs.exists(home);
|
|
16170
17432
|
await this.fs.mkdir(home, { recursive: true });
|
|
16171
17433
|
try {
|
|
16172
17434
|
await this.provision(row, this.umaskFor(options), shared2);
|
|
@@ -16174,16 +17436,16 @@ var UserDb = class {
|
|
|
16174
17436
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
16175
17437
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
16176
17438
|
} catch (err) {
|
|
16177
|
-
if (
|
|
17439
|
+
if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
|
|
16178
17440
|
throw err;
|
|
16179
17441
|
}
|
|
16180
17442
|
remember();
|
|
16181
|
-
return this.account(row,
|
|
17443
|
+
return this.account(row, parsed2(group));
|
|
16182
17444
|
});
|
|
16183
17445
|
}
|
|
16184
17446
|
async groups() {
|
|
16185
17447
|
const { group } = await this.tables();
|
|
16186
|
-
return
|
|
17448
|
+
return parsed2(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
|
|
16187
17449
|
}
|
|
16188
17450
|
addGroup(name2, gid) {
|
|
16189
17451
|
return this.serial(async () => {
|
|
@@ -16194,7 +17456,7 @@ var UserDb = class {
|
|
|
16194
17456
|
throw fsError("EINVAL", `not a group id: ${String(gid)}`);
|
|
16195
17457
|
}
|
|
16196
17458
|
const { group } = await this.tables();
|
|
16197
|
-
const rows =
|
|
17459
|
+
const rows = parsed2(group);
|
|
16198
17460
|
const found = rows.find((g) => g.name === name2);
|
|
16199
17461
|
if (found) return { name: found.name, gid: found.gid, members: [...found.members] };
|
|
16200
17462
|
const taken = rows.find((g) => g.gid === gid);
|
|
@@ -16214,9 +17476,9 @@ var UserDb = class {
|
|
|
16214
17476
|
member(name2, user, on) {
|
|
16215
17477
|
return this.serial(async () => {
|
|
16216
17478
|
const { passwd, group } = await this.tables();
|
|
16217
|
-
const row =
|
|
17479
|
+
const row = parsed2(group).find((g) => g.name === name2);
|
|
16218
17480
|
if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
|
|
16219
|
-
const who =
|
|
17481
|
+
const who = parsed2(passwd).find((u) => u.name === user);
|
|
16220
17482
|
if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
16221
17483
|
if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
|
|
16222
17484
|
if (row.members.includes(user) !== on) {
|
|
@@ -16229,7 +17491,7 @@ var UserDb = class {
|
|
|
16229
17491
|
remove(name2, options = {}) {
|
|
16230
17492
|
return this.serial(async () => {
|
|
16231
17493
|
const { passwd, group } = await this.tables();
|
|
16232
|
-
const row =
|
|
17494
|
+
const row = parsed2(passwd).find((r) => r.name === name2);
|
|
16233
17495
|
if (!row) return false;
|
|
16234
17496
|
if (row.uid < FIRST_UID || row.uid > LAST_UID) {
|
|
16235
17497
|
throw fsError("EPERM", `${name2} is a system account`);
|
|
@@ -16237,9 +17499,9 @@ var UserDb = class {
|
|
|
16237
17499
|
const keptGroups = group.filter(
|
|
16238
17500
|
(r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
|
|
16239
17501
|
);
|
|
16240
|
-
for (const r of
|
|
17502
|
+
for (const r of parsed2(keptGroups)) r.members = r.members.filter((m) => m !== name2);
|
|
16241
17503
|
const home = this.fs.resolvePath("/", row.home);
|
|
16242
|
-
const shared2 =
|
|
17504
|
+
const shared2 = parsed2(passwd).some(
|
|
16243
17505
|
(r) => r !== row && this.fs.resolvePath("/", r.home) === home
|
|
16244
17506
|
);
|
|
16245
17507
|
const removeHome = !options.keepHome && !shared2 && HOME_DIR.test(home) && await this.fs.exists(home);
|
|
@@ -16305,11 +17567,11 @@ var REPLAY_TAGS_MAX = 64;
|
|
|
16305
17567
|
var REPLAY_ARGV_MAX = 4096;
|
|
16306
17568
|
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
16307
17569
|
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
16308
|
-
function
|
|
17570
|
+
function strings2(value2, max) {
|
|
16309
17571
|
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
16310
17572
|
}
|
|
16311
17573
|
function words(value2) {
|
|
16312
|
-
if (!
|
|
17574
|
+
if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
16313
17575
|
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
16314
17576
|
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
16315
17577
|
}
|
|
@@ -16344,7 +17606,7 @@ function replayOf(value2) {
|
|
|
16344
17606
|
const bound3 = filesOf(r.userWritable);
|
|
16345
17607
|
const named = (s) => typeof s === "string" && s !== "";
|
|
16346
17608
|
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
16347
|
-
const valid2 = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 ||
|
|
17609
|
+
const valid2 = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 || strings2(tags, REPLAY_TAGS_MAX)) && rootOnly2 !== void 0 && bound3 !== void 0;
|
|
16348
17610
|
if (!valid2) return failed2("EINVAL", NOT_A_REQUEST);
|
|
16349
17611
|
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
16350
17612
|
return {
|
|
@@ -16436,28 +17698,28 @@ function recordedCommand(argv) {
|
|
|
16436
17698
|
const [path = "", ...args] = argv;
|
|
16437
17699
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
16438
17700
|
}
|
|
16439
|
-
function flags(
|
|
17701
|
+
function flags(parsed3, arg, next3) {
|
|
16440
17702
|
for (let j = 1; j < arg.length; j++) {
|
|
16441
17703
|
const flag = arg[j];
|
|
16442
|
-
if (flag === "n")
|
|
16443
|
-
else if (flag === "l")
|
|
16444
|
-
else if (flag === "v")
|
|
16445
|
-
else if (flag === "k")
|
|
16446
|
-
else if (flag === "K")
|
|
17704
|
+
if (flag === "n") parsed3.nonInteractive = true;
|
|
17705
|
+
else if (flag === "l") parsed3.list = true;
|
|
17706
|
+
else if (flag === "v") parsed3.validate = true;
|
|
17707
|
+
else if (flag === "k") parsed3.reset = true;
|
|
17708
|
+
else if (flag === "K") parsed3.removeAll = true;
|
|
16447
17709
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
16448
17710
|
${USAGE}`;
|
|
16449
17711
|
else {
|
|
16450
17712
|
const user = arg.slice(j + 1) || next3;
|
|
16451
17713
|
if (!user) return `sudo: option requires an argument -- 'u'
|
|
16452
17714
|
${USAGE}`;
|
|
16453
|
-
|
|
17715
|
+
parsed3.user = user;
|
|
16454
17716
|
return arg.slice(j + 1) ? 0 : 1;
|
|
16455
17717
|
}
|
|
16456
17718
|
}
|
|
16457
17719
|
return 0;
|
|
16458
17720
|
}
|
|
16459
17721
|
function parseArgs(args) {
|
|
16460
|
-
const
|
|
17722
|
+
const parsed3 = {
|
|
16461
17723
|
list: false,
|
|
16462
17724
|
nonInteractive: false,
|
|
16463
17725
|
validate: false,
|
|
@@ -16474,19 +17736,19 @@ function parseArgs(args) {
|
|
|
16474
17736
|
break;
|
|
16475
17737
|
}
|
|
16476
17738
|
if (!arg.startsWith("-") || arg === "-") break;
|
|
16477
|
-
const used = flags(
|
|
17739
|
+
const used = flags(parsed3, arg, args[i + 1]);
|
|
16478
17740
|
if (typeof used === "string") return used;
|
|
16479
17741
|
i += used;
|
|
16480
17742
|
}
|
|
16481
|
-
|
|
16482
|
-
return usable(
|
|
17743
|
+
parsed3.command = args.slice(i);
|
|
17744
|
+
return usable(parsed3) ? parsed3 : USAGE;
|
|
16483
17745
|
}
|
|
16484
|
-
function usable(
|
|
16485
|
-
const command =
|
|
16486
|
-
const modes = [
|
|
16487
|
-
if (modes > 1 ||
|
|
17746
|
+
function usable(parsed3) {
|
|
17747
|
+
const command = parsed3.command.length > 0;
|
|
17748
|
+
const modes = [parsed3.list, parsed3.validate, parsed3.removeAll].filter(Boolean).length;
|
|
17749
|
+
if (modes > 1 || parsed3.removeAll && parsed3.reset) return false;
|
|
16488
17750
|
if (modes === 1) return !command;
|
|
16489
|
-
return command ||
|
|
17751
|
+
return command || parsed3.reset;
|
|
16490
17752
|
}
|
|
16491
17753
|
function policyOf(settings) {
|
|
16492
17754
|
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
@@ -16630,28 +17892,28 @@ var Sudo = class {
|
|
|
16630
17892
|
};
|
|
16631
17893
|
}
|
|
16632
17894
|
async check(cred, call) {
|
|
16633
|
-
const
|
|
17895
|
+
const lines3 = [];
|
|
16634
17896
|
let ok4 = true;
|
|
16635
17897
|
const report = ({ path, problems, ok: read }) => {
|
|
16636
17898
|
for (const problem of problems) {
|
|
16637
17899
|
const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
|
|
16638
|
-
|
|
17900
|
+
lines3.push([2, `visudo: ${where} ${problem.message}`]);
|
|
16639
17901
|
}
|
|
16640
|
-
if (read && problems.length === 0)
|
|
17902
|
+
if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
|
|
16641
17903
|
if (problems.length > 0) ok4 = false;
|
|
16642
17904
|
};
|
|
16643
17905
|
const values = (defaults2) => {
|
|
16644
|
-
for (const line of defaultsText(defaults2))
|
|
17906
|
+
for (const line of defaultsText(defaults2)) lines3.push([1, line]);
|
|
16645
17907
|
};
|
|
16646
17908
|
if (call.files !== void 0) {
|
|
16647
17909
|
const defaults2 = [];
|
|
16648
17910
|
for (const { name: name2, text } of call.files) {
|
|
16649
|
-
const
|
|
16650
|
-
defaults2.push(...
|
|
16651
|
-
report({ path: name2, rules: [], problems:
|
|
17911
|
+
const parsed3 = parsePolicy(text);
|
|
17912
|
+
defaults2.push(...parsed3.defaults);
|
|
17913
|
+
report({ path: name2, rules: [], problems: parsed3.errors, ok: true });
|
|
16652
17914
|
}
|
|
16653
17915
|
if (defaults2.length > 0) values(defaults2);
|
|
16654
|
-
return { lines:
|
|
17916
|
+
return { lines: lines3, ok: ok4 };
|
|
16655
17917
|
}
|
|
16656
17918
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
16657
17919
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
@@ -16660,7 +17922,7 @@ var Sudo = class {
|
|
|
16660
17922
|
const files = await this.policy();
|
|
16661
17923
|
for (const file of files) report(file);
|
|
16662
17924
|
values(files.flatMap((f) => f.defaults ?? []));
|
|
16663
|
-
return { lines:
|
|
17925
|
+
return { lines: lines3, ok: ok4 };
|
|
16664
17926
|
}
|
|
16665
17927
|
async groupIds() {
|
|
16666
17928
|
return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
|
|
@@ -16725,13 +17987,13 @@ var Sudo = class {
|
|
|
16725
17987
|
return void 0;
|
|
16726
17988
|
}
|
|
16727
17989
|
async request(caller, call, signal) {
|
|
16728
|
-
const
|
|
16729
|
-
if (typeof
|
|
17990
|
+
const parsed3 = parseArgs(call.args);
|
|
17991
|
+
if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
|
|
16730
17992
|
if (this.o.mode !== "enforce") {
|
|
16731
17993
|
const mode = this.o.mode;
|
|
16732
|
-
if (
|
|
17994
|
+
if (parsed3.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
16733
17995
|
` };
|
|
16734
|
-
return
|
|
17996
|
+
return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
|
|
16735
17997
|
}
|
|
16736
17998
|
const { cred } = caller;
|
|
16737
17999
|
const policy = await this.policyRules(caller);
|
|
@@ -16744,22 +18006,22 @@ var Sudo = class {
|
|
|
16744
18006
|
};
|
|
16745
18007
|
const applies = await this.applies(self, cred);
|
|
16746
18008
|
const settings = resolveDefaults(policy.defaults, applies);
|
|
16747
|
-
if (
|
|
18009
|
+
if (parsed3.removeAll) {
|
|
16748
18010
|
this.timestamps.remove(owner.uid);
|
|
16749
18011
|
return DONE;
|
|
16750
18012
|
}
|
|
16751
|
-
if (
|
|
18013
|
+
if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
|
|
16752
18014
|
this.timestamps.invalidate(owner, settings.timestampType);
|
|
16753
18015
|
return DONE;
|
|
16754
18016
|
}
|
|
16755
|
-
const target = await this.lookup(
|
|
16756
|
-
if (!target) return failed2("ENOENT", `sudo: unknown user ${
|
|
18017
|
+
const target = await this.lookup(parsed3.user);
|
|
18018
|
+
if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed3.user}`);
|
|
16757
18019
|
const root = cred.euid === 0;
|
|
16758
18020
|
const rules = root ? [] : policy.rules;
|
|
16759
18021
|
const mine = rules.filter((rule) => applies(rule.who));
|
|
16760
|
-
if (
|
|
16761
|
-
const session = { caller, self, target, owner, policy: policyOf(settings), parsed:
|
|
16762
|
-
if (
|
|
18022
|
+
if (parsed3.list) return await this.listed(self, root, mine, settings.matching);
|
|
18023
|
+
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed3, signal };
|
|
18024
|
+
if (parsed3.validate) return this.validate(session, root, mine, call.cwd);
|
|
16763
18025
|
return this.command(session, root, mine, call);
|
|
16764
18026
|
}
|
|
16765
18027
|
async replay(value2) {
|
|
@@ -16818,9 +18080,9 @@ var Sudo = class {
|
|
|
16818
18080
|
);
|
|
16819
18081
|
}
|
|
16820
18082
|
async command(s, root, mine, call) {
|
|
16821
|
-
const { caller, self, target, parsed:
|
|
18083
|
+
const { caller, self, target, parsed: parsed3 } = s;
|
|
16822
18084
|
const { cred } = caller;
|
|
16823
|
-
const [word = "", ...args] =
|
|
18085
|
+
const [word = "", ...args] = parsed3.command;
|
|
16824
18086
|
const cwd = await this.canonical(call.cwd);
|
|
16825
18087
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
16826
18088
|
return failed2(
|
|
@@ -16835,7 +18097,7 @@ var Sudo = class {
|
|
|
16835
18097
|
const matching = mine.filter(
|
|
16836
18098
|
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, bound3)
|
|
16837
18099
|
);
|
|
16838
|
-
const grant = () => this.grant(self, target, cred,
|
|
18100
|
+
const grant = () => this.grant(self, target, cred, parsed3.command, path, cwd, call.term, bound3);
|
|
16839
18101
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
16840
18102
|
const quoted = argv.map(quoteWord).join(" ");
|
|
16841
18103
|
if (matching.length === 0) {
|
|
@@ -16844,38 +18106,38 @@ var Sudo = class {
|
|
|
16844
18106
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
16845
18107
|
);
|
|
16846
18108
|
}
|
|
16847
|
-
if (!
|
|
18109
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
|
|
16848
18110
|
this.timestamps.stamp(s.owner, s.policy, bound3);
|
|
16849
18111
|
this.logged(s, cwd, argv);
|
|
16850
18112
|
return grant();
|
|
16851
18113
|
}
|
|
16852
|
-
if (
|
|
18114
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16853
18115
|
return this.pendingFor(cred.ruid, async () => {
|
|
16854
|
-
const answer2 = await this.ask(s, { argv:
|
|
18116
|
+
const answer2 = await this.ask(s, { argv: parsed3.command, path, cwd, bound: bound3 });
|
|
16855
18117
|
if ("error" in answer2) return answer2;
|
|
16856
18118
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
16857
18119
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
16858
18120
|
}
|
|
16859
18121
|
const now = await this.bound(path, cwd);
|
|
16860
18122
|
if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
16861
|
-
const shown2 = recordable(cwd, [...
|
|
18123
|
+
const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
|
|
16862
18124
|
const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
|
|
16863
18125
|
if (!("error" in result)) this.stamped(s, answer2, bound3);
|
|
16864
18126
|
return result;
|
|
16865
18127
|
});
|
|
16866
18128
|
}
|
|
16867
18129
|
async validate(s, root, mine, dir) {
|
|
16868
|
-
const { self, parsed:
|
|
18130
|
+
const { self, parsed: parsed3 } = s;
|
|
16869
18131
|
if (root) return DONE;
|
|
16870
18132
|
if (mine.length === 0) {
|
|
16871
18133
|
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
16872
18134
|
}
|
|
16873
18135
|
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
16874
|
-
if (!
|
|
18136
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
16875
18137
|
this.timestamps.stamp(s.owner, s.policy);
|
|
16876
18138
|
return DONE;
|
|
16877
18139
|
}
|
|
16878
|
-
if (
|
|
18140
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16879
18141
|
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
16880
18142
|
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
16881
18143
|
const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
|
|
@@ -16930,7 +18192,7 @@ var Sudo = class {
|
|
|
16930
18192
|
}
|
|
16931
18193
|
const shown2 = [];
|
|
16932
18194
|
for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
|
|
16933
|
-
const
|
|
18195
|
+
const lines3 = shown2.join("\n");
|
|
16934
18196
|
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
16935
18197
|
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
16936
18198
|
${params}
|
|
@@ -16938,7 +18200,7 @@ var Sudo = class {
|
|
|
16938
18200
|
`;
|
|
16939
18201
|
return {
|
|
16940
18202
|
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
16941
|
-
${
|
|
18203
|
+
${lines3}
|
|
16942
18204
|
`
|
|
16943
18205
|
};
|
|
16944
18206
|
}
|
|
@@ -17355,7 +18617,7 @@ async function epollSyscall(req, proc) {
|
|
|
17355
18617
|
|
|
17356
18618
|
// src/kernel/locks.ts
|
|
17357
18619
|
var overlaps = (a, b) => a.start < b.end && b.start < a.end;
|
|
17358
|
-
var clashes = (lock, owner,
|
|
18620
|
+
var clashes = (lock, owner, want2) => lock.owner !== owner && overlaps(lock, want2) && (lock.kind === "write" || want2.kind === "write");
|
|
17359
18621
|
function lockRange(start2, len) {
|
|
17360
18622
|
if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
|
|
17361
18623
|
const from = len < 0 ? start2 + len : start2;
|
|
@@ -17441,8 +18703,8 @@ var LockTable = class {
|
|
|
17441
18703
|
blocked = /* @__PURE__ */ new Map();
|
|
17442
18704
|
wakers = /* @__PURE__ */ new Map();
|
|
17443
18705
|
held = /* @__PURE__ */ new Map();
|
|
17444
|
-
holder(key, owner,
|
|
17445
|
-
return this.records.get(key)?.find((lock) => clashes(lock, owner,
|
|
18706
|
+
holder(key, owner, want2) {
|
|
18707
|
+
return this.records.get(key)?.find((lock) => clashes(lock, owner, want2));
|
|
17446
18708
|
}
|
|
17447
18709
|
set(key, owner, kind, start2, end) {
|
|
17448
18710
|
if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
|
|
@@ -17461,10 +18723,10 @@ var LockTable = class {
|
|
|
17461
18723
|
return true;
|
|
17462
18724
|
}
|
|
17463
18725
|
async wait(key, owner, kind, range2, blocking) {
|
|
17464
|
-
const
|
|
18726
|
+
const want2 = { key, kind, ...range2 };
|
|
17465
18727
|
let signal;
|
|
17466
18728
|
for (; ; ) {
|
|
17467
|
-
const holder = this.holder(key, owner,
|
|
18729
|
+
const holder = this.holder(key, owner, want2);
|
|
17468
18730
|
if (!holder) {
|
|
17469
18731
|
this.set(key, owner, kind, range2.start, range2.end);
|
|
17470
18732
|
return;
|
|
@@ -17472,7 +18734,7 @@ var LockTable = class {
|
|
|
17472
18734
|
if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
|
|
17473
18735
|
signal ??= blocking();
|
|
17474
18736
|
if (signal.aborted) throw new KernelError("EINTR");
|
|
17475
|
-
this.blocked.set(owner,
|
|
18737
|
+
this.blocked.set(owner, want2);
|
|
17476
18738
|
try {
|
|
17477
18739
|
await this.changed(key, signal);
|
|
17478
18740
|
} finally {
|
|
@@ -17484,9 +18746,9 @@ var LockTable = class {
|
|
|
17484
18746
|
const seen = /* @__PURE__ */ new Set();
|
|
17485
18747
|
for (let at = from; at !== void 0 && !seen.has(at); ) {
|
|
17486
18748
|
seen.add(at);
|
|
17487
|
-
const
|
|
17488
|
-
if (!
|
|
17489
|
-
const holder = this.holder(
|
|
18749
|
+
const want2 = this.blocked.get(at);
|
|
18750
|
+
if (!want2) return false;
|
|
18751
|
+
const holder = this.holder(want2.key, at, want2);
|
|
17490
18752
|
if (!holder) return false;
|
|
17491
18753
|
if (holder.owner === target) return true;
|
|
17492
18754
|
at = holder.owner;
|
|
@@ -17851,24 +19113,24 @@ function errno2(code, message3) {
|
|
|
17851
19113
|
}
|
|
17852
19114
|
function requestHead(method, url, headers, extra, first = []) {
|
|
17853
19115
|
if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
|
|
17854
|
-
const
|
|
17855
|
-
for (const [name2, value2] of first)
|
|
19116
|
+
const lines3 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
|
|
19117
|
+
for (const [name2, value2] of first) lines3.push(`${name2}: ${value2}`);
|
|
17856
19118
|
for (const [name2, value2] of headers) {
|
|
17857
19119
|
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
17858
19120
|
if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
|
|
17859
|
-
|
|
19121
|
+
lines3.push(`${name2}: ${value2}`);
|
|
17860
19122
|
}
|
|
17861
|
-
for (const [name2, value2] of extra)
|
|
17862
|
-
return latin1Bytes(`${
|
|
19123
|
+
for (const [name2, value2] of extra) lines3.push(`${name2}: ${value2}`);
|
|
19124
|
+
return latin1Bytes(`${lines3.join("\r\n")}\r
|
|
17863
19125
|
\r
|
|
17864
19126
|
`);
|
|
17865
19127
|
}
|
|
17866
19128
|
function parseHead(bytes) {
|
|
17867
|
-
const [statusLine = "", ...
|
|
19129
|
+
const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
|
|
17868
19130
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
17869
19131
|
if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
17870
19132
|
const headers = [];
|
|
17871
|
-
for (const line of
|
|
19133
|
+
for (const line of lines3) {
|
|
17872
19134
|
const colon = line.indexOf(":");
|
|
17873
19135
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
17874
19136
|
}
|
|
@@ -17958,8 +19220,8 @@ var KernelLoopback = class {
|
|
|
17958
19220
|
);
|
|
17959
19221
|
} catch (err) {
|
|
17960
19222
|
socket.close();
|
|
17961
|
-
const
|
|
17962
|
-
throw
|
|
19223
|
+
const refused5 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
19224
|
+
throw refused5 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
17963
19225
|
}
|
|
17964
19226
|
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
17965
19227
|
return socket;
|
|
@@ -18348,9 +19610,9 @@ function pem(der2) {
|
|
|
18348
19610
|
for (let i = 0; i < der2.length; i += 32768) {
|
|
18349
19611
|
b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
|
|
18350
19612
|
}
|
|
18351
|
-
const
|
|
19613
|
+
const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
|
|
18352
19614
|
return `-----BEGIN CERTIFICATE-----
|
|
18353
|
-
${
|
|
19615
|
+
${lines3.join("\n")}
|
|
18354
19616
|
-----END CERTIFICATE-----
|
|
18355
19617
|
`;
|
|
18356
19618
|
}
|
|
@@ -19089,13 +20351,13 @@ var HOSTS_PATH = "/etc/hosts";
|
|
|
19089
20351
|
function parseHosts(text) {
|
|
19090
20352
|
const table = /* @__PURE__ */ new Map();
|
|
19091
20353
|
for (const raw2 of text.split("\n")) {
|
|
19092
|
-
const [
|
|
19093
|
-
if (!
|
|
19094
|
-
if (ipv4Bytes(
|
|
20354
|
+
const [address2, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
|
|
20355
|
+
if (!address2 || names2.length === 0) continue;
|
|
20356
|
+
if (ipv4Bytes(address2) === void 0 && ipv6Groups(address2) === void 0) continue;
|
|
19095
20357
|
for (const name2 of names2) {
|
|
19096
20358
|
const key = name2.toLowerCase().replace(/\.$/, "");
|
|
19097
20359
|
const known = table.get(key) ?? [];
|
|
19098
|
-
if (!known.includes(
|
|
20360
|
+
if (!known.includes(address2)) known.push(address2);
|
|
19099
20361
|
table.set(key, known);
|
|
19100
20362
|
}
|
|
19101
20363
|
}
|
|
@@ -19183,14 +20445,14 @@ var Resolver = class {
|
|
|
19183
20445
|
if (hostsEntry(key, this.own) !== void 0) return true;
|
|
19184
20446
|
return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
|
|
19185
20447
|
}
|
|
19186
|
-
async reverse(
|
|
19187
|
-
const v6 = ipv6Groups(
|
|
19188
|
-
const same2 = (a) => a ===
|
|
20448
|
+
async reverse(address2) {
|
|
20449
|
+
const v6 = ipv6Groups(address2)?.join(":");
|
|
20450
|
+
const same2 = (a) => a === address2 || v6 !== void 0 && ipv6Groups(a)?.join(":") === v6;
|
|
19189
20451
|
const file = await this.hosts?.().catch(() => void 0);
|
|
19190
20452
|
for (const table of [file, this.builtin]) {
|
|
19191
20453
|
for (const [name2, addresses] of table ?? []) if (addresses.some(same2)) return name2;
|
|
19192
20454
|
}
|
|
19193
|
-
if (ipv4Bytes(
|
|
20455
|
+
if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
|
|
19194
20456
|
return void 0;
|
|
19195
20457
|
}
|
|
19196
20458
|
async resolve(name2, family, blocking) {
|
|
@@ -19244,13 +20506,13 @@ var Resolver = class {
|
|
|
19244
20506
|
if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
|
|
19245
20507
|
return addresses;
|
|
19246
20508
|
}
|
|
19247
|
-
usable(name2,
|
|
19248
|
-
const v4 = ipv4Bytes(
|
|
19249
|
-
if (!v4 && (family === 4 || !ipv6Groups(
|
|
20509
|
+
usable(name2, address2, family) {
|
|
20510
|
+
const v4 = ipv4Bytes(address2) !== void 0;
|
|
20511
|
+
if (!v4 && (family === 4 || !ipv6Groups(address2))) return false;
|
|
19250
20512
|
if (v4 && family === 6) return false;
|
|
19251
|
-
const kind = this.routes.classify(
|
|
20513
|
+
const kind = this.routes.classify(address2);
|
|
19252
20514
|
if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
|
|
19253
|
-
this.log(`slicc-kernel: dropped the uplink's answer ${
|
|
20515
|
+
this.log(`slicc-kernel: dropped the uplink's answer ${address2} for ${name2}: it is ${kind}`);
|
|
19254
20516
|
return false;
|
|
19255
20517
|
}
|
|
19256
20518
|
remember(id2, addresses, ttl) {
|
|
@@ -19284,275 +20546,6 @@ function waitFor(lookup4, signal) {
|
|
|
19284
20546
|
});
|
|
19285
20547
|
}
|
|
19286
20548
|
|
|
19287
|
-
// src/kernel/tty.ts
|
|
19288
|
-
var ICRNL = 256;
|
|
19289
|
-
var IUTF8 = 16384;
|
|
19290
|
-
var OPOST = 1;
|
|
19291
|
-
var ONLCR = 4;
|
|
19292
|
-
var ISIG = 1;
|
|
19293
|
-
var ICANON = 2;
|
|
19294
|
-
var ECHO = 8;
|
|
19295
|
-
var ECHOE = 16;
|
|
19296
|
-
var ECHOCTL = 512;
|
|
19297
|
-
var NOFLSH = 128;
|
|
19298
|
-
var IEXTEN = 32768;
|
|
19299
|
-
var VINTR = 0;
|
|
19300
|
-
var VQUIT = 1;
|
|
19301
|
-
var VERASE = 2;
|
|
19302
|
-
var VKILL = 3;
|
|
19303
|
-
var VEOF = 4;
|
|
19304
|
-
var VSUSP = 10;
|
|
19305
|
-
var VWERASE = 14;
|
|
19306
|
-
function defaultTermios() {
|
|
19307
|
-
const cCc = new Array(32).fill(0);
|
|
19308
|
-
Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
|
|
19309
|
-
Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
|
|
19310
|
-
Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
|
|
19311
|
-
return {
|
|
19312
|
-
c_iflag: ICRNL | 1024 | 8192 | IUTF8,
|
|
19313
|
-
c_oflag: OPOST | ONLCR,
|
|
19314
|
-
c_cflag: 15 | 48 | 128,
|
|
19315
|
-
c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
|
|
19316
|
-
c_cc: cCc
|
|
19317
|
-
};
|
|
19318
|
-
}
|
|
19319
|
-
var encoder7 = new TextEncoder();
|
|
19320
|
-
function isContinuation(byte) {
|
|
19321
|
-
return (byte & 192) === 128;
|
|
19322
|
-
}
|
|
19323
|
-
var KernelTty = class {
|
|
19324
|
-
termios = defaultTermios();
|
|
19325
|
-
cols = 80;
|
|
19326
|
-
rows = 24;
|
|
19327
|
-
readable = [];
|
|
19328
|
-
line = [];
|
|
19329
|
-
waiters = [];
|
|
19330
|
-
hungUp = false;
|
|
19331
|
-
name;
|
|
19332
|
-
owner;
|
|
19333
|
-
onRead;
|
|
19334
|
-
readers = 0;
|
|
19335
|
-
screen;
|
|
19336
|
-
signal;
|
|
19337
|
-
constructor(screen, signal) {
|
|
19338
|
-
this.screen = screen;
|
|
19339
|
-
this.signal = signal;
|
|
19340
|
-
}
|
|
19341
|
-
receive(bytes) {
|
|
19342
|
-
for (const byte of bytes) this.key(byte);
|
|
19343
|
-
this.wake();
|
|
19344
|
-
}
|
|
19345
|
-
resize(cols, rows) {
|
|
19346
|
-
if (cols === this.cols && rows === this.rows) return;
|
|
19347
|
-
this.setSize(cols, rows);
|
|
19348
|
-
this.signal(SIG.WINCH);
|
|
19349
|
-
}
|
|
19350
|
-
hangup() {
|
|
19351
|
-
this.hungUp = true;
|
|
19352
|
-
this.wake();
|
|
19353
|
-
}
|
|
19354
|
-
signalHangup() {
|
|
19355
|
-
this.signal(SIG.HUP);
|
|
19356
|
-
}
|
|
19357
|
-
setSize(cols, rows) {
|
|
19358
|
-
this.cols = cols;
|
|
19359
|
-
this.rows = rows;
|
|
19360
|
-
}
|
|
19361
|
-
tcgets() {
|
|
19362
|
-
return { ...this.termios, c_cc: [...this.termios.c_cc] };
|
|
19363
|
-
}
|
|
19364
|
-
tcsets(termios, flush = false) {
|
|
19365
|
-
const wasCanonical = this.canonical;
|
|
19366
|
-
this.termios = { ...termios, c_cc: [...termios.c_cc] };
|
|
19367
|
-
if (flush) {
|
|
19368
|
-
this.line = [];
|
|
19369
|
-
this.readable = [];
|
|
19370
|
-
}
|
|
19371
|
-
if (wasCanonical && !this.canonical && this.line.length > 0) {
|
|
19372
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
19373
|
-
this.line = [];
|
|
19374
|
-
this.wake();
|
|
19375
|
-
}
|
|
19376
|
-
}
|
|
19377
|
-
winsize() {
|
|
19378
|
-
return [this.rows, this.cols];
|
|
19379
|
-
}
|
|
19380
|
-
file() {
|
|
19381
|
-
return new OpenFile({
|
|
19382
|
-
read: (max, signal) => this.read(max, signal),
|
|
19383
|
-
write: async (bytes) => {
|
|
19384
|
-
if (this.hungUp) throw new KernelError("EIO");
|
|
19385
|
-
this.output(bytes);
|
|
19386
|
-
return bytes.length;
|
|
19387
|
-
},
|
|
19388
|
-
poll: () => ({
|
|
19389
|
-
readable: this.readable.length > 0 || this.hungUp,
|
|
19390
|
-
writable: true,
|
|
19391
|
-
hangup: this.hungUp,
|
|
19392
|
-
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
19393
|
-
}),
|
|
19394
|
-
changed: (signal) => this.changed(signal),
|
|
19395
|
-
close: () => {
|
|
19396
|
-
},
|
|
19397
|
-
tty: this
|
|
19398
|
-
});
|
|
19399
|
-
}
|
|
19400
|
-
get canonical() {
|
|
19401
|
-
return (this.termios.c_lflag & ICANON) !== 0;
|
|
19402
|
-
}
|
|
19403
|
-
cc(slot2) {
|
|
19404
|
-
return this.termios.c_cc[slot2] ?? 0;
|
|
19405
|
-
}
|
|
19406
|
-
key(input) {
|
|
19407
|
-
const { c_iflag, c_lflag } = this.termios;
|
|
19408
|
-
const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
|
|
19409
|
-
if (c_lflag & ISIG && this.signalKey(byte)) return;
|
|
19410
|
-
if (!this.canonical) {
|
|
19411
|
-
this.readable.push(Uint8Array.of(byte));
|
|
19412
|
-
if (c_lflag & ECHO) this.echo(byte);
|
|
19413
|
-
return;
|
|
19414
|
-
}
|
|
19415
|
-
this.edit(byte);
|
|
19416
|
-
}
|
|
19417
|
-
signalKey(byte) {
|
|
19418
|
-
const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
|
|
19419
|
-
if (!sig || byte === 0) return false;
|
|
19420
|
-
if (this.termios.c_lflag & ECHO) this.echo(byte);
|
|
19421
|
-
if (!(this.termios.c_lflag & NOFLSH)) {
|
|
19422
|
-
this.line = [];
|
|
19423
|
-
this.readable = [];
|
|
19424
|
-
}
|
|
19425
|
-
this.signal(sig);
|
|
19426
|
-
return true;
|
|
19427
|
-
}
|
|
19428
|
-
edit(byte) {
|
|
19429
|
-
const echo = (this.termios.c_lflag & ECHO) !== 0;
|
|
19430
|
-
if (byte === this.cc(VERASE) || byte === 8) {
|
|
19431
|
-
this.rubOut(this.lastCharLength());
|
|
19432
|
-
} else if (byte === this.cc(VKILL)) {
|
|
19433
|
-
this.rubOut(this.line.length);
|
|
19434
|
-
} else if (byte === this.cc(VWERASE)) {
|
|
19435
|
-
this.rubOut(this.lastWordLength());
|
|
19436
|
-
} else if (byte === this.cc(VEOF)) {
|
|
19437
|
-
this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
|
|
19438
|
-
this.line = [];
|
|
19439
|
-
} else if (byte === 10) {
|
|
19440
|
-
this.line.push(byte);
|
|
19441
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
19442
|
-
this.line = [];
|
|
19443
|
-
if (echo) this.output(encoder7.encode("\n"));
|
|
19444
|
-
} else {
|
|
19445
|
-
this.line.push(byte);
|
|
19446
|
-
if (echo) this.echo(byte);
|
|
19447
|
-
}
|
|
19448
|
-
}
|
|
19449
|
-
lastWordLength() {
|
|
19450
|
-
let end = this.line.length;
|
|
19451
|
-
while (end > 0 && this.line[end - 1] === 32) end--;
|
|
19452
|
-
while (end > 0 && this.line[end - 1] !== 32) end--;
|
|
19453
|
-
return this.line.length - end;
|
|
19454
|
-
}
|
|
19455
|
-
lastCharLength() {
|
|
19456
|
-
const { line } = this;
|
|
19457
|
-
let n = Math.min(1, line.length);
|
|
19458
|
-
if (this.termios.c_iflag & IUTF8) {
|
|
19459
|
-
while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
|
|
19460
|
-
}
|
|
19461
|
-
return n;
|
|
19462
|
-
}
|
|
19463
|
-
rubOut(n) {
|
|
19464
|
-
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
19465
|
-
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
19466
|
-
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
19467
|
-
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
19468
|
-
this.output(encoder7.encode("\b \b".repeat(cells)), false);
|
|
19469
|
-
}
|
|
19470
|
-
}
|
|
19471
|
-
echo(byte) {
|
|
19472
|
-
if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
|
|
19473
|
-
this.output(Uint8Array.of(94, byte + 64), false);
|
|
19474
|
-
return;
|
|
19475
|
-
}
|
|
19476
|
-
this.output(Uint8Array.of(byte));
|
|
19477
|
-
}
|
|
19478
|
-
output(bytes, translate2 = true) {
|
|
19479
|
-
const { c_oflag } = this.termios;
|
|
19480
|
-
if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
|
|
19481
|
-
this.screen.write(bytes);
|
|
19482
|
-
return;
|
|
19483
|
-
}
|
|
19484
|
-
const out = [];
|
|
19485
|
-
for (const byte of bytes) {
|
|
19486
|
-
if (byte === 10) out.push(13);
|
|
19487
|
-
out.push(byte);
|
|
19488
|
-
}
|
|
19489
|
-
this.screen.write(Uint8Array.from(out));
|
|
19490
|
-
}
|
|
19491
|
-
get reading() {
|
|
19492
|
-
return this.readers > 0;
|
|
19493
|
-
}
|
|
19494
|
-
async read(max, signal) {
|
|
19495
|
-
this.onRead?.();
|
|
19496
|
-
this.readers++;
|
|
19497
|
-
try {
|
|
19498
|
-
return await this.take(max, signal);
|
|
19499
|
-
} finally {
|
|
19500
|
-
this.readers--;
|
|
19501
|
-
}
|
|
19502
|
-
}
|
|
19503
|
-
async take(max, signal) {
|
|
19504
|
-
while (this.readable.length === 0) {
|
|
19505
|
-
if (this.hungUp) return new Uint8Array(0);
|
|
19506
|
-
await this.changed(signal);
|
|
19507
|
-
}
|
|
19508
|
-
const head = this.readable[0];
|
|
19509
|
-
if (head === null) {
|
|
19510
|
-
this.readable.shift();
|
|
19511
|
-
return new Uint8Array(0);
|
|
19512
|
-
}
|
|
19513
|
-
if (head === void 0) return new Uint8Array(0);
|
|
19514
|
-
const chunks2 = [];
|
|
19515
|
-
let n = 0;
|
|
19516
|
-
while (this.readable.length > 0 && n < max) {
|
|
19517
|
-
const next3 = this.readable[0];
|
|
19518
|
-
if (next3 === null || next3 === void 0) break;
|
|
19519
|
-
const take = next3.subarray(0, max - n);
|
|
19520
|
-
chunks2.push(take);
|
|
19521
|
-
n += take.length;
|
|
19522
|
-
if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
|
|
19523
|
-
else this.readable.shift();
|
|
19524
|
-
if (this.canonical) break;
|
|
19525
|
-
}
|
|
19526
|
-
const out = new Uint8Array(n);
|
|
19527
|
-
let at = 0;
|
|
19528
|
-
for (const chunk2 of chunks2) {
|
|
19529
|
-
out.set(chunk2, at);
|
|
19530
|
-
at += chunk2.length;
|
|
19531
|
-
}
|
|
19532
|
-
return out;
|
|
19533
|
-
}
|
|
19534
|
-
changed(signal) {
|
|
19535
|
-
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
19536
|
-
return new Promise((resolve2, reject) => {
|
|
19537
|
-
const onAbort = () => {
|
|
19538
|
-
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
19539
|
-
reject(new KernelError("EINTR"));
|
|
19540
|
-
};
|
|
19541
|
-
const waiter = () => {
|
|
19542
|
-
signal?.removeEventListener("abort", onAbort);
|
|
19543
|
-
resolve2();
|
|
19544
|
-
};
|
|
19545
|
-
this.waiters.push(waiter);
|
|
19546
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19547
|
-
});
|
|
19548
|
-
}
|
|
19549
|
-
wake() {
|
|
19550
|
-
const waiters = this.waiters;
|
|
19551
|
-
this.waiters = [];
|
|
19552
|
-
for (const resolve2 of waiters) resolve2();
|
|
19553
|
-
}
|
|
19554
|
-
};
|
|
19555
|
-
|
|
19556
20549
|
// src/kernel/pty.ts
|
|
19557
20550
|
var PtyPair = class {
|
|
19558
20551
|
slave;
|
|
@@ -19745,6 +20738,7 @@ function ptySyscall(req, ctx) {
|
|
|
19745
20738
|
return done3;
|
|
19746
20739
|
case "pty-ctty": {
|
|
19747
20740
|
const tty = terminalOf(ctx, req.fd);
|
|
20741
|
+
if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
|
|
19748
20742
|
const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
|
|
19749
20743
|
if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done3;
|
|
19750
20744
|
if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
|
|
@@ -20526,10 +21520,10 @@ function basicOf(value2) {
|
|
|
20526
21520
|
function placeholdersIn(text) {
|
|
20527
21521
|
return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
|
|
20528
21522
|
}
|
|
20529
|
-
function scan(
|
|
21523
|
+
function scan(lines3) {
|
|
20530
21524
|
const found = [];
|
|
20531
|
-
for (let i = 1; i <
|
|
20532
|
-
const m = HEADER_LINE.exec(
|
|
21525
|
+
for (let i = 1; i < lines3.length; i++) {
|
|
21526
|
+
const m = HEADER_LINE.exec(lines3[i]);
|
|
20533
21527
|
if (!m) return void 0;
|
|
20534
21528
|
const value2 = m[2];
|
|
20535
21529
|
const basic = basicOf(value2);
|
|
@@ -20552,10 +21546,10 @@ function usable2(values) {
|
|
|
20552
21546
|
}
|
|
20553
21547
|
async function substituteHead(head, ask) {
|
|
20554
21548
|
if (!head.endsWith("\r\n\r\n")) return void 0;
|
|
20555
|
-
const
|
|
20556
|
-
if (
|
|
20557
|
-
const request = REQUEST_LINE2.exec(
|
|
20558
|
-
const found = request ? scan(
|
|
21549
|
+
const lines3 = head.slice(0, -4).split("\r\n");
|
|
21550
|
+
if (lines3.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
|
|
21551
|
+
const request = REQUEST_LINE2.exec(lines3[0]);
|
|
21552
|
+
const found = request ? scan(lines3) : void 0;
|
|
20559
21553
|
if (!request || !found || found.length !== 1) return void 0;
|
|
20560
21554
|
const at = found[0];
|
|
20561
21555
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
@@ -20567,8 +21561,8 @@ async function substituteHead(head, ask) {
|
|
|
20567
21561
|
);
|
|
20568
21562
|
if (Object.keys(values).length === 0) return void 0;
|
|
20569
21563
|
const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
|
|
20570
|
-
const out = [...
|
|
20571
|
-
out[at.line] = `${
|
|
21564
|
+
const out = [...lines3];
|
|
21565
|
+
out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
|
|
20572
21566
|
return { head: `${out.join("\r\n")}\r
|
|
20573
21567
|
\r
|
|
20574
21568
|
`, values };
|
|
@@ -20891,9 +21885,9 @@ function serverHeader(header5, server) {
|
|
|
20891
21885
|
return server.plus && server.binding ? END_POINT : "n,,";
|
|
20892
21886
|
}
|
|
20893
21887
|
function rewritten(first, header5) {
|
|
20894
|
-
const
|
|
20895
|
-
if (!
|
|
20896
|
-
return { mechanism: header5 === END_POINT ? PLUS : PLAIN2, data: `${header5}${
|
|
21888
|
+
const parsed3 = clientFirstOf(first.data);
|
|
21889
|
+
if (!parsed3 || parsed3.header === header5) return first;
|
|
21890
|
+
return { mechanism: header5 === END_POINT ? PLUS : PLAIN2, data: `${header5}${parsed3.bare}` };
|
|
20897
21891
|
}
|
|
20898
21892
|
var PgClient = class {
|
|
20899
21893
|
clientFirst;
|
|
@@ -20913,10 +21907,10 @@ var PgClient = class {
|
|
|
20913
21907
|
}
|
|
20914
21908
|
if (server.sasl && this.clientFirst === void 0) {
|
|
20915
21909
|
const first = initial(message3);
|
|
20916
|
-
const
|
|
20917
|
-
if (!first || !
|
|
21910
|
+
const parsed3 = first && clientFirstOf(first.data);
|
|
21911
|
+
if (!first || !parsed3) return { bytes: message3, done: true };
|
|
20918
21912
|
this.clientFirst = first.data;
|
|
20919
|
-
this.header = serverHeader(
|
|
21913
|
+
this.header = serverHeader(parsed3.header, server);
|
|
20920
21914
|
const sent = rewritten(first, this.header);
|
|
20921
21915
|
if (sent === first) return { bytes: message3, done: false };
|
|
20922
21916
|
return {
|
|
@@ -21081,7 +22075,7 @@ function headEnd2(bytes) {
|
|
|
21081
22075
|
}
|
|
21082
22076
|
return -1;
|
|
21083
22077
|
}
|
|
21084
|
-
function
|
|
22078
|
+
function modeOf3(start2) {
|
|
21085
22079
|
const sniffs = [
|
|
21086
22080
|
["http1", sniffHttp1(start2)],
|
|
21087
22081
|
["smtp", sniffSmtp(start2)],
|
|
@@ -21190,7 +22184,7 @@ var SessionWriter = class {
|
|
|
21190
22184
|
joined5.set(bytes, this.buffer.length);
|
|
21191
22185
|
this.buffer.fill(0);
|
|
21192
22186
|
this.buffer = joined5;
|
|
21193
|
-
if (this.mode === "sniff") this.mode =
|
|
22187
|
+
if (this.mode === "sniff") this.mode = modeOf3(latin12(joined5.subarray(0, 8))) ?? "sniff";
|
|
21194
22188
|
if (this.mode === "pg" && !this.sawPg) {
|
|
21195
22189
|
this.sawPg = true;
|
|
21196
22190
|
if (!this.s.verified) void this.proof("pg");
|
|
@@ -21677,12 +22671,12 @@ function secure(prepared, seal) {
|
|
|
21677
22671
|
prepared.own?.release();
|
|
21678
22672
|
}
|
|
21679
22673
|
};
|
|
21680
|
-
const
|
|
22674
|
+
const abandon2 = () => {
|
|
21681
22675
|
if (ran) return;
|
|
21682
22676
|
prepared.options.session.free();
|
|
21683
22677
|
prepared.own?.release();
|
|
21684
22678
|
};
|
|
21685
|
-
return { make, abandon };
|
|
22679
|
+
return { make, abandon: abandon2 };
|
|
21686
22680
|
}
|
|
21687
22681
|
function supportOf(net) {
|
|
21688
22682
|
const support = net.tlsClient;
|
|
@@ -21699,19 +22693,19 @@ async function uplinkAddress(proc, host) {
|
|
|
21699
22693
|
async function connectSecure(proc, socket, host, port, req) {
|
|
21700
22694
|
const servername = serverName(req.servername || host);
|
|
21701
22695
|
if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
|
|
21702
|
-
const
|
|
21703
|
-
const route2 = proc.net.uplinkFor(
|
|
22696
|
+
const address2 = await uplinkAddress(proc, host);
|
|
22697
|
+
const route2 = proc.net.uplinkFor(address2);
|
|
21704
22698
|
if (!route2)
|
|
21705
22699
|
throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
|
|
21706
22700
|
const support = supportOf(proc.net);
|
|
21707
22701
|
let info;
|
|
21708
|
-
const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
|
|
22702
|
+
const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
|
|
21709
22703
|
try {
|
|
21710
|
-
await socket.connectSecure({ family: "inet", host:
|
|
22704
|
+
await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
|
|
21711
22705
|
info = await conn.upgrade(make);
|
|
21712
22706
|
});
|
|
21713
22707
|
} finally {
|
|
21714
|
-
|
|
22708
|
+
abandon2();
|
|
21715
22709
|
}
|
|
21716
22710
|
return sessionOf(info, servername);
|
|
21717
22711
|
}
|
|
@@ -21744,14 +22738,14 @@ async function upgradeSecure(proc, socket, req) {
|
|
|
21744
22738
|
const host = req.servername || (peer?.family === "inet" ? peer.host : "");
|
|
21745
22739
|
const servername = serverName(host);
|
|
21746
22740
|
const port = peer?.family === "inet" ? peer.port : 0;
|
|
21747
|
-
const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
22741
|
+
const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
21748
22742
|
proc,
|
|
21749
22743
|
port
|
|
21750
22744
|
});
|
|
21751
22745
|
try {
|
|
21752
22746
|
return sessionOf(await socket.upgradeSecure(make), servername);
|
|
21753
22747
|
} finally {
|
|
21754
|
-
|
|
22748
|
+
abandon2();
|
|
21755
22749
|
}
|
|
21756
22750
|
}
|
|
21757
22751
|
|
|
@@ -23675,6 +24669,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
23675
24669
|
"fd-open-clipboard",
|
|
23676
24670
|
"fd-clipboard-abort",
|
|
23677
24671
|
"fd-open-embedder",
|
|
24672
|
+
"fd-dev-nodes",
|
|
24673
|
+
"fd-open-dev",
|
|
24674
|
+
"fd-dev-ioctl",
|
|
24675
|
+
"fd-dev-meta",
|
|
23678
24676
|
"tty-get",
|
|
23679
24677
|
"tty-set",
|
|
23680
24678
|
"tty-winsz",
|
|
@@ -24051,7 +25049,7 @@ var WasmProcess = class {
|
|
|
24051
25049
|
async dispatch(req, cancelled) {
|
|
24052
25050
|
try {
|
|
24053
25051
|
if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
|
|
24054
|
-
if (isTtySyscall(req)) return this.ttySyscall(req);
|
|
25052
|
+
if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
|
|
24055
25053
|
if (isJobSyscall(req)) return this.jobSyscall(req);
|
|
24056
25054
|
if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
|
|
24057
25055
|
if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
|
|
@@ -24115,7 +25113,7 @@ var WasmProcess = class {
|
|
|
24115
25113
|
return owner;
|
|
24116
25114
|
}
|
|
24117
25115
|
pathKeys = /* @__PURE__ */ new Map();
|
|
24118
|
-
async lockPath(target,
|
|
25116
|
+
async lockPath(target, want2) {
|
|
24119
25117
|
if (target.fd !== void 0) {
|
|
24120
25118
|
const st = await this.fds.get(target.fd).file.stat?.();
|
|
24121
25119
|
if (!st) throw new KernelError("EBADF");
|
|
@@ -24125,9 +25123,9 @@ var WasmProcess = class {
|
|
|
24125
25123
|
if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
|
|
24126
25124
|
const path = target.path;
|
|
24127
25125
|
const g = this.guarded();
|
|
24128
|
-
const checked = `${
|
|
24129
|
-
if (g &&
|
|
24130
|
-
await openable(g, "lock", path,
|
|
25126
|
+
const checked = `${want2}:${path}`;
|
|
25127
|
+
if (g && want2 !== 0 && !this.lockChecked.has(checked)) {
|
|
25128
|
+
await openable(g, "lock", path, want2, false);
|
|
24131
25129
|
this.lockChecked.add(checked);
|
|
24132
25130
|
}
|
|
24133
25131
|
return path;
|
|
@@ -24140,8 +25138,8 @@ var WasmProcess = class {
|
|
|
24140
25138
|
const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
|
|
24141
25139
|
if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
|
|
24142
25140
|
if (access2 && !access2.stat) return objectKey(access2);
|
|
24143
|
-
const
|
|
24144
|
-
const path = await this.lockPath(target,
|
|
25141
|
+
const want2 = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
|
|
25142
|
+
const path = await this.lockPath(target, want2);
|
|
24145
25143
|
if (known !== void 0) return known;
|
|
24146
25144
|
const st = await this.options.fs.stat?.(path).catch((err) => {
|
|
24147
25145
|
if (!file) throw err;
|
|
@@ -24227,7 +25225,7 @@ var WasmProcess = class {
|
|
|
24227
25225
|
const file = this.fds.get(req.fd).file;
|
|
24228
25226
|
const read = req.peek ? file.peek : file.read;
|
|
24229
25227
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
24230
|
-
if (file.tty) this.checkForeground(file.tty);
|
|
25228
|
+
if (file.tty && !file.device) this.checkForeground(file.tty);
|
|
24231
25229
|
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
24232
25230
|
if (file.embedder) return this.readEmbedder(file, req, cancelled);
|
|
24233
25231
|
if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
|
|
@@ -24324,6 +25322,46 @@ var WasmProcess = class {
|
|
|
24324
25322
|
throw err;
|
|
24325
25323
|
});
|
|
24326
25324
|
}
|
|
25325
|
+
async openDev(path, flags2, cancelled) {
|
|
25326
|
+
const devices = this.options.devices;
|
|
25327
|
+
if (!devices) throw new KernelError("ENOENT");
|
|
25328
|
+
const shown2 = await this.options.identity?.();
|
|
25329
|
+
const tags = this.options.embedder?.tagsOf(this.pid);
|
|
25330
|
+
const stamp = {
|
|
25331
|
+
pid: shown2?.pid ?? this.pid,
|
|
25332
|
+
uid: this.cred.euid,
|
|
25333
|
+
gid: this.cred.egid,
|
|
25334
|
+
groups: [...this.cred.groups],
|
|
25335
|
+
users: devices.users,
|
|
25336
|
+
...tags?.length ? { tag: tags[0], tags: [...tags] } : {}
|
|
25337
|
+
};
|
|
25338
|
+
const guard2 = this.guarded();
|
|
25339
|
+
const file = await devices.open(path, flags2, {
|
|
25340
|
+
stamp,
|
|
25341
|
+
...guard2 ? { guard: guard2 } : {},
|
|
25342
|
+
signal: this.blockingSignal(cancelled)
|
|
25343
|
+
});
|
|
25344
|
+
return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
|
|
25345
|
+
}
|
|
25346
|
+
devMeta(path, change) {
|
|
25347
|
+
const devices = this.options.devices;
|
|
25348
|
+
if (!devices) throw new KernelError("ENOENT");
|
|
25349
|
+
devices.meta(path, change, this.guarded());
|
|
25350
|
+
return { ok: true, kind: "void" };
|
|
25351
|
+
}
|
|
25352
|
+
async devIoctl(req, cancelled) {
|
|
25353
|
+
const file = this.fds.get(req.fd).file;
|
|
25354
|
+
if (!file.device) {
|
|
25355
|
+
if (req.request >>> 0 === 21531) return pollFile(file).nread ?? 0;
|
|
25356
|
+
throw new KernelError("ENOTTY");
|
|
25357
|
+
}
|
|
25358
|
+
const result = await file.device.ioctl(
|
|
25359
|
+
req.request >>> 0,
|
|
25360
|
+
req.arg,
|
|
25361
|
+
this.blockingSignal(cancelled)
|
|
25362
|
+
);
|
|
25363
|
+
return result ?? null;
|
|
25364
|
+
}
|
|
24327
25365
|
async openEmbedder(flags2) {
|
|
24328
25366
|
const broker = this.options.embedder;
|
|
24329
25367
|
if (!broker) throw new KernelError("ENOENT");
|
|
@@ -24411,11 +25449,11 @@ var WasmProcess = class {
|
|
|
24411
25449
|
} else if (req.create) {
|
|
24412
25450
|
await nodes.onEntry(path, async () => {
|
|
24413
25451
|
const marks = this.options.owners || plain3 !== void 0;
|
|
24414
|
-
const
|
|
25452
|
+
const fresh4 = marks && !await present3(fs, path, true);
|
|
24415
25453
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
|
|
24416
25454
|
await this.fds.get(fd).file.stat?.();
|
|
24417
|
-
if (
|
|
24418
|
-
else if (
|
|
25455
|
+
if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
|
|
25456
|
+
else if (fresh4) {
|
|
24419
25457
|
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
24420
25458
|
}
|
|
24421
25459
|
});
|
|
@@ -24514,8 +25552,8 @@ var WasmProcess = class {
|
|
|
24514
25552
|
if (req.exclusive) return inParent(g, "open", req.path);
|
|
24515
25553
|
if (req.created && guard2.created?.take(req.path)) return;
|
|
24516
25554
|
const access2 = req.flags & WRITE_ACCESS;
|
|
24517
|
-
const
|
|
24518
|
-
await openable(g, "open", req.path,
|
|
25555
|
+
const want2 = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
|
|
25556
|
+
await openable(g, "open", req.path, want2, req.create === true);
|
|
24519
25557
|
}
|
|
24520
25558
|
openTty(name2) {
|
|
24521
25559
|
const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
|
|
@@ -24579,7 +25617,7 @@ var WasmProcess = class {
|
|
|
24579
25617
|
}
|
|
24580
25618
|
terminalByName(name2) {
|
|
24581
25619
|
const n = ptsNumber(name2);
|
|
24582
|
-
const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
|
|
25620
|
+
const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) ?? this.options.devices?.openTty(name2) : this.options.ptys?.get(n).slave;
|
|
24583
25621
|
if (!tty) throw new KernelError("ENOENT");
|
|
24584
25622
|
return tty;
|
|
24585
25623
|
}
|
|
@@ -24701,6 +25739,16 @@ var WasmProcess = class {
|
|
|
24701
25739
|
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
24702
25740
|
case "fd-open-embedder":
|
|
24703
25741
|
return { ok: true, kind: "json", json: await this.openEmbedder(req.flags) };
|
|
25742
|
+
case "fd-dev-nodes":
|
|
25743
|
+
return { ok: true, kind: "json", json: this.options.devices?.list() ?? [] };
|
|
25744
|
+
case "fd-open-dev":
|
|
25745
|
+
return { ok: true, kind: "json", json: await this.openDev(req.path, req.flags, cancelled) };
|
|
25746
|
+
case "fd-dev-ioctl":
|
|
25747
|
+
return { ok: true, kind: "json", json: await this.devIoctl(req, cancelled) };
|
|
25748
|
+
case "fd-dev-meta": {
|
|
25749
|
+
const { op: _, path, ...change } = req;
|
|
25750
|
+
return this.devMeta(path, change);
|
|
25751
|
+
}
|
|
24704
25752
|
case "fd-clipboard-abort": {
|
|
24705
25753
|
const file = this.fds.get(req.fd);
|
|
24706
25754
|
file.file.clipboard?.abort();
|
|
@@ -24745,6 +25793,11 @@ var WasmProcess = class {
|
|
|
24745
25793
|
return { ok: true, kind: "void" };
|
|
24746
25794
|
}
|
|
24747
25795
|
const file = this.fds.get(req.fd).file;
|
|
25796
|
+
const device = file.device;
|
|
25797
|
+
if (device && (req.op === "fd-chmod" || req.op === "fd-chown" || req.op === "fd-utimes")) {
|
|
25798
|
+
const { op: _, fd: __, ...change } = req;
|
|
25799
|
+
return this.devMeta(device.path, change);
|
|
25800
|
+
}
|
|
24748
25801
|
if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
|
|
24749
25802
|
switch (req.op) {
|
|
24750
25803
|
case "fd-pread":
|
|
@@ -24914,14 +25967,19 @@ var WasmProcess = class {
|
|
|
24914
25967
|
...file.fifo !== void 0 ? { fifo: file.fifo } : {}
|
|
24915
25968
|
};
|
|
24916
25969
|
}
|
|
24917
|
-
ttySyscall(req) {
|
|
25970
|
+
async ttySyscall(req, cancelled) {
|
|
24918
25971
|
const tty = this.tty(req.fd);
|
|
24919
25972
|
const jobs = this.options.jobs;
|
|
25973
|
+
const device = this.fds.get(req.fd).file.device;
|
|
25974
|
+
if (device && req.op.startsWith("tty-pgrp")) throw new KernelError("ENOTTY");
|
|
24920
25975
|
switch (req.op) {
|
|
24921
25976
|
case "tty-get":
|
|
24922
25977
|
return { ok: true, kind: "json", json: tty.tcgets() };
|
|
24923
25978
|
case "tty-set":
|
|
24924
|
-
|
|
25979
|
+
if (device) {
|
|
25980
|
+
const how = { flush: req.flush === true, drain: req.drain === true };
|
|
25981
|
+
await device.setTermios(req.termios, how, this.blockingSignal(cancelled));
|
|
25982
|
+
} else tty.tcsets(req.termios, req.flush === true);
|
|
24925
25983
|
return { ok: true, kind: "void" };
|
|
24926
25984
|
case "tty-winsz": {
|
|
24927
25985
|
const size = tty.winsize();
|
|
@@ -25802,6 +26860,7 @@ function processOptions(opts, header5, hooks, created4) {
|
|
|
25802
26860
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
25803
26861
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
25804
26862
|
...embedderOptions(opts),
|
|
26863
|
+
devices: opts.devices,
|
|
25805
26864
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
25806
26865
|
...opts.source ? { source: opts.source } : {},
|
|
25807
26866
|
noLinks: opts.noLinks,
|
|
@@ -26556,15 +27615,15 @@ var RemoteWatch = class {
|
|
|
26556
27615
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
26557
27616
|
}
|
|
26558
27617
|
hold(add, drop, wait2) {
|
|
26559
|
-
const
|
|
27618
|
+
const fresh4 = add.filter(
|
|
26560
27619
|
([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
|
|
26561
27620
|
);
|
|
26562
27621
|
const gone = drop.filter((path) => this.myHolds.has(path));
|
|
26563
27622
|
for (const path of gone) this.myHolds.delete(path);
|
|
26564
|
-
for (const [path, fid] of
|
|
26565
|
-
if (
|
|
27623
|
+
for (const [path, fid] of fresh4) this.myHolds.set(path, fid);
|
|
27624
|
+
if (fresh4.length === 0 && gone.length === 0 || this.peers.size === 0)
|
|
26566
27625
|
return Promise.resolve();
|
|
26567
|
-
const seq2 = this.sendHolds(
|
|
27626
|
+
const seq2 = this.sendHolds(fresh4, gone);
|
|
26568
27627
|
if (!wait2) return Promise.resolve();
|
|
26569
27628
|
const heard = new Set(
|
|
26570
27629
|
[...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
|
|
@@ -26988,14 +28047,14 @@ var LockService = class {
|
|
|
26988
28047
|
);
|
|
26989
28048
|
});
|
|
26990
28049
|
}
|
|
26991
|
-
async holder(key, owner,
|
|
28050
|
+
async holder(key, owner, want2) {
|
|
26992
28051
|
return await this.ask({
|
|
26993
28052
|
op: "holder",
|
|
26994
28053
|
key,
|
|
26995
28054
|
owner,
|
|
26996
|
-
kind:
|
|
26997
|
-
start:
|
|
26998
|
-
end:
|
|
28055
|
+
kind: want2.kind,
|
|
28056
|
+
start: want2.start,
|
|
28057
|
+
end: want2.end
|
|
26999
28058
|
}) ?? void 0;
|
|
27000
28059
|
}
|
|
27001
28060
|
async set(key, owner, kind, start2, end) {
|
|
@@ -27274,9 +28333,9 @@ function local(name2) {
|
|
|
27274
28333
|
function literal2(name2) {
|
|
27275
28334
|
return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
|
|
27276
28335
|
}
|
|
27277
|
-
function addressData(
|
|
27278
|
-
if (type === TYPE_A) return ipv4Bytes(
|
|
27279
|
-
const groups = type === TYPE_AAAA ? ipv6Groups(
|
|
28336
|
+
function addressData(address2, type) {
|
|
28337
|
+
if (type === TYPE_A) return ipv4Bytes(address2);
|
|
28338
|
+
const groups = type === TYPE_AAAA ? ipv6Groups(address2) : void 0;
|
|
27280
28339
|
return groups?.flatMap((g) => [g >> 8, g & 255]);
|
|
27281
28340
|
}
|
|
27282
28341
|
var DnsStub = class {
|
|
@@ -27289,10 +28348,10 @@ var DnsStub = class {
|
|
|
27289
28348
|
this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
|
|
27290
28349
|
}
|
|
27291
28350
|
async answer(query, udp) {
|
|
27292
|
-
const
|
|
27293
|
-
if (!
|
|
27294
|
-
if (!
|
|
27295
|
-
const q =
|
|
28351
|
+
const parsed3 = parseQuery(query);
|
|
28352
|
+
if (!parsed3) return void 0;
|
|
28353
|
+
if (!parsed3.ok) return errorReply2(parsed3.id, parsed3.flags, parsed3.rcode);
|
|
28354
|
+
const q = parsed3.question;
|
|
27296
28355
|
const reply = await this.reply(query, q).catch(
|
|
27297
28356
|
() => answerReply(query, q, SERVFAIL, [], false)
|
|
27298
28357
|
);
|
|
@@ -27301,8 +28360,8 @@ var DnsStub = class {
|
|
|
27301
28360
|
async reply(query, q) {
|
|
27302
28361
|
if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
|
|
27303
28362
|
if (q.type === TYPE_PTR) {
|
|
27304
|
-
const
|
|
27305
|
-
const name2 =
|
|
28363
|
+
const address2 = reverseAddress(q.name);
|
|
28364
|
+
const name2 = address2 !== void 0 ? await this.reverse(address2) : void 0;
|
|
27306
28365
|
if (name2 !== void 0)
|
|
27307
28366
|
return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
|
|
27308
28367
|
return this.forward(query, q);
|
|
@@ -27332,8 +28391,8 @@ var DnsStub = class {
|
|
|
27332
28391
|
known(name2) {
|
|
27333
28392
|
return this.resolver.known(name2).catch(() => false);
|
|
27334
28393
|
}
|
|
27335
|
-
reverse(
|
|
27336
|
-
return this.resolver.reverse(
|
|
28394
|
+
reverse(address2) {
|
|
28395
|
+
return this.resolver.reverse(address2).catch(() => void 0);
|
|
27337
28396
|
}
|
|
27338
28397
|
async forward(query, q) {
|
|
27339
28398
|
if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
|
|
@@ -27512,7 +28571,7 @@ function honors(traits, redirect) {
|
|
|
27512
28571
|
const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
|
|
27513
28572
|
return redirect === "manual" && raw2;
|
|
27514
28573
|
}
|
|
27515
|
-
function
|
|
28574
|
+
function refused4(message3) {
|
|
27516
28575
|
return new TransportError(message3, 502, void 0, "transport");
|
|
27517
28576
|
}
|
|
27518
28577
|
var RemoteTransport = class {
|
|
@@ -27603,7 +28662,7 @@ var RemoteTransport = class {
|
|
|
27603
28662
|
const { signal, redirect } = request;
|
|
27604
28663
|
signal.throwIfAborted();
|
|
27605
28664
|
if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
|
|
27606
|
-
throw
|
|
28665
|
+
throw refused4(`this transport cannot honor redirect: ${redirect}`);
|
|
27607
28666
|
}
|
|
27608
28667
|
let finished = false;
|
|
27609
28668
|
const abort = () => {
|
|
@@ -27654,7 +28713,7 @@ var RemoteTransport = class {
|
|
|
27654
28713
|
};
|
|
27655
28714
|
if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
|
|
27656
28715
|
await cancel();
|
|
27657
|
-
throw
|
|
28716
|
+
throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
|
|
27658
28717
|
}
|
|
27659
28718
|
return {
|
|
27660
28719
|
...url !== void 0 ? { url } : {},
|
|
@@ -28174,6 +29233,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
|
|
|
28174
29233
|
"proc-list",
|
|
28175
29234
|
"proc-identity",
|
|
28176
29235
|
"fd-info",
|
|
29236
|
+
"fd-dev-nodes",
|
|
28177
29237
|
"sig-mask",
|
|
28178
29238
|
"sig-inherited",
|
|
28179
29239
|
"proc-setpgid",
|
|
@@ -28270,8 +29330,8 @@ var WasmMemories = class {
|
|
|
28270
29330
|
const learned = this.learned;
|
|
28271
29331
|
if (!learned) return this.base;
|
|
28272
29332
|
const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
|
|
28273
|
-
const
|
|
28274
|
-
if (
|
|
29333
|
+
const raised2 = learned.ceiling + steps * DECAY_STEP;
|
|
29334
|
+
if (raised2 < (this.base ?? DEFAULT_CEILING)) return raised2;
|
|
28275
29335
|
this.learned = void 0;
|
|
28276
29336
|
return this.base;
|
|
28277
29337
|
}
|
|
@@ -28414,9 +29474,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
28414
29474
|
let open = false;
|
|
28415
29475
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
28416
29476
|
async function hello(message3) {
|
|
28417
|
-
const
|
|
28418
|
-
if (
|
|
28419
|
-
reply({ hello: { protocol: DRIVER_PROTOCOL, error:
|
|
29477
|
+
const refused5 = driverVersionError(message3.protocol);
|
|
29478
|
+
if (refused5) {
|
|
29479
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused5, errno: "EPROTO" } });
|
|
28420
29480
|
return;
|
|
28421
29481
|
}
|
|
28422
29482
|
try {
|
|
@@ -28469,8 +29529,8 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
28469
29529
|
function noticeOf(data) {
|
|
28470
29530
|
if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
|
|
28471
29531
|
const notice = data ?? {};
|
|
28472
|
-
const
|
|
28473
|
-
return { paths:
|
|
29532
|
+
const strings3 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
29533
|
+
return { paths: strings3(notice.paths), trees: strings3(notice.trees), outside: true };
|
|
28474
29534
|
}
|
|
28475
29535
|
|
|
28476
29536
|
// src/fs/opfs.ts
|
|
@@ -28964,7 +30024,7 @@ var OpfsFs = class {
|
|
|
28964
30024
|
const parent = entries.at(-1);
|
|
28965
30025
|
const setgid = ((parent?.mode ?? 0) & S_ISGID4) !== 0;
|
|
28966
30026
|
const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
|
|
28967
|
-
const
|
|
30027
|
+
const want2 = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
|
|
28968
30028
|
let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
|
|
28969
30029
|
if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
|
|
28970
30030
|
const fields = {};
|
|
@@ -28972,7 +30032,7 @@ var OpfsFs = class {
|
|
|
28972
30032
|
fields.mode = mode;
|
|
28973
30033
|
}
|
|
28974
30034
|
if (self) fields.heir = void 0;
|
|
28975
|
-
if (ownOf(self) || !sameOwner(
|
|
30035
|
+
if (ownOf(self) || !sameOwner(want2, inherit(entries))) Object.assign(fields, want2);
|
|
28976
30036
|
return Object.keys(fields).length > 0 ? fields : void 0;
|
|
28977
30037
|
}
|
|
28978
30038
|
async stampNew(path, owner, directory) {
|
|
@@ -29209,9 +30269,9 @@ var OpfsFs = class {
|
|
|
29209
30269
|
this.lookups++;
|
|
29210
30270
|
if (split2(path).length === 0) return this.root;
|
|
29211
30271
|
const cached = this.files.get(path);
|
|
29212
|
-
const
|
|
29213
|
-
if (cached && !
|
|
29214
|
-
if (cached &&
|
|
30272
|
+
const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
|
|
30273
|
+
if (cached && !fresh4) this.files.delete(path);
|
|
30274
|
+
if (cached && fresh4) {
|
|
29215
30275
|
if (this.glanced(cached)) return this.remember(path, cached);
|
|
29216
30276
|
try {
|
|
29217
30277
|
await this.glance(cached);
|
|
@@ -29429,8 +30489,8 @@ var OpfsFs = class {
|
|
|
29429
30489
|
glanced(handle) {
|
|
29430
30490
|
if (this.exact > 0) return void 0;
|
|
29431
30491
|
const seen = this.glances.get(handle);
|
|
29432
|
-
const
|
|
29433
|
-
return
|
|
30492
|
+
const fresh4 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
|
|
30493
|
+
return fresh4 ? seen.file : void 0;
|
|
29434
30494
|
}
|
|
29435
30495
|
async glance(handle) {
|
|
29436
30496
|
const at = performance.now();
|
|
@@ -29632,13 +30692,13 @@ var OpfsFs = class {
|
|
|
29632
30692
|
async keptFor(path) {
|
|
29633
30693
|
const at = normalizePath(path);
|
|
29634
30694
|
const cached = this.files.get(at);
|
|
29635
|
-
const
|
|
29636
|
-
const [target, existed] =
|
|
30695
|
+
const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
|
|
30696
|
+
const [target, existed] = fresh4 ? [at, true] : await this.writeTarget(path);
|
|
29637
30697
|
if (!existed) return [void 0, target, false];
|
|
29638
30698
|
this.refuse(target);
|
|
29639
30699
|
const [entry] = await this.meta.get([target]);
|
|
29640
30700
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
29641
|
-
const kept =
|
|
30701
|
+
const kept = fresh4 ? cached : this.files.get(target);
|
|
29642
30702
|
const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
29643
30703
|
return [usable3 ? kept : void 0, target, true];
|
|
29644
30704
|
}
|
|
@@ -31192,7 +32252,7 @@ function unescape(field4) {
|
|
|
31192
32252
|
);
|
|
31193
32253
|
}
|
|
31194
32254
|
function parseFstab(text) {
|
|
31195
|
-
const
|
|
32255
|
+
const lines3 = [];
|
|
31196
32256
|
text.split("\n").forEach((raw2, index) => {
|
|
31197
32257
|
const content = raw2.replace(/#.*$/, "").trim();
|
|
31198
32258
|
if (!content) return;
|
|
@@ -31201,12 +32261,12 @@ function parseFstab(text) {
|
|
|
31201
32261
|
if (options.split(",").includes("noauto")) return;
|
|
31202
32262
|
const spec = { source, target, type, options: {} };
|
|
31203
32263
|
try {
|
|
31204
|
-
|
|
32264
|
+
lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
31205
32265
|
} catch (err) {
|
|
31206
|
-
|
|
32266
|
+
lines3.push({ line: index + 1, spec, invalid: err });
|
|
31207
32267
|
}
|
|
31208
32268
|
});
|
|
31209
|
-
return
|
|
32269
|
+
return lines3;
|
|
31210
32270
|
}
|
|
31211
32271
|
function codeOf6(err) {
|
|
31212
32272
|
const code = err?.code;
|
|
@@ -31224,20 +32284,20 @@ function wait(ms, signal) {
|
|
|
31224
32284
|
signal.addEventListener("abort", done4, { once: true });
|
|
31225
32285
|
});
|
|
31226
32286
|
}
|
|
31227
|
-
async function mountFstab(
|
|
32287
|
+
async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
31228
32288
|
}, settled = () => {
|
|
31229
32289
|
}) {
|
|
31230
32290
|
return Promise.all(
|
|
31231
|
-
|
|
32291
|
+
lines3.map(async (each) => {
|
|
31232
32292
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
31233
32293
|
settled(each, result);
|
|
31234
32294
|
return result;
|
|
31235
32295
|
})
|
|
31236
32296
|
);
|
|
31237
32297
|
}
|
|
31238
|
-
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
31239
|
-
let failed5 =
|
|
31240
|
-
for (let attempt = 0; !
|
|
32298
|
+
async function mountLine({ line, spec, invalid: invalid2 }, mount, signal, retries, discard) {
|
|
32299
|
+
let failed5 = invalid2;
|
|
32300
|
+
for (let attempt = 0; !invalid2 && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
31241
32301
|
try {
|
|
31242
32302
|
const entry = await mount(spec);
|
|
31243
32303
|
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
@@ -31405,14 +32465,14 @@ var RESTORE_AGAIN_MS = 3e4;
|
|
|
31405
32465
|
function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
31406
32466
|
const handles = /* @__PURE__ */ new Map();
|
|
31407
32467
|
let nextFh = 0;
|
|
31408
|
-
const
|
|
32468
|
+
const fresh4 = (remote) => {
|
|
31409
32469
|
if (remote.client.token === remote.token && !remote.client.gone) return remote;
|
|
31410
32470
|
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
31411
32471
|
};
|
|
31412
32472
|
const live2 = (fh) => {
|
|
31413
32473
|
const remote = handles.get(fh);
|
|
31414
32474
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
31415
|
-
return
|
|
32475
|
+
return fresh4(remote);
|
|
31416
32476
|
};
|
|
31417
32477
|
const call = (op, body = {}) => current().call(op, body);
|
|
31418
32478
|
const same2 = (was, mode, seen) => seen?.mode === (mode | 128) && (was.ino !== void 0 && seen.ino !== void 0 ? seen.ino === was.ino : seen.size === was.size && seen.mtime === was.mtime);
|
|
@@ -31494,7 +32554,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
31494
32554
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
31495
32555
|
handles.delete(fh);
|
|
31496
32556
|
try {
|
|
31497
|
-
await
|
|
32557
|
+
await fresh4(remote).client.call("release", { fh: remote.fh }, true);
|
|
31498
32558
|
} finally {
|
|
31499
32559
|
if (remote.restore) await settle(current(), remote.restore);
|
|
31500
32560
|
}
|
|
@@ -31544,7 +32604,7 @@ async function resize(h, path, size) {
|
|
|
31544
32604
|
await h.release(fh);
|
|
31545
32605
|
}
|
|
31546
32606
|
}
|
|
31547
|
-
function
|
|
32607
|
+
function sleep2(ms, signal) {
|
|
31548
32608
|
return new Promise((resolve2) => {
|
|
31549
32609
|
const done4 = () => {
|
|
31550
32610
|
clearTimeout(timer);
|
|
@@ -31572,22 +32632,22 @@ function keepWatching(loop, signal) {
|
|
|
31572
32632
|
const stop = () => session2.abort();
|
|
31573
32633
|
signal.addEventListener("abort", stop);
|
|
31574
32634
|
let timer;
|
|
31575
|
-
const
|
|
32635
|
+
const quiet5 = () => {
|
|
31576
32636
|
clearTimeout(timer);
|
|
31577
32637
|
timer = setTimeout(stop, silenceMs);
|
|
31578
32638
|
};
|
|
31579
32639
|
let opened2 = false;
|
|
31580
32640
|
try {
|
|
31581
32641
|
const client = await loop.connect();
|
|
31582
|
-
|
|
32642
|
+
quiet5();
|
|
31583
32643
|
await client.watch(
|
|
31584
32644
|
session2.signal,
|
|
31585
32645
|
(event) => {
|
|
31586
|
-
|
|
32646
|
+
quiet5();
|
|
31587
32647
|
if (!event.ping) loop.event(event);
|
|
31588
32648
|
},
|
|
31589
32649
|
() => {
|
|
31590
|
-
|
|
32650
|
+
quiet5();
|
|
31591
32651
|
opened2 = true;
|
|
31592
32652
|
up = true;
|
|
31593
32653
|
loop.up(client);
|
|
@@ -31616,7 +32676,7 @@ function keepWatching(loop, signal) {
|
|
|
31616
32676
|
loop.down();
|
|
31617
32677
|
}
|
|
31618
32678
|
ready2.resolve();
|
|
31619
|
-
await
|
|
32679
|
+
await sleep2(wait2, signal);
|
|
31620
32680
|
wait2 = Math.min(wait2 * 2, most);
|
|
31621
32681
|
}
|
|
31622
32682
|
})();
|
|
@@ -31858,7 +32918,7 @@ var PortTransport = class {
|
|
|
31858
32918
|
}).finally(() => this.fail("the folder's watch ended"));
|
|
31859
32919
|
}
|
|
31860
32920
|
};
|
|
31861
|
-
var
|
|
32921
|
+
var errorOf3 = (err) => ({
|
|
31862
32922
|
code: typeof codeOf7(err) === "string" ? codeOf7(err) : "EIO",
|
|
31863
32923
|
message: err?.message ?? String(err)
|
|
31864
32924
|
});
|
|
@@ -31908,7 +32968,7 @@ function serveHostfsPort(port, transport) {
|
|
|
31908
32968
|
);
|
|
31909
32969
|
send2({ id: id2, end: true });
|
|
31910
32970
|
} catch (err) {
|
|
31911
|
-
send2({ id: id2, error:
|
|
32971
|
+
send2({ id: id2, error: errorOf3(err) });
|
|
31912
32972
|
} finally {
|
|
31913
32973
|
watches.delete(id2);
|
|
31914
32974
|
}
|
|
@@ -31936,7 +32996,7 @@ function serveHostfsPort(port, transport) {
|
|
|
31936
32996
|
if (op !== "call") throw fsError2("EINVAL", `unknown hostfs port op ${op}`);
|
|
31937
32997
|
send2({ id: id2, result: await answer2(data.name, data.body) });
|
|
31938
32998
|
} catch (err) {
|
|
31939
|
-
send2({ id: id2, error:
|
|
32999
|
+
send2({ id: id2, error: errorOf3(err) });
|
|
31940
33000
|
}
|
|
31941
33001
|
};
|
|
31942
33002
|
const stop = () => {
|
|
@@ -33158,6 +34218,7 @@ var Launcher = class {
|
|
|
33158
34218
|
scrubs = /* @__PURE__ */ new Map();
|
|
33159
34219
|
clipboard;
|
|
33160
34220
|
embedder;
|
|
34221
|
+
devices;
|
|
33161
34222
|
rootFs;
|
|
33162
34223
|
fileWatches;
|
|
33163
34224
|
nextPid = 1e3;
|
|
@@ -33182,6 +34243,7 @@ var Launcher = class {
|
|
|
33182
34243
|
);
|
|
33183
34244
|
this.embedder = this.embedderBroker(options.embedder);
|
|
33184
34245
|
this.embedder.denied = options.onDenial;
|
|
34246
|
+
this.devices = this.deviceNodes(options.devices);
|
|
33185
34247
|
this.sealed = new SealedBroker(
|
|
33186
34248
|
options.sealed,
|
|
33187
34249
|
this.usersMode,
|
|
@@ -33397,16 +34459,16 @@ var Launcher = class {
|
|
|
33397
34459
|
if (!found) {
|
|
33398
34460
|
found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
|
|
33399
34461
|
const tiers = await this.tiers(scope, appended2);
|
|
33400
|
-
const
|
|
34462
|
+
const refused5 = [];
|
|
33401
34463
|
const commands = await scanCommands(
|
|
33402
34464
|
this.scanned,
|
|
33403
34465
|
tiers,
|
|
33404
34466
|
tiers.length - appended2.length,
|
|
33405
34467
|
void 0,
|
|
33406
|
-
|
|
34468
|
+
refused5,
|
|
33407
34469
|
await this.globalRoots(scope)
|
|
33408
34470
|
);
|
|
33409
|
-
this.refusedOf.set(commands,
|
|
34471
|
+
this.refusedOf.set(commands, refused5.map(refusing));
|
|
33410
34472
|
return commands;
|
|
33411
34473
|
}).then(
|
|
33412
34474
|
(commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(VISUDO_COMMAND.name, VISUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND).set(SEALED_COMMAND.name, SEALED_COMMAND)
|
|
@@ -33613,14 +34675,14 @@ var Launcher = class {
|
|
|
33613
34675
|
return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
|
|
33614
34676
|
}
|
|
33615
34677
|
async refusedAt(path, name2, scope) {
|
|
33616
|
-
const
|
|
33617
|
-
if (
|
|
34678
|
+
const refused5 = this.refusedOf.get(await this.commands(scope)) ?? [];
|
|
34679
|
+
if (refused5.length === 0) return void 0;
|
|
33618
34680
|
const st = await this.base.stat(path).catch(() => void 0);
|
|
33619
34681
|
if (!st?.isFile) return void 0;
|
|
33620
34682
|
const real = await physicalPathAsync(this.base, path).catch(() => path);
|
|
33621
34683
|
const same2 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
|
|
33622
34684
|
const owners = [];
|
|
33623
|
-
for (const c of
|
|
34685
|
+
for (const c of refused5) {
|
|
33624
34686
|
const origin = c.origin;
|
|
33625
34687
|
for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
|
|
33626
34688
|
if (!await same2(own)) continue;
|
|
@@ -33630,11 +34692,11 @@ var Launcher = class {
|
|
|
33630
34692
|
}
|
|
33631
34693
|
const stem = name2.replace(/\.wasm$/, "");
|
|
33632
34694
|
const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
|
|
33633
|
-
return named ?? owners[0] ?? await this.refusedCopy(real, name2,
|
|
34695
|
+
return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
|
|
33634
34696
|
}
|
|
33635
|
-
async refusedCopy(real, name2,
|
|
34697
|
+
async refusedCopy(real, name2, refused5) {
|
|
33636
34698
|
const dir = real.slice(0, real.lastIndexOf("/"));
|
|
33637
|
-
for (const c of
|
|
34699
|
+
for (const c of refused5) {
|
|
33638
34700
|
const origin = c.origin;
|
|
33639
34701
|
if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
|
|
33640
34702
|
const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
|
|
@@ -33801,8 +34863,8 @@ var Launcher = class {
|
|
|
33801
34863
|
const via = planned?.via ?? [];
|
|
33802
34864
|
for (const [i, file] of via.entries()) {
|
|
33803
34865
|
if (!byPath(file)) continue;
|
|
33804
|
-
const
|
|
33805
|
-
await need(g, "exec", this.fs.resolvePath(req.cwd, file),
|
|
34866
|
+
const want2 = i < via.length - 1 ? R_OK | X_OK : X_OK;
|
|
34867
|
+
await need(g, "exec", this.fs.resolvePath(req.cwd, file), want2);
|
|
33806
34868
|
}
|
|
33807
34869
|
for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
|
|
33808
34870
|
if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
|
|
@@ -33842,12 +34904,12 @@ var Launcher = class {
|
|
|
33842
34904
|
return void 0;
|
|
33843
34905
|
}
|
|
33844
34906
|
async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
|
|
33845
|
-
const
|
|
34907
|
+
const refused5 = (err) => {
|
|
33846
34908
|
throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
|
|
33847
34909
|
};
|
|
33848
|
-
const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(
|
|
34910
|
+
const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused5);
|
|
33849
34911
|
const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
|
|
33850
|
-
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(
|
|
34912
|
+
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
|
|
33851
34913
|
if (planned) return planned;
|
|
33852
34914
|
throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
|
|
33853
34915
|
}
|
|
@@ -34483,6 +35545,7 @@ var Launcher = class {
|
|
|
34483
35545
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
34484
35546
|
clipboard: this.clipboard,
|
|
34485
35547
|
embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
|
|
35548
|
+
devices: this.devices,
|
|
34486
35549
|
...this.dac ? { dac: this.dac } : {},
|
|
34487
35550
|
program: req.program,
|
|
34488
35551
|
changes: this.changes.sab,
|
|
@@ -34666,8 +35729,8 @@ var Launcher = class {
|
|
|
34666
35729
|
this.sampledAt = Math.max(this.sampledAt, now);
|
|
34667
35730
|
this.load = this.load.map((avg, i) => {
|
|
34668
35731
|
const window = LOAD_WINDOWS_MS[i];
|
|
34669
|
-
const
|
|
34670
|
-
return avg * Math.exp(-elapsed / window) + running *
|
|
35732
|
+
const fresh4 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
|
|
35733
|
+
return avg * Math.exp(-elapsed / window) + running * fresh4;
|
|
34671
35734
|
});
|
|
34672
35735
|
}
|
|
34673
35736
|
idleSince;
|
|
@@ -35014,7 +36077,7 @@ var Launcher = class {
|
|
|
35014
36077
|
forker(ppid, parent) {
|
|
35015
36078
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
35016
36079
|
return async (state, given) => {
|
|
35017
|
-
const
|
|
36080
|
+
const fresh4 = (fds, quietOom) => this.start({
|
|
35018
36081
|
...image,
|
|
35019
36082
|
quietOom,
|
|
35020
36083
|
cwd: state.cwd ?? parent.cwd,
|
|
@@ -35030,7 +36093,7 @@ var Launcher = class {
|
|
|
35030
36093
|
tagBase: this.tagBases.get(ppid),
|
|
35031
36094
|
links: this.links.get(ppid)
|
|
35032
36095
|
});
|
|
35033
|
-
return childHandle(await this.admitted(parent.program.abi !== "js", given,
|
|
36096
|
+
return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
|
|
35034
36097
|
};
|
|
35035
36098
|
}
|
|
35036
36099
|
keySignal(tty, fallback, sig, send2) {
|
|
@@ -35071,6 +36134,14 @@ var Launcher = class {
|
|
|
35071
36134
|
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
35072
36135
|
this.sealed.factsOf = (pid) => this.processFacts(pid);
|
|
35073
36136
|
}
|
|
36137
|
+
deviceNodes(hook) {
|
|
36138
|
+
const nodes = new DeviceNodes(this.usersMode, {
|
|
36139
|
+
groupId: async (name2) => (await this.users.groups()).find((g) => g.name === name2)?.gid,
|
|
36140
|
+
userId: async (name2) => (await this.users.lookup(name2))?.uid
|
|
36141
|
+
});
|
|
36142
|
+
if (hook) nodes.register(0, hook);
|
|
36143
|
+
return nodes;
|
|
36144
|
+
}
|
|
35074
36145
|
embedderBroker(hook) {
|
|
35075
36146
|
const broker = new EmbedderBroker(
|
|
35076
36147
|
this.usersMode,
|
|
@@ -35360,8 +36431,8 @@ var Launcher = class {
|
|
|
35360
36431
|
for (const [path, mode] of layout) {
|
|
35361
36432
|
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
35362
36433
|
const real = await this.realLayoutPath(path);
|
|
35363
|
-
const
|
|
35364
|
-
if (
|
|
36434
|
+
const fresh4 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
36435
|
+
if (fresh4) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
35365
36436
|
}
|
|
35366
36437
|
}
|
|
35367
36438
|
async realLayoutPath(given) {
|
|
@@ -35495,10 +36566,10 @@ var Launcher = class {
|
|
|
35495
36566
|
}
|
|
35496
36567
|
async mountFstab() {
|
|
35497
36568
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
35498
|
-
const
|
|
35499
|
-
for (const { spec } of
|
|
36569
|
+
const lines3 = parseFstab(text);
|
|
36570
|
+
for (const { spec } of lines3) this.mounts.note(spec, "pending");
|
|
35500
36571
|
return mountFstab(
|
|
35501
|
-
|
|
36572
|
+
lines3,
|
|
35502
36573
|
(spec) => this.mount(spec).catch((err) => {
|
|
35503
36574
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
35504
36575
|
throw err;
|
|
@@ -36204,13 +37275,13 @@ function unsendableHeaders(url, names2, hint) {
|
|
|
36204
37275
|
function refuseUnsendable(url, out, hint) {
|
|
36205
37276
|
if (!platformFilters()) return;
|
|
36206
37277
|
const kept = new Request(PROBE, { headers: out }).headers;
|
|
36207
|
-
const
|
|
37278
|
+
const refused5 = [];
|
|
36208
37279
|
for (const name2 of new Set(out.keys())) {
|
|
36209
37280
|
if (kept.has(name2)) continue;
|
|
36210
37281
|
if (TRANSPORT_OWN.has(name2)) out.delete(name2);
|
|
36211
|
-
else
|
|
37282
|
+
else refused5.push(name2);
|
|
36212
37283
|
}
|
|
36213
|
-
if (
|
|
37284
|
+
if (refused5.length > 0) throw unsendableHeaders(url, refused5, hint);
|
|
36214
37285
|
}
|
|
36215
37286
|
function streamOf(body) {
|
|
36216
37287
|
const it = body[Symbol.asyncIterator]();
|
|
@@ -36522,7 +37593,7 @@ function serveTransport(peer, transport, {
|
|
|
36522
37593
|
}
|
|
36523
37594
|
|
|
36524
37595
|
// src/client/protocol.ts
|
|
36525
|
-
var PROTOCOL = [1,
|
|
37596
|
+
var PROTOCOL = [1, 18];
|
|
36526
37597
|
var FS_METHODS = [
|
|
36527
37598
|
"readFile",
|
|
36528
37599
|
"writeFile",
|
|
@@ -36782,8 +37853,10 @@ function hostRegistries(send2, gone, port) {
|
|
|
36782
37853
|
const cdps = cdpRegistry(send2, gone);
|
|
36783
37854
|
const facades = facadeRegistry(send2, gone);
|
|
36784
37855
|
const requests = requestBinding(send2, port);
|
|
37856
|
+
const devices = deviceBinding(send2, port);
|
|
36785
37857
|
return {
|
|
36786
37858
|
requests,
|
|
37859
|
+
devices,
|
|
36787
37860
|
answer(l, req) {
|
|
36788
37861
|
if (req.op === "serve-cdp") return cdps.serve(l, req);
|
|
36789
37862
|
if (req.op === "unserve-cdp") return cdps.drop(req.registration);
|
|
@@ -36792,6 +37865,7 @@ function hostRegistries(send2, gone, port) {
|
|
|
36792
37865
|
},
|
|
36793
37866
|
close() {
|
|
36794
37867
|
requests.close();
|
|
37868
|
+
devices.close();
|
|
36795
37869
|
cdps.close();
|
|
36796
37870
|
facades.close();
|
|
36797
37871
|
}
|
|
@@ -36905,6 +37979,28 @@ function answered(pending, req, reply, detach) {
|
|
|
36905
37979
|
}
|
|
36906
37980
|
);
|
|
36907
37981
|
}
|
|
37982
|
+
async function helloRefused(host, hosts, message3) {
|
|
37983
|
+
return versionError(message3.protocol) ?? await hosts.requests.accept(host, message3) ?? await hosts.devices.accept(host, message3);
|
|
37984
|
+
}
|
|
37985
|
+
function deviceBinding(send2, port) {
|
|
37986
|
+
const asker = devicesAsker({ postMessage: (m, t) => send2(m, t) });
|
|
37987
|
+
let unbind;
|
|
37988
|
+
port.addEventListener("message", (event) => {
|
|
37989
|
+
const reply = devicesReplyOf(event.data);
|
|
37990
|
+
if (reply) asker.answer(reply);
|
|
37991
|
+
});
|
|
37992
|
+
return {
|
|
37993
|
+
async accept(host, hello) {
|
|
37994
|
+
if (!hello.devices) return void 0;
|
|
37995
|
+
if (!host.attachDevices || host.session === void 0)
|
|
37996
|
+
return "EPERM: this port may not answer device opens";
|
|
37997
|
+
const launcher = await host.launcher();
|
|
37998
|
+
unbind = launcher.devices.register(host.session, asker.hookFor(host.session));
|
|
37999
|
+
return void 0;
|
|
38000
|
+
},
|
|
38001
|
+
close: () => unbind?.()
|
|
38002
|
+
};
|
|
38003
|
+
}
|
|
36908
38004
|
function serveClient(port, host) {
|
|
36909
38005
|
let state = "new";
|
|
36910
38006
|
let self;
|
|
@@ -36938,9 +38034,9 @@ function serveClient(port, host) {
|
|
|
36938
38034
|
ended.resolve();
|
|
36939
38035
|
}
|
|
36940
38036
|
async function hello(message3) {
|
|
36941
|
-
const
|
|
36942
|
-
if (
|
|
36943
|
-
send2({ hello: { protocol: PROTOCOL, error:
|
|
38037
|
+
const refused5 = await helloRefused(host, hosts, message3);
|
|
38038
|
+
if (refused5) {
|
|
38039
|
+
send2({ hello: { protocol: PROTOCOL, error: refused5 } });
|
|
36944
38040
|
detach();
|
|
36945
38041
|
return;
|
|
36946
38042
|
}
|
|
@@ -37135,14 +38231,14 @@ async function followRedirects(send2, request) {
|
|
|
37135
38231
|
var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
|
|
37136
38232
|
var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
|
|
37137
38233
|
var resolveHost = async (host) => (await lookup2(host, { all: true, verbatim: true })).map((a) => a.address);
|
|
37138
|
-
function ipv4Place(
|
|
37139
|
-
const [a, b] =
|
|
38234
|
+
function ipv4Place(address2) {
|
|
38235
|
+
const [a, b] = address2.split(".").map(Number);
|
|
37140
38236
|
if (a === 127 || a === 0) return "loopback";
|
|
37141
38237
|
if (a === 169 && b === 254) return "link-local";
|
|
37142
38238
|
return void 0;
|
|
37143
38239
|
}
|
|
37144
|
-
function placeOf(
|
|
37145
|
-
const plain3 =
|
|
38240
|
+
function placeOf(address2) {
|
|
38241
|
+
const plain3 = address2.replace(/^\[(.*)\]$/, "$1").toLowerCase();
|
|
37146
38242
|
const kind = isIP3(plain3);
|
|
37147
38243
|
if (kind === 4) return ipv4Place(plain3);
|
|
37148
38244
|
if (kind !== 6) return void 0;
|
|
@@ -37158,12 +38254,12 @@ function ipv4Of(words3) {
|
|
|
37158
38254
|
const [high = 0, low = 0] = words3.slice(6);
|
|
37159
38255
|
return [high >> 8, high & 255, low >> 8, low & 255].join(".");
|
|
37160
38256
|
}
|
|
37161
|
-
function hextets2(
|
|
37162
|
-
const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(
|
|
37163
|
-
let text =
|
|
38257
|
+
function hextets2(address2) {
|
|
38258
|
+
const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address2)?.[1];
|
|
38259
|
+
let text = address2;
|
|
37164
38260
|
if (dotted) {
|
|
37165
38261
|
const [a, b, c, d] = dotted.split(".").map(Number);
|
|
37166
|
-
text = `${
|
|
38262
|
+
text = `${address2.slice(0, -dotted.length)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
|
|
37167
38263
|
}
|
|
37168
38264
|
const [head = "", tail2] = text.split("::");
|
|
37169
38265
|
const part = (side) => side ? side.split(":").map((w) => Number.parseInt(w, 16)) : [];
|
|
@@ -37184,13 +38280,13 @@ function allows(policy, port) {
|
|
|
37184
38280
|
if (Array.isArray(policy)) return policy.includes(port);
|
|
37185
38281
|
return policy === true && !DEVTOOLS_PORTS.includes(port);
|
|
37186
38282
|
}
|
|
37187
|
-
function refusal2(url,
|
|
37188
|
-
const where = `${
|
|
38283
|
+
function refusal2(url, address2, place) {
|
|
38284
|
+
const where = `${address2}:${portOf(url)}`;
|
|
37189
38285
|
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
37190
38286
|
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
37191
38287
|
}
|
|
37192
|
-
function connectRefusal(
|
|
37193
|
-
const where = `${
|
|
38288
|
+
function connectRefusal(address2, port, place) {
|
|
38289
|
+
const where = `${address2}:${port}`;
|
|
37194
38290
|
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
37195
38291
|
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
37196
38292
|
}
|
|
@@ -37202,11 +38298,11 @@ async function addressesOf(host, resolve2) {
|
|
|
37202
38298
|
}
|
|
37203
38299
|
async function checkDestination(target, policy, resolve2 = resolveHost) {
|
|
37204
38300
|
const url = new URL(target);
|
|
37205
|
-
for (const
|
|
37206
|
-
const place = placeOf(
|
|
38301
|
+
for (const address2 of await addressesOf(url.hostname, resolve2)) {
|
|
38302
|
+
const place = placeOf(address2);
|
|
37207
38303
|
if (!place) continue;
|
|
37208
38304
|
if (place === "loopback" && allows(policy, portOf(url))) continue;
|
|
37209
|
-
throw refusal2(url,
|
|
38305
|
+
throw refusal2(url, address2, place);
|
|
37210
38306
|
}
|
|
37211
38307
|
}
|
|
37212
38308
|
function guarded3(check, signal) {
|
|
@@ -37230,9 +38326,9 @@ function envProxyEnabled() {
|
|
|
37230
38326
|
}
|
|
37231
38327
|
function normaliseProxyUrl(url) {
|
|
37232
38328
|
try {
|
|
37233
|
-
const
|
|
37234
|
-
const host =
|
|
37235
|
-
const port =
|
|
38329
|
+
const parsed3 = new URL(url);
|
|
38330
|
+
const host = parsed3.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
38331
|
+
const port = parsed3.port || (parsed3.protocol === "https:" ? "443" : "80");
|
|
37236
38332
|
return `${host}:${port}`;
|
|
37237
38333
|
} catch {
|
|
37238
38334
|
return void 0;
|
|
@@ -37268,8 +38364,8 @@ function ensureSubscribed() {
|
|
|
37268
38364
|
const entry = loopbackStore.getStore();
|
|
37269
38365
|
if (!entry) return;
|
|
37270
38366
|
const { socket, connectParams } = message3;
|
|
37271
|
-
const
|
|
37272
|
-
if (!
|
|
38367
|
+
const address2 = socket.remoteAddress;
|
|
38368
|
+
if (!address2) {
|
|
37273
38369
|
const err2 = connectRefusal("unknown", 0, "loopback");
|
|
37274
38370
|
entry.refused = err2;
|
|
37275
38371
|
socket.destroy(err2);
|
|
@@ -37284,11 +38380,11 @@ function ensureSubscribed() {
|
|
|
37284
38380
|
}
|
|
37285
38381
|
return;
|
|
37286
38382
|
}
|
|
37287
|
-
const place = placeOf(
|
|
38383
|
+
const place = placeOf(address2);
|
|
37288
38384
|
if (!place) return;
|
|
37289
38385
|
const port = socket.remotePort ?? 0;
|
|
37290
38386
|
if (place === "loopback" && allows(entry.policy, port)) return;
|
|
37291
|
-
const err = connectRefusal(
|
|
38387
|
+
const err = connectRefusal(address2, port, place);
|
|
37292
38388
|
entry.refused = err;
|
|
37293
38389
|
socket.destroy(err);
|
|
37294
38390
|
});
|
|
@@ -37824,11 +38920,11 @@ function indexOf(haystack, needle) {
|
|
|
37824
38920
|
return -1;
|
|
37825
38921
|
}
|
|
37826
38922
|
function parseHead2(bytes) {
|
|
37827
|
-
const [statusLine = "", ...
|
|
38923
|
+
const [statusLine = "", ...lines3] = latin13.decode(bytes).split("\r\n");
|
|
37828
38924
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
37829
38925
|
if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
37830
38926
|
const headers = [];
|
|
37831
|
-
for (const line of
|
|
38927
|
+
for (const line of lines3) {
|
|
37832
38928
|
const colon = line.indexOf(":");
|
|
37833
38929
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
37834
38930
|
}
|
|
@@ -37897,13 +38993,13 @@ function stream(chunks2, socket, signal) {
|
|
|
37897
38993
|
async function requestBytes(request) {
|
|
37898
38994
|
const url = new URL(request.url);
|
|
37899
38995
|
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
|
|
37900
|
-
const
|
|
38996
|
+
const lines3 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
37901
38997
|
request.headers.forEach((value2, name2) => {
|
|
37902
|
-
if (!OWN_HEADERS2.has(name2))
|
|
38998
|
+
if (!OWN_HEADERS2.has(name2)) lines3.push(`${name2}: ${value2}`);
|
|
37903
38999
|
});
|
|
37904
|
-
if (body)
|
|
37905
|
-
|
|
37906
|
-
const head = encoder11.encode(
|
|
39000
|
+
if (body) lines3.push(`Content-Length: ${body.length}`);
|
|
39001
|
+
lines3.push("Connection: close", "", "");
|
|
39002
|
+
const head = encoder11.encode(lines3.join("\r\n"));
|
|
37907
39003
|
const out = new Uint8Array(head.length + (body?.length ?? 0));
|
|
37908
39004
|
out.set(head);
|
|
37909
39005
|
if (body) out.set(body, head.length);
|
|
@@ -38044,8 +39140,10 @@ var Channel = class {
|
|
|
38044
39140
|
return this.request(call).done;
|
|
38045
39141
|
}
|
|
38046
39142
|
requests;
|
|
39143
|
+
devices;
|
|
38047
39144
|
receive(data) {
|
|
38048
39145
|
if (this.requests?.handle(data)) return;
|
|
39146
|
+
if (this.devices?.handle(data)) return;
|
|
38049
39147
|
if (data.traits && data.id === void 0) {
|
|
38050
39148
|
if (this.remote) this.remote.traits = data.traits;
|
|
38051
39149
|
for (const listener of [...this.traitsListeners]) listener(data.traits);
|
|
@@ -38250,7 +39348,7 @@ async function sudoRunOn(channel, replay, { stdout, stderr, tag }) {
|
|
|
38250
39348
|
return { pid, pgid: pid, ended: endedOf(done4, () => endedBy) };
|
|
38251
39349
|
}
|
|
38252
39350
|
function endedOf(done4, signal) {
|
|
38253
|
-
return
|
|
39351
|
+
return quiet2(
|
|
38254
39352
|
done4.then((status) => {
|
|
38255
39353
|
const by = signal();
|
|
38256
39354
|
return { status, ...by ? { signal: by } : {} };
|
|
@@ -38380,9 +39478,14 @@ function since(channel, minor, what, run2) {
|
|
|
38380
39478
|
}
|
|
38381
39479
|
return run2();
|
|
38382
39480
|
}
|
|
38383
|
-
async function greet(channel, lock, timeoutMs, requests) {
|
|
39481
|
+
async function greet(channel, lock, timeoutMs, requests, devices) {
|
|
38384
39482
|
channel.post({
|
|
38385
|
-
hello: {
|
|
39483
|
+
hello: {
|
|
39484
|
+
protocol: PROTOCOL,
|
|
39485
|
+
...lock ? { lock } : {},
|
|
39486
|
+
...requests ? { requests } : {},
|
|
39487
|
+
...devices ? { devices: true } : {}
|
|
39488
|
+
}
|
|
38386
39489
|
});
|
|
38387
39490
|
const timer = setTimeout(
|
|
38388
39491
|
() => channel.end(
|
|
@@ -38396,9 +39499,9 @@ async function greet(channel, lock, timeoutMs, requests) {
|
|
|
38396
39499
|
} finally {
|
|
38397
39500
|
clearTimeout(timer);
|
|
38398
39501
|
}
|
|
38399
|
-
const
|
|
38400
|
-
if (
|
|
38401
|
-
const error = new Error(
|
|
39502
|
+
const refused5 = hello.error ?? versionError(hello.protocol);
|
|
39503
|
+
if (refused5) {
|
|
39504
|
+
const error = new Error(refused5);
|
|
38402
39505
|
channel.end(error);
|
|
38403
39506
|
throw error;
|
|
38404
39507
|
}
|
|
@@ -38410,11 +39513,14 @@ async function attachKernel(port, options = {}) {
|
|
|
38410
39513
|
const channel = new Channel(port, own.release);
|
|
38411
39514
|
const requests = options.requests;
|
|
38412
39515
|
if (requests) channel.requests = embedderAnswerer(port, () => requests);
|
|
39516
|
+
const devices = options.devices ? devicesHook(options.devices) : void 0;
|
|
39517
|
+
if (devices) channel.devices = devicesAnswerer(port, () => devices);
|
|
38413
39518
|
const hello = await greet(
|
|
38414
39519
|
channel,
|
|
38415
39520
|
own.name,
|
|
38416
39521
|
options.timeoutMs ?? 1e4,
|
|
38417
|
-
requests ? kindsOf(requests) : void 0
|
|
39522
|
+
requests ? kindsOf(requests) : void 0,
|
|
39523
|
+
devices !== void 0
|
|
38418
39524
|
);
|
|
38419
39525
|
channel.protocol = hello.protocol;
|
|
38420
39526
|
if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));
|
|
@@ -38741,7 +39847,8 @@ function pidAlive(pid) {
|
|
|
38741
39847
|
}
|
|
38742
39848
|
}
|
|
38743
39849
|
var nodeSessions = 0;
|
|
38744
|
-
|
|
39850
|
+
var portSessions = /* @__PURE__ */ new WeakMap();
|
|
39851
|
+
function nodeClient(launcher, clients, { user, tag, requests, devices }) {
|
|
38745
39852
|
tagOption(tag);
|
|
38746
39853
|
const { port1, port2 } = new MessageChannel3();
|
|
38747
39854
|
const session = ++nodeSessions;
|
|
@@ -38750,17 +39857,27 @@ function nodeClient(launcher, clients, { user, tag, requests }) {
|
|
|
38750
39857
|
signal: signalNumber,
|
|
38751
39858
|
session,
|
|
38752
39859
|
...requests === "attach" ? { attachRequests: true } : {},
|
|
39860
|
+
...devices === "attach" ? { attachDevices: true } : {},
|
|
38753
39861
|
...user !== void 0 ? { user } : {},
|
|
38754
39862
|
...tag !== void 0 ? { tag } : {}
|
|
38755
39863
|
});
|
|
38756
39864
|
clients.add(served2);
|
|
38757
39865
|
const unbind = requests && requests !== "attach" ? launcher.embedder.register(session, checkedRequests(requests)) : void 0;
|
|
39866
|
+
const unbindDevices = devices && devices !== "attach" ? launcher.devices.register(session, devicesHook(devices)) : void 0;
|
|
38758
39867
|
void served2.closed.then(() => {
|
|
38759
39868
|
clients.delete(served2);
|
|
38760
39869
|
unbind?.();
|
|
39870
|
+
unbindDevices?.();
|
|
38761
39871
|
});
|
|
39872
|
+
portSessions.set(port2, session);
|
|
38762
39873
|
return port2;
|
|
38763
39874
|
}
|
|
39875
|
+
function sessionOfPort(client) {
|
|
39876
|
+
if (client === void 0) return 0;
|
|
39877
|
+
const session = portSessions.get(client);
|
|
39878
|
+
if (session === void 0) throw fsError("EINVAL", "client is not a port from connect()");
|
|
39879
|
+
return session;
|
|
39880
|
+
}
|
|
38764
39881
|
function checkedRequests(hook) {
|
|
38765
39882
|
const kinds = kindsOf(hook);
|
|
38766
39883
|
return { kinds, handle: (request) => hook.handle(request) };
|
|
@@ -38787,7 +39904,23 @@ async function nodeSudoRun(launcher, guard2, replay, options) {
|
|
|
38787
39904
|
const { status, signal } = await running.ended;
|
|
38788
39905
|
return { status, ...signalField(signal) };
|
|
38789
39906
|
});
|
|
38790
|
-
return { pid: running.pid, pgid: running.pid, ended:
|
|
39907
|
+
return { pid: running.pid, pgid: running.pid, ended: quiet2(ended) };
|
|
39908
|
+
}
|
|
39909
|
+
function pageHooks(options) {
|
|
39910
|
+
return {
|
|
39911
|
+
...options.clipboard ? { clipboard: options.clipboard } : {},
|
|
39912
|
+
...options.approve ? { approve: options.approve } : {},
|
|
39913
|
+
...options.requests ? { embedder: checkedRequests(options.requests) } : {},
|
|
39914
|
+
...options.devices ? { devices: devicesHook(options.devices) } : {}
|
|
39915
|
+
};
|
|
39916
|
+
}
|
|
39917
|
+
function nodeDevices(launcher, guard2) {
|
|
39918
|
+
return {
|
|
39919
|
+
add: ({ client, ...spec }) => guard2(async () => {
|
|
39920
|
+
const id2 = await launcher.devices.add(spec, sessionOfPort(client));
|
|
39921
|
+
return { remove: async () => void launcher.devices.remove(id2) };
|
|
39922
|
+
})
|
|
39923
|
+
};
|
|
38791
39924
|
}
|
|
38792
39925
|
async function createNodeKernel(options = {}) {
|
|
38793
39926
|
const hooks = sealedHooks(options.sealed);
|
|
@@ -38833,9 +39966,7 @@ async function createNodeKernel(options = {}) {
|
|
|
38833
39966
|
...options.protect ? { protect: options.protect } : {},
|
|
38834
39967
|
...hooks.request ? { sealed: hooks.request } : {},
|
|
38835
39968
|
...hooks.fetch ? { sealedFetch: hooks.fetch } : {},
|
|
38836
|
-
...options
|
|
38837
|
-
...options.requests ? { embedder: checkedRequests(options.requests) } : {},
|
|
38838
|
-
...options.approve ? { approve: options.approve } : {},
|
|
39969
|
+
...pageHooks(options),
|
|
38839
39970
|
...options.sudo?.clock ? { sudoClock: options.sudo.clock } : {},
|
|
38840
39971
|
...options.wasmMemories ? { wasmMemories: options.wasmMemories } : {},
|
|
38841
39972
|
cron: { lock: cronLock(root), ...options.cron?.clock ? { clock: options.cron.clock } : {} }
|
|
@@ -38880,8 +40011,8 @@ async function createNodeKernel(options = {}) {
|
|
|
38880
40011
|
);
|
|
38881
40012
|
return {
|
|
38882
40013
|
pid: session.pid,
|
|
38883
|
-
exited:
|
|
38884
|
-
ended:
|
|
40014
|
+
exited: quiet2(guard2(() => session.exited)),
|
|
40015
|
+
ended: quiet2(
|
|
38885
40016
|
guard2(async () => ({ status: await session.exited, ...signalField(session.termsig()) }))
|
|
38886
40017
|
),
|
|
38887
40018
|
get onData() {
|
|
@@ -38919,6 +40050,7 @@ async function createNodeKernel(options = {}) {
|
|
|
38919
40050
|
updateTransportTraits: () => guard2(async () => launcher.updateTransportTraits()),
|
|
38920
40051
|
...nodeAccounts(launcher, guard2),
|
|
38921
40052
|
sudoRun: (replay, sudoOptions = {}) => nodeSudoRun(launcher, guard2, replay, sudoOptions),
|
|
40053
|
+
devices: nodeDevices(launcher, guard2),
|
|
38922
40054
|
async connect(connectOptions = {}) {
|
|
38923
40055
|
if (terminated) throw new Error(TERMINATED);
|
|
38924
40056
|
const { user } = connectOptions;
|