@ai-ecoverse/slicc-kernel 1.181.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 +62 -11
- 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 +1727 -703
- 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 +1897 -751
- 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/cdp/types.d.ts +2 -0
- 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/kernel/socket.d.ts +2 -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 +3 -3
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");
|
|
@@ -2454,6 +2674,7 @@ var LoopbackNet = class {
|
|
|
2454
2674
|
reserved = /* @__PURE__ */ new Set();
|
|
2455
2675
|
facades = /* @__PURE__ */ new Map();
|
|
2456
2676
|
proxyStamped;
|
|
2677
|
+
stamped = /* @__PURE__ */ new Set();
|
|
2457
2678
|
nextEphemeral = EPHEMERAL_FIRST;
|
|
2458
2679
|
route;
|
|
2459
2680
|
origins;
|
|
@@ -2528,6 +2749,9 @@ var LoopbackNet = class {
|
|
|
2528
2749
|
this.kernelOnly(port, true);
|
|
2529
2750
|
this.facades.set(port, name2);
|
|
2530
2751
|
}
|
|
2752
|
+
stampPeers(port) {
|
|
2753
|
+
this.stamped.add(port);
|
|
2754
|
+
}
|
|
2531
2755
|
stampProxy(port) {
|
|
2532
2756
|
this.proxyStamped = port;
|
|
2533
2757
|
}
|
|
@@ -2536,7 +2760,7 @@ var LoopbackNet = class {
|
|
|
2536
2760
|
}
|
|
2537
2761
|
stampsPeers(addr) {
|
|
2538
2762
|
if (addr.family !== "inet" || !isLoopback(canonicalHost(addr.host))) return false;
|
|
2539
|
-
return this.facades.has(addr.port) || this.proxyStamped === addr.port;
|
|
2763
|
+
return this.facades.has(addr.port) || this.stamped.has(addr.port) || this.proxyStamped === addr.port;
|
|
2540
2764
|
}
|
|
2541
2765
|
activate(addr, start2) {
|
|
2542
2766
|
this.activators.set(keyOf(addr), start2);
|
|
@@ -2727,7 +2951,7 @@ function signalName(sig) {
|
|
|
2727
2951
|
const name2 = Object.entries(SIG).find(([, n]) => n === sig)?.[0];
|
|
2728
2952
|
return name2 === void 0 ? void 0 : `SIG${name2}`;
|
|
2729
2953
|
}
|
|
2730
|
-
function
|
|
2954
|
+
function quiet2(p) {
|
|
2731
2955
|
p.catch(() => void 0);
|
|
2732
2956
|
return p;
|
|
2733
2957
|
}
|
|
@@ -2961,21 +3185,21 @@ function latin1Bytes(text) {
|
|
|
2961
3185
|
return out;
|
|
2962
3186
|
}
|
|
2963
3187
|
function parseRequestHead(bytes) {
|
|
2964
|
-
const
|
|
2965
|
-
while (
|
|
2966
|
-
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() ?? "");
|
|
2967
3191
|
if (!match) throw new HttpError(400, "malformed request line");
|
|
2968
3192
|
if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
|
|
2969
3193
|
return {
|
|
2970
3194
|
method: match[1],
|
|
2971
3195
|
target: match[2],
|
|
2972
3196
|
minor: Number(match[4]),
|
|
2973
|
-
headers: parseFields(
|
|
3197
|
+
headers: parseFields(lines3)
|
|
2974
3198
|
};
|
|
2975
3199
|
}
|
|
2976
|
-
function parseFields(
|
|
3200
|
+
function parseFields(lines3) {
|
|
2977
3201
|
const headers = [];
|
|
2978
|
-
for (const line of
|
|
3202
|
+
for (const line of lines3) {
|
|
2979
3203
|
if (line === "") continue;
|
|
2980
3204
|
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
2981
3205
|
throw new HttpError(400, "obsolete line folding");
|
|
@@ -3444,10 +3668,10 @@ var CdpFacade = class {
|
|
|
3444
3668
|
);
|
|
3445
3669
|
return;
|
|
3446
3670
|
}
|
|
3447
|
-
await this.write(conn, json(await this.list(runtime)));
|
|
3671
|
+
await this.write(conn, json(await this.list(runtime, conn.peerStamp)));
|
|
3448
3672
|
}
|
|
3449
|
-
async list(runtime) {
|
|
3450
|
-
const conn = await this.host(runtime);
|
|
3673
|
+
async list(runtime, stamp) {
|
|
3674
|
+
const conn = await this.host(runtime, stamp);
|
|
3451
3675
|
try {
|
|
3452
3676
|
const reply = await new Promise(
|
|
3453
3677
|
(resolve2, reject) => {
|
|
@@ -3460,10 +3684,10 @@ var CdpFacade = class {
|
|
|
3460
3684
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
3461
3685
|
};
|
|
3462
3686
|
conn.onmessage = (message3) => {
|
|
3463
|
-
const
|
|
3464
|
-
if (
|
|
3687
|
+
const parsed3 = JSON.parse(message3);
|
|
3688
|
+
if (parsed3.id !== 1) return;
|
|
3465
3689
|
clearTimeout(timer);
|
|
3466
|
-
resolve2(
|
|
3690
|
+
resolve2(parsed3);
|
|
3467
3691
|
};
|
|
3468
3692
|
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
3469
3693
|
}
|
|
@@ -3482,12 +3706,15 @@ var CdpFacade = class {
|
|
|
3482
3706
|
conn.close();
|
|
3483
3707
|
}
|
|
3484
3708
|
}
|
|
3485
|
-
host(runtime) {
|
|
3486
|
-
return this.options.hosts.connect(
|
|
3709
|
+
host(runtime, stamp) {
|
|
3710
|
+
return this.options.hosts.connect({
|
|
3711
|
+
...runtime ? { runtime } : {},
|
|
3712
|
+
...stamp ? { stamp } : {}
|
|
3713
|
+
});
|
|
3487
3714
|
}
|
|
3488
3715
|
async upgrade(conn, incoming, req, runtime) {
|
|
3489
3716
|
const accept2 = await acceptKey(req.headers);
|
|
3490
|
-
const host = await this.host(runtime);
|
|
3717
|
+
const host = await this.host(runtime, conn.peerStamp);
|
|
3491
3718
|
const limits = {
|
|
3492
3719
|
max: this.options.maxMessage ?? MAX_MESSAGE,
|
|
3493
3720
|
closeWaitMs: this.options.closeWaitMs ?? CLOSE_WAIT_MS
|
|
@@ -3567,6 +3794,7 @@ function enableCdp(net, hosts) {
|
|
|
3567
3794
|
let facade;
|
|
3568
3795
|
net.kernelOnly(CDP_PORT, true);
|
|
3569
3796
|
net.reserve(CDP_PORT);
|
|
3797
|
+
net.stampPeers(CDP_PORT);
|
|
3570
3798
|
net.activate({ family: "inet", host: "127.0.0.1", port: CDP_PORT }, () => {
|
|
3571
3799
|
const started = new CdpFacade({ net, hosts });
|
|
3572
3800
|
facade = started;
|
|
@@ -3609,7 +3837,7 @@ var CdpHosts = class {
|
|
|
3609
3837
|
if (!entry) throw new HttpError(503, NO_CDP_HOST);
|
|
3610
3838
|
let conn;
|
|
3611
3839
|
try {
|
|
3612
|
-
conn = await entry.hook(request
|
|
3840
|
+
conn = await entry.hook(hookRequest(request));
|
|
3613
3841
|
} catch (e) {
|
|
3614
3842
|
throw new HttpError(502, `CDP host: ${e instanceof Error ? e.message : String(e)}`);
|
|
3615
3843
|
}
|
|
@@ -3629,6 +3857,12 @@ var CdpHosts = class {
|
|
|
3629
3857
|
for (const entry of [this.embedder, ...this.clients]) if (entry) closeAll(entry);
|
|
3630
3858
|
}
|
|
3631
3859
|
};
|
|
3860
|
+
function hookRequest({ runtime, stamp }) {
|
|
3861
|
+
return {
|
|
3862
|
+
...runtime ? { runtime } : {},
|
|
3863
|
+
...stamp ? { stamp: structuredClone(stamp) } : {}
|
|
3864
|
+
};
|
|
3865
|
+
}
|
|
3632
3866
|
function closeAll(entry) {
|
|
3633
3867
|
for (const conn of [...entry.open]) {
|
|
3634
3868
|
conn.close();
|
|
@@ -3787,7 +4021,7 @@ function satisfies(version, range2) {
|
|
|
3787
4021
|
}
|
|
3788
4022
|
|
|
3789
4023
|
// src/version.ts
|
|
3790
|
-
var KERNEL_VERSION = true ? "1.
|
|
4024
|
+
var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
|
|
3791
4025
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3792
4026
|
function procVersion() {
|
|
3793
4027
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -4079,10 +4313,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
|
|
|
4079
4313
|
const own = commandsOf(pkg, manifest);
|
|
4080
4314
|
const named = new Set(own.map((command) => command.name));
|
|
4081
4315
|
const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
|
|
4082
|
-
const
|
|
4316
|
+
const refused5 = engineRefusal(manifest, kernel);
|
|
4083
4317
|
const name2 = pkg.slice(root.length + 1);
|
|
4084
4318
|
const dependency = direct?.has(name2) === false;
|
|
4085
|
-
const marks = { ...
|
|
4319
|
+
const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
|
|
4086
4320
|
out.push({
|
|
4087
4321
|
name: name2,
|
|
4088
4322
|
pkg,
|
|
@@ -4114,7 +4348,7 @@ function winners(tier) {
|
|
|
4114
4348
|
}
|
|
4115
4349
|
return out;
|
|
4116
4350
|
}
|
|
4117
|
-
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()) {
|
|
4118
4352
|
const found = /* @__PURE__ */ new Map();
|
|
4119
4353
|
const physical = physicalResolver(fs);
|
|
4120
4354
|
const tiers = (typeof modules === "string" ? [modules] : modules).map(
|
|
@@ -4128,7 +4362,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
|
|
|
4128
4362
|
const dependencies = [];
|
|
4129
4363
|
for (const [index, roots] of tiers.entries()) {
|
|
4130
4364
|
const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
|
|
4131
|
-
if (
|
|
4365
|
+
if (refused5) refused5.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
|
|
4132
4366
|
await take(tier.filter((entry) => !entry.dependency));
|
|
4133
4367
|
dependencies.push(tier.filter((entry) => entry.dependency));
|
|
4134
4368
|
}
|
|
@@ -4732,9 +4966,9 @@ var TAGS_VERSION = 1;
|
|
|
4732
4966
|
async function readTags(fs) {
|
|
4733
4967
|
const text = await fs.readFile(TAGS_PATH).catch(() => "");
|
|
4734
4968
|
try {
|
|
4735
|
-
const
|
|
4736
|
-
if (!
|
|
4737
|
-
const { v: _, ...tables } =
|
|
4969
|
+
const parsed3 = JSON.parse(text);
|
|
4970
|
+
if (!parsed3 || typeof parsed3 !== "object") return {};
|
|
4971
|
+
const { v: _, ...tables } = parsed3;
|
|
4738
4972
|
return tables;
|
|
4739
4973
|
} catch {
|
|
4740
4974
|
return {};
|
|
@@ -4804,27 +5038,27 @@ var Crontabs = class {
|
|
|
4804
5038
|
const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
|
|
4805
5039
|
if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
|
|
4806
5040
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
4807
|
-
const
|
|
4808
|
-
await fs.writeFile(
|
|
4809
|
-
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);
|
|
4810
5044
|
}
|
|
4811
5045
|
async install(path, target, text, caller) {
|
|
4812
5046
|
if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
|
|
4813
5047
|
const { errors } = parseCrontab(text, path);
|
|
4814
5048
|
if (errors.length > 0) {
|
|
4815
|
-
const
|
|
4816
|
-
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") };
|
|
4817
5051
|
}
|
|
4818
5052
|
const { fs, owners } = this.o;
|
|
4819
5053
|
if (!await fs.exists(SPOOL_DIR)) {
|
|
4820
5054
|
await fs.mkdir(SPOOL_DIR, { recursive: true });
|
|
4821
5055
|
if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
|
|
4822
5056
|
}
|
|
4823
|
-
const
|
|
5057
|
+
const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
|
|
4824
5058
|
const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
|
|
4825
5059
|
await this.retag(path, text, caller);
|
|
4826
|
-
await fs.writeFile(
|
|
4827
|
-
await fs.rename(
|
|
5060
|
+
await fs.writeFile(fresh4, text, owner);
|
|
5061
|
+
await fs.rename(fresh4, path);
|
|
4828
5062
|
return { text: "" };
|
|
4829
5063
|
}
|
|
4830
5064
|
};
|
|
@@ -5158,15 +5392,15 @@ var UnlinkedKeeper = class {
|
|
|
5158
5392
|
const releases = [];
|
|
5159
5393
|
const take = async () => {
|
|
5160
5394
|
for (; ; ) {
|
|
5161
|
-
const
|
|
5162
|
-
if (
|
|
5163
|
-
|
|
5164
|
-
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])) {
|
|
5165
5399
|
for (const release of releases.splice(0)) release();
|
|
5166
|
-
|
|
5167
|
-
|
|
5400
|
+
fresh4.push(...paths.splice(0));
|
|
5401
|
+
fresh4.sort();
|
|
5168
5402
|
}
|
|
5169
|
-
for (const path of
|
|
5403
|
+
for (const path of fresh4) {
|
|
5170
5404
|
paths.push(path);
|
|
5171
5405
|
releases.push(await this.gate.acquire(path));
|
|
5172
5406
|
}
|
|
@@ -5562,10 +5796,10 @@ var Output = class {
|
|
|
5562
5796
|
};
|
|
5563
5797
|
function readStamp(text) {
|
|
5564
5798
|
try {
|
|
5565
|
-
const
|
|
5566
|
-
const jobs =
|
|
5567
|
-
const reboot =
|
|
5568
|
-
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 : {};
|
|
5569
5803
|
return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
|
|
5570
5804
|
} catch {
|
|
5571
5805
|
return { jobs: {}, reboot: {}, when: {} };
|
|
@@ -5685,10 +5919,10 @@ var CronService = class {
|
|
|
5685
5919
|
const text = JSON.stringify(stamp);
|
|
5686
5920
|
const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
|
|
5687
5921
|
if (old === text || old === void 0 && text === EMPTY_STAMP) return;
|
|
5688
|
-
const
|
|
5922
|
+
const fresh4 = `${STAMP_PATH}.new`;
|
|
5689
5923
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
5690
|
-
await fs.writeFile(
|
|
5691
|
-
await fs.rename(
|
|
5924
|
+
await fs.writeFile(fresh4, text, owner);
|
|
5925
|
+
await fs.rename(fresh4, STAMP_PATH);
|
|
5692
5926
|
}
|
|
5693
5927
|
async load() {
|
|
5694
5928
|
const { fs } = this.host;
|
|
@@ -5741,10 +5975,10 @@ var CronService = class {
|
|
|
5741
5975
|
await this.once(path, text, errors);
|
|
5742
5976
|
return table;
|
|
5743
5977
|
}
|
|
5744
|
-
async once(path, version,
|
|
5978
|
+
async once(path, version, lines3) {
|
|
5745
5979
|
if (this.reported.get(path) === version) return;
|
|
5746
5980
|
this.reported.set(path, version);
|
|
5747
|
-
for (const line of
|
|
5981
|
+
for (const line of lines3) await this.error(line);
|
|
5748
5982
|
}
|
|
5749
5983
|
async error(line) {
|
|
5750
5984
|
if (this.stopping.signal.aborted) return;
|
|
@@ -6046,8 +6280,8 @@ function canonicalHost2(host) {
|
|
|
6046
6280
|
function localV4(a, b) {
|
|
6047
6281
|
return a === 127 || a === 0 || a === 169 && b === 254;
|
|
6048
6282
|
}
|
|
6049
|
-
function hextets(
|
|
6050
|
-
const [head, tail2, extra] =
|
|
6283
|
+
function hextets(address2) {
|
|
6284
|
+
const [head, tail2, extra] = address2.split("::");
|
|
6051
6285
|
if (extra !== void 0) return void 0;
|
|
6052
6286
|
const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
6053
6287
|
const front = part(head);
|
|
@@ -6055,8 +6289,8 @@ function hextets(address) {
|
|
|
6055
6289
|
const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
6056
6290
|
return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
|
|
6057
6291
|
}
|
|
6058
|
-
function localV6(
|
|
6059
|
-
const g = hextets(
|
|
6292
|
+
function localV6(address2) {
|
|
6293
|
+
const g = hextets(address2);
|
|
6060
6294
|
if (!g) return true;
|
|
6061
6295
|
if ((g[0] & 65472) === 65152) return true;
|
|
6062
6296
|
const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
|
|
@@ -6429,8 +6663,8 @@ var RealmProxy = class {
|
|
|
6429
6663
|
}
|
|
6430
6664
|
async connect(conn, incoming, req, idle2) {
|
|
6431
6665
|
const target = tunnelTarget(req);
|
|
6432
|
-
const
|
|
6433
|
-
if (
|
|
6666
|
+
const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
6667
|
+
if (refused5) throw refused5;
|
|
6434
6668
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
6435
6669
|
try {
|
|
6436
6670
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -6986,15 +7220,15 @@ async function followLinks(fs, path) {
|
|
|
6986
7220
|
}
|
|
6987
7221
|
var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
|
|
6988
7222
|
var EMPTY2 = new Uint8Array(0);
|
|
6989
|
-
function
|
|
7223
|
+
function quiet3(promise) {
|
|
6990
7224
|
return promise.catch(() => void 0);
|
|
6991
7225
|
}
|
|
6992
7226
|
async function readsAs2(fs, cred, target) {
|
|
6993
|
-
const walked = await
|
|
7227
|
+
const walked = await quiet3(access(fs, cred, target, R_OK, { directory: false }));
|
|
6994
7228
|
return walked !== void 0 && walked.denied === void 0 && !walked.missing;
|
|
6995
7229
|
}
|
|
6996
7230
|
async function shownTarget(fs, target) {
|
|
6997
|
-
const st = await
|
|
7231
|
+
const st = await quiet3(fs.stat(target));
|
|
6998
7232
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
6999
7233
|
}
|
|
7000
7234
|
async function madeFor(owner, virtual) {
|
|
@@ -7317,10 +7551,10 @@ var FreshModules = class {
|
|
|
7317
7551
|
this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
|
|
7318
7552
|
}
|
|
7319
7553
|
forget(pid) {
|
|
7320
|
-
for (const [path,
|
|
7554
|
+
for (const [path, fresh4] of this.fresh) if (fresh4.pid === pid) this.fresh.delete(path);
|
|
7321
7555
|
}
|
|
7322
|
-
drop(
|
|
7323
|
-
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);
|
|
7324
7558
|
}
|
|
7325
7559
|
moved(from, to) {
|
|
7326
7560
|
const source = normalizePath(from);
|
|
@@ -7329,10 +7563,10 @@ var FreshModules = class {
|
|
|
7329
7563
|
for (const key of [...this.fresh.keys()]) {
|
|
7330
7564
|
if (key === target || within3(key, target)) this.fresh.delete(key);
|
|
7331
7565
|
}
|
|
7332
|
-
for (const [key,
|
|
7566
|
+
for (const [key, fresh4] of [...this.fresh]) {
|
|
7333
7567
|
if (key !== source && !within3(key, source)) continue;
|
|
7334
7568
|
this.fresh.delete(key);
|
|
7335
|
-
this.fresh.set(target + key.slice(source.length),
|
|
7569
|
+
this.fresh.set(target + key.slice(source.length), fresh4);
|
|
7336
7570
|
}
|
|
7337
7571
|
}
|
|
7338
7572
|
wrap(fs) {
|
|
@@ -7342,15 +7576,15 @@ var FreshModules = class {
|
|
|
7342
7576
|
};
|
|
7343
7577
|
const decide = async (given, known) => {
|
|
7344
7578
|
const path = normalizePath(given);
|
|
7345
|
-
const
|
|
7346
|
-
if (!
|
|
7579
|
+
const fresh4 = this.fresh.get(path);
|
|
7580
|
+
if (!fresh4) return;
|
|
7347
7581
|
const bytes = known && known.length >= MAGIC.length ? known : await head(path);
|
|
7348
|
-
const
|
|
7349
|
-
|
|
7350
|
-
if (
|
|
7582
|
+
const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
|
|
7583
|
+
fresh4.setup = false;
|
|
7584
|
+
if (want2 === fresh4.added) return;
|
|
7351
7585
|
const mode = (await fs.stat(path)).mode & 4095;
|
|
7352
|
-
await fs.chmod(path, mode & ~
|
|
7353
|
-
|
|
7586
|
+
await fs.chmod(path, mode & ~fresh4.added | want2);
|
|
7587
|
+
fresh4.added = want2;
|
|
7354
7588
|
};
|
|
7355
7589
|
const overrides = {
|
|
7356
7590
|
writeFile: async (path, content, owner) => {
|
|
@@ -7363,9 +7597,9 @@ var FreshModules = class {
|
|
|
7363
7597
|
}
|
|
7364
7598
|
},
|
|
7365
7599
|
chmod: async (path, mode) => {
|
|
7366
|
-
const
|
|
7367
|
-
if (
|
|
7368
|
-
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);
|
|
7369
7603
|
await fs.chmod(path, mode);
|
|
7370
7604
|
},
|
|
7371
7605
|
rm: async (path, options) => {
|
|
@@ -7382,16 +7616,16 @@ var FreshModules = class {
|
|
|
7382
7616
|
link: async (from, to, follow2) => {
|
|
7383
7617
|
await fs.link(from, to, follow2);
|
|
7384
7618
|
const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
|
|
7385
|
-
const
|
|
7386
|
-
if (
|
|
7619
|
+
const fresh4 = this.fresh.get(normalizePath(source));
|
|
7620
|
+
if (fresh4) this.fresh.set(normalizePath(to), fresh4);
|
|
7387
7621
|
}
|
|
7388
7622
|
};
|
|
7389
7623
|
const { append } = fs;
|
|
7390
7624
|
if (append) {
|
|
7391
7625
|
overrides.append = async (path, bytes, options) => {
|
|
7392
|
-
const
|
|
7626
|
+
const fresh4 = this.fresh.has(normalizePath(path));
|
|
7393
7627
|
const end = await append.call(fs, path, bytes, options);
|
|
7394
|
-
if (
|
|
7628
|
+
if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
|
|
7395
7629
|
return end;
|
|
7396
7630
|
};
|
|
7397
7631
|
}
|
|
@@ -8246,7 +8480,7 @@ var FsWatchers = class {
|
|
|
8246
8480
|
};
|
|
8247
8481
|
const content = (run2, types) => async (...args) => {
|
|
8248
8482
|
const at = normalizePath(args[0]);
|
|
8249
|
-
const
|
|
8483
|
+
const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
|
|
8250
8484
|
let done4 = false;
|
|
8251
8485
|
try {
|
|
8252
8486
|
await run2(...args);
|
|
@@ -8255,11 +8489,11 @@ var FsWatchers = class {
|
|
|
8255
8489
|
const real = await realOf(at);
|
|
8256
8490
|
const paths = real === at ? [at] : [at, real];
|
|
8257
8491
|
this.report(paths);
|
|
8258
|
-
if (done4) for (const type of types(
|
|
8492
|
+
if (done4) for (const type of types(fresh4, args)) this.emit(type, paths);
|
|
8259
8493
|
}
|
|
8260
8494
|
};
|
|
8261
|
-
const written2 = (
|
|
8262
|
-
const overwritten = (
|
|
8495
|
+
const written2 = (fresh4) => fresh4 ? ["rename", "change"] : ["change"];
|
|
8496
|
+
const overwritten = (fresh4, args) => fresh4 || args[1].length === 0 ? written2(fresh4) : ["change", "change"];
|
|
8263
8497
|
const reshaping = (run2) => async (...args) => {
|
|
8264
8498
|
this.reshaped();
|
|
8265
8499
|
try {
|
|
@@ -8292,14 +8526,14 @@ var FsWatchers = class {
|
|
|
8292
8526
|
if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
|
|
8293
8527
|
}
|
|
8294
8528
|
}
|
|
8295
|
-
const
|
|
8529
|
+
const fresh4 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
|
|
8296
8530
|
let done4 = false;
|
|
8297
8531
|
try {
|
|
8298
8532
|
await fs.mkdir(path, options);
|
|
8299
8533
|
done4 = true;
|
|
8300
8534
|
} finally {
|
|
8301
8535
|
this.report([...created4, path]);
|
|
8302
|
-
if (done4) this.emit("rename",
|
|
8536
|
+
if (done4) this.emit("rename", fresh4 ? [...created4, path] : created4);
|
|
8303
8537
|
}
|
|
8304
8538
|
},
|
|
8305
8539
|
rm: reshaping(async (path, options) => {
|
|
@@ -8809,15 +9043,15 @@ function embedderFailure(err) {
|
|
|
8809
9043
|
return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
|
|
8810
9044
|
}
|
|
8811
9045
|
function headerOf(bytes) {
|
|
8812
|
-
let
|
|
9046
|
+
let parsed3;
|
|
8813
9047
|
try {
|
|
8814
|
-
|
|
9048
|
+
parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
8815
9049
|
} catch {
|
|
8816
9050
|
throw new KernelError("EINVAL", "the request header is not JSON");
|
|
8817
9051
|
}
|
|
8818
|
-
if (typeof
|
|
9052
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
8819
9053
|
throw new KernelError("EINVAL", "the request header is not an object");
|
|
8820
|
-
const { kind, ...options } =
|
|
9054
|
+
const { kind, ...options } = parsed3;
|
|
8821
9055
|
if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
|
|
8822
9056
|
throw new KernelError("EINVAL", "the request has no valid kind");
|
|
8823
9057
|
return { kind, options };
|
|
@@ -9258,6 +9492,970 @@ function kindsOf(hook) {
|
|
|
9258
9492
|
);
|
|
9259
9493
|
}
|
|
9260
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
|
+
|
|
9261
10459
|
// src/kernel/exec-ticket.ts
|
|
9262
10460
|
var TICKET_ENV = "SLICC_SEALED_TICKET";
|
|
9263
10461
|
var TICKET_TTL_MS = 3e4;
|
|
@@ -9647,8 +10845,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
9647
10845
|
} finally {
|
|
9648
10846
|
clearTimeout(timer);
|
|
9649
10847
|
}
|
|
9650
|
-
const
|
|
9651
|
-
if (
|
|
10848
|
+
const refused5 = driverVersionError(hello.protocol);
|
|
10849
|
+
if (refused5) throw errnoError("EPROTO", refused5);
|
|
9652
10850
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
9653
10851
|
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
|
|
9654
10852
|
}
|
|
@@ -9694,7 +10892,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9694
10892
|
var S_ISGID3 = 1024;
|
|
9695
10893
|
var ID = /^\d{1,10}$/;
|
|
9696
10894
|
var MASK = /^0*[0-7]{1,4}$/;
|
|
9697
|
-
var
|
|
10895
|
+
var MAX_ID3 = 4294967294;
|
|
9698
10896
|
function idOption(value2) {
|
|
9699
10897
|
return value2 === void 0 ? 0 : Number(value2);
|
|
9700
10898
|
}
|
|
@@ -9706,7 +10904,7 @@ function checkIds(options) {
|
|
|
9706
10904
|
for (const key of ["uid", "gid"]) {
|
|
9707
10905
|
const value2 = options[key];
|
|
9708
10906
|
if (value2 === void 0) continue;
|
|
9709
|
-
if (!ID.test(value2) || Number(value2) >
|
|
10907
|
+
if (!ID.test(value2) || Number(value2) > MAX_ID3) throw errnoError("EINVAL", `${key}=${value2}`);
|
|
9710
10908
|
}
|
|
9711
10909
|
}
|
|
9712
10910
|
function checkMasks(options) {
|
|
@@ -9718,7 +10916,7 @@ function checkMasks(options) {
|
|
|
9718
10916
|
}
|
|
9719
10917
|
}
|
|
9720
10918
|
}
|
|
9721
|
-
function
|
|
10919
|
+
function modeOf2(attr2, mount) {
|
|
9722
10920
|
const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
|
|
9723
10921
|
const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
|
|
9724
10922
|
if (mount.caps.owners) return attr2.mode ?? fallback;
|
|
@@ -9879,9 +11077,9 @@ var HeldPaths = class extends Map {
|
|
|
9879
11077
|
this.unlinked.set(path, file);
|
|
9880
11078
|
}
|
|
9881
11079
|
keep(path, file) {
|
|
9882
|
-
const
|
|
9883
|
-
for (const id2 of
|
|
9884
|
-
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));
|
|
9885
11083
|
this.unlinked.set(path, file);
|
|
9886
11084
|
}
|
|
9887
11085
|
unkeep(path, file) {
|
|
@@ -10002,7 +11200,7 @@ function statOf2(attr2, path, mount) {
|
|
|
10002
11200
|
isSymbolicLink: attr2.kind === "symlink",
|
|
10003
11201
|
...attr2.kind === "fifo" ? { isFifo: true } : {},
|
|
10004
11202
|
size: attr2.size,
|
|
10005
|
-
mode:
|
|
11203
|
+
mode: modeOf2(attr2, mount),
|
|
10006
11204
|
mtime,
|
|
10007
11205
|
atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
|
|
10008
11206
|
ctime: mtime,
|
|
@@ -11093,8 +12291,8 @@ var MountFs = class _MountFs {
|
|
|
11093
12291
|
}
|
|
11094
12292
|
this.writable(mount, real);
|
|
11095
12293
|
const { uid, gid, mode } = change;
|
|
11096
|
-
const
|
|
11097
|
-
const set3 =
|
|
12294
|
+
const fresh4 = change.kind === "new" && { uid, gid, mode };
|
|
12295
|
+
const set3 = fresh4 ? await this.driverOwner(found, real, fresh4, false) : change;
|
|
11098
12296
|
this.table.forget(mount, real);
|
|
11099
12297
|
const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
|
|
11100
12298
|
await mount.conn.call({
|
|
@@ -11371,10 +12569,10 @@ async function runMkdir(entry, path, req) {
|
|
|
11371
12569
|
const plain3 = plainDirMode(entry, req.mode);
|
|
11372
12570
|
const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
|
|
11373
12571
|
const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
|
|
11374
|
-
const
|
|
12572
|
+
const fresh4 = plain3 !== void 0 && !await fs.exists(path);
|
|
11375
12573
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
11376
12574
|
await fixParents?.();
|
|
11377
|
-
if (
|
|
12575
|
+
if (fresh4) await fs.chmod(path, plain3);
|
|
11378
12576
|
return done;
|
|
11379
12577
|
}
|
|
11380
12578
|
async function shownAs(entry, path, st) {
|
|
@@ -11586,10 +12784,10 @@ async function guard(g, path, req, cwd) {
|
|
|
11586
12784
|
}
|
|
11587
12785
|
async function opening(entry, path, mode) {
|
|
11588
12786
|
const g = guardOf(entry);
|
|
11589
|
-
const
|
|
12787
|
+
const want2 = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
|
|
11590
12788
|
const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
|
|
11591
|
-
if (g &&
|
|
11592
|
-
await need(g, "open", path,
|
|
12789
|
+
if (g && want2 !== 0 && !created4) {
|
|
12790
|
+
await need(g, "open", path, want2, { directory: false });
|
|
11593
12791
|
}
|
|
11594
12792
|
}
|
|
11595
12793
|
async function stillThere(entry, path, req) {
|
|
@@ -11613,7 +12811,7 @@ async function holdOp(entry, path, req) {
|
|
|
11613
12811
|
await entry.source?.open(path, real);
|
|
11614
12812
|
await stillThere(entry, path, req);
|
|
11615
12813
|
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
11616
|
-
const fid = req.ino === void 0 ? void 0 : await
|
|
12814
|
+
const fid = req.ino === void 0 ? void 0 : await idOf2(entry, real ?? path);
|
|
11617
12815
|
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
11618
12816
|
return json2(await heldOpen(entry, path, real, req, fid));
|
|
11619
12817
|
}
|
|
@@ -11627,7 +12825,7 @@ async function heldOpen(entry, path, real, req, fid) {
|
|
|
11627
12825
|
await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
|
|
11628
12826
|
return real !== void 0 && real !== path ? { handle, real } : handle;
|
|
11629
12827
|
}
|
|
11630
|
-
async function
|
|
12828
|
+
async function idOf2(entry, path) {
|
|
11631
12829
|
return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
|
|
11632
12830
|
}
|
|
11633
12831
|
async function ownEntry(entry, path, real) {
|
|
@@ -11699,8 +12897,8 @@ async function guarded(entry, path, req) {
|
|
|
11699
12897
|
return { paths };
|
|
11700
12898
|
} catch (err) {
|
|
11701
12899
|
const refusal3 = err.code === "EACCES";
|
|
11702
|
-
const
|
|
11703
|
-
return { refused:
|
|
12900
|
+
const refused5 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
|
|
12901
|
+
return { refused: refused5 };
|
|
11704
12902
|
}
|
|
11705
12903
|
}
|
|
11706
12904
|
async function copyOp(entry, path, req) {
|
|
@@ -11801,7 +12999,7 @@ async function bound(entry, path, handle) {
|
|
|
11801
12999
|
const now = await entry.fs.stat(path).catch(() => void 0);
|
|
11802
13000
|
if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
|
|
11803
13001
|
throw syncError("ENOENT", path);
|
|
11804
|
-
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;
|
|
11805
13003
|
if (same2) {
|
|
11806
13004
|
entry.trust?.(handle, bound3.doubt);
|
|
11807
13005
|
return;
|
|
@@ -11896,13 +13094,13 @@ async function created(entry, req) {
|
|
|
11896
13094
|
return seen === void 0 ? looked : json2({ ...looked.json, seen });
|
|
11897
13095
|
}
|
|
11898
13096
|
var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
|
|
11899
|
-
async function rmPermitted(entry, path, req,
|
|
13097
|
+
async function rmPermitted(entry, path, req, refused5) {
|
|
11900
13098
|
const st = await entry.fs.lstat(path).catch(() => void 0);
|
|
11901
|
-
if (!st?.isDirectory) return
|
|
13099
|
+
if (!st?.isDirectory) return refused5;
|
|
11902
13100
|
const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
|
|
11903
13101
|
if ("refused" in unlisted) return unlisted.refused;
|
|
11904
13102
|
const names2 = await entry.fs.readdir(path).catch(() => void 0);
|
|
11905
|
-
if (!names2) return
|
|
13103
|
+
if (!names2) return refused5;
|
|
11906
13104
|
let first;
|
|
11907
13105
|
for (const name2 of names2) {
|
|
11908
13106
|
const child = await dispatchOne(entry, {
|
|
@@ -11953,14 +13151,14 @@ async function newDevName(entry, path, req) {
|
|
|
11953
13151
|
const named = NAMING.has(req.op);
|
|
11954
13152
|
if (!named && !CREATIONS.has(req.op)) return void 0;
|
|
11955
13153
|
const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
|
|
11956
|
-
const
|
|
11957
|
-
if (!
|
|
13154
|
+
const refused5 = devCreation(target, entry.cred?.());
|
|
13155
|
+
if (!refused5) return void 0;
|
|
11958
13156
|
const there = await entry.fs.lstat(target).then(
|
|
11959
13157
|
() => true,
|
|
11960
13158
|
() => false
|
|
11961
13159
|
);
|
|
11962
13160
|
if (there) return void 0;
|
|
11963
|
-
return { ok: false, errno:
|
|
13161
|
+
return { ok: false, errno: refused5, message: `${target}: no new names in /dev or /proc` };
|
|
11964
13162
|
}
|
|
11965
13163
|
var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
|
|
11966
13164
|
async function linkFree(fs, path) {
|
|
@@ -12071,13 +13269,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
|
12071
13269
|
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
12072
13270
|
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
12073
13271
|
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
12074
|
-
var
|
|
13272
|
+
var encoder4 = new TextEncoder();
|
|
12075
13273
|
var decoder2 = new TextDecoder();
|
|
12076
13274
|
function encodeSabResult(result) {
|
|
12077
13275
|
if (!result.ok) {
|
|
12078
13276
|
return {
|
|
12079
13277
|
status: SAB_STATUS_ERRNO,
|
|
12080
|
-
payload:
|
|
13278
|
+
payload: encoder4.encode(
|
|
12081
13279
|
JSON.stringify({
|
|
12082
13280
|
errno: result.errno,
|
|
12083
13281
|
message: result.message,
|
|
@@ -12092,7 +13290,7 @@ function encodeSabResult(result) {
|
|
|
12092
13290
|
case "bytes":
|
|
12093
13291
|
return { status: SAB_STATUS_BYTES, payload: result.bytes };
|
|
12094
13292
|
case "json":
|
|
12095
|
-
return { status: SAB_STATUS_JSON, payload:
|
|
13293
|
+
return { status: SAB_STATUS_JSON, payload: encoder4.encode(JSON.stringify(result.json)) };
|
|
12096
13294
|
default:
|
|
12097
13295
|
return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
|
|
12098
13296
|
}
|
|
@@ -12419,9 +13617,9 @@ var Pages = class {
|
|
|
12419
13617
|
const page = (start2 + at) / PAGE;
|
|
12420
13618
|
if (this.pages.has(page)) continue;
|
|
12421
13619
|
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
12422
|
-
const
|
|
12423
|
-
|
|
12424
|
-
this.pages.set(page,
|
|
13620
|
+
const fresh4 = this.take(part.length);
|
|
13621
|
+
fresh4.set(part);
|
|
13622
|
+
this.pages.set(page, fresh4);
|
|
12425
13623
|
}
|
|
12426
13624
|
}
|
|
12427
13625
|
read(at, length2) {
|
|
@@ -12632,16 +13830,16 @@ var pinnedRoot;
|
|
|
12632
13830
|
var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
|
|
12633
13831
|
function mountTable(mounts = []) {
|
|
12634
13832
|
const root = pinnedRoot ?? rootKind();
|
|
12635
|
-
const
|
|
13833
|
+
const lines3 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
|
|
12636
13834
|
for (const m of mounts) {
|
|
12637
13835
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
12638
13836
|
if (m.state !== "ok") options.push(m.state);
|
|
12639
13837
|
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
12640
|
-
|
|
13838
|
+
lines3.push(
|
|
12641
13839
|
`${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
12642
13840
|
);
|
|
12643
13841
|
}
|
|
12644
|
-
return `${
|
|
13842
|
+
return `${lines3.join("\n")}
|
|
12645
13843
|
`;
|
|
12646
13844
|
}
|
|
12647
13845
|
|
|
@@ -13676,7 +14874,7 @@ function recordRefusal(opening2) {
|
|
|
13676
14874
|
const found = headerClass(opening2);
|
|
13677
14875
|
return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
|
|
13678
14876
|
}
|
|
13679
|
-
function
|
|
14877
|
+
function refused3(engine, session, err, opening2) {
|
|
13680
14878
|
const flags2 = session.verifyResult;
|
|
13681
14879
|
const code = verifyCode(flags2);
|
|
13682
14880
|
if (code && flags2 !== -1)
|
|
@@ -13697,7 +14895,7 @@ async function handshake(conn, o, session, inbox) {
|
|
|
13697
14895
|
done4 = session.handshake();
|
|
13698
14896
|
} catch (err) {
|
|
13699
14897
|
await send(conn, session).catch(() => void 0);
|
|
13700
|
-
throw
|
|
14898
|
+
throw refused3(o.engine, session, err, inbox.opening);
|
|
13701
14899
|
}
|
|
13702
14900
|
await send(conn, session);
|
|
13703
14901
|
if (done4) return;
|
|
@@ -14148,10 +15346,10 @@ async function anchorFor(engine, chain2, host, found) {
|
|
|
14148
15346
|
var FRAGMENT = 16;
|
|
14149
15347
|
var MIN_VALUE = 8;
|
|
14150
15348
|
var MIN_BASE64 = 8;
|
|
14151
|
-
var
|
|
15349
|
+
var encoder5 = new TextEncoder();
|
|
14152
15350
|
var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
14153
15351
|
function utf8(text) {
|
|
14154
|
-
return
|
|
15352
|
+
return encoder5.encode(text);
|
|
14155
15353
|
}
|
|
14156
15354
|
function base64(bytes, padded = true) {
|
|
14157
15355
|
let out = "";
|
|
@@ -14225,14 +15423,73 @@ function encodings(bytes) {
|
|
|
14225
15423
|
}
|
|
14226
15424
|
return [...standard, ...standard.map(urlSafe)];
|
|
14227
15425
|
}
|
|
14228
|
-
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)) {
|
|
14229
15479
|
const raw2 = utf8(value2);
|
|
14230
15480
|
const seen = /* @__PURE__ */ new Set([value2]);
|
|
14231
15481
|
const out = [];
|
|
14232
|
-
if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
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
|
+
});
|
|
14236
15493
|
const add = (text, form) => {
|
|
14237
15494
|
if (seen.has(text)) return;
|
|
14238
15495
|
seen.add(text);
|
|
@@ -14365,12 +15622,19 @@ function prepare(secrets, sameLength) {
|
|
|
14365
15622
|
const ids = /* @__PURE__ */ new Map();
|
|
14366
15623
|
const wrapIds = /* @__PURE__ */ new Map();
|
|
14367
15624
|
const values = secrets.map((secret) => utf8(secret.value));
|
|
15625
|
+
const splits = [];
|
|
14368
15626
|
secrets.forEach((secret, index) => {
|
|
14369
15627
|
if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
|
|
14370
15628
|
throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
|
|
14371
15629
|
}
|
|
14372
|
-
|
|
14373
|
-
|
|
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
|
+
};
|
|
14374
15638
|
register(main, sources, ids, shape.bytes, source);
|
|
14375
15639
|
if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
|
|
14376
15640
|
}
|
|
@@ -14381,7 +15645,16 @@ function prepare(secrets, sameLength) {
|
|
|
14381
15645
|
utf8(secret.placeholder),
|
|
14382
15646
|
utf8(`[redacted:${secret.name}]`)
|
|
14383
15647
|
]);
|
|
14384
|
-
return {
|
|
15648
|
+
return {
|
|
15649
|
+
secrets,
|
|
15650
|
+
splits,
|
|
15651
|
+
main,
|
|
15652
|
+
wrap,
|
|
15653
|
+
sources,
|
|
15654
|
+
wrapSources,
|
|
15655
|
+
replacements,
|
|
15656
|
+
sameLength
|
|
15657
|
+
};
|
|
14385
15658
|
}
|
|
14386
15659
|
function filled(input, start2, end) {
|
|
14387
15660
|
const out = new Uint8Array(end - start2);
|
|
@@ -14412,14 +15685,16 @@ function resolve(c, input, start2, end, source) {
|
|
|
14412
15685
|
growing: false
|
|
14413
15686
|
};
|
|
14414
15687
|
}
|
|
14415
|
-
const
|
|
15688
|
+
const split3 = c.splits[source.secret];
|
|
15689
|
+
const value2 = split3.pieces[source.piece];
|
|
14416
15690
|
let stop = end;
|
|
14417
15691
|
let at = source.offset + FRAGMENT;
|
|
14418
15692
|
while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
|
|
14419
15693
|
stop++;
|
|
14420
15694
|
at++;
|
|
14421
15695
|
}
|
|
14422
|
-
const
|
|
15696
|
+
const whole = split3.whole && source.offset === 0 && at === value2.length;
|
|
15697
|
+
const form = whole ? "raw" : "fragment";
|
|
14423
15698
|
const growing = stop === input.length && at < value2.length;
|
|
14424
15699
|
return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
|
|
14425
15700
|
}
|
|
@@ -14573,9 +15848,9 @@ function compile(secrets, options = {}) {
|
|
|
14573
15848
|
// src/kernel/scrub-file.ts
|
|
14574
15849
|
var SCRUB_IDLE_MS = 50;
|
|
14575
15850
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
14576
|
-
var
|
|
15851
|
+
var encoder6 = new TextEncoder();
|
|
14577
15852
|
function scrubberOf(secrets, sameLength = false) {
|
|
14578
|
-
const usable3 = secrets.filter((s) =>
|
|
15853
|
+
const usable3 = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
|
|
14579
15854
|
return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
|
|
14580
15855
|
}
|
|
14581
15856
|
async function writeAll(file, bytes) {
|
|
@@ -14599,7 +15874,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14599
15874
|
let timer;
|
|
14600
15875
|
let failed5;
|
|
14601
15876
|
const waiters = /* @__PURE__ */ new Set();
|
|
14602
|
-
const
|
|
15877
|
+
const quiet5 = () => !busy && pending === 0;
|
|
14603
15878
|
const wake = () => {
|
|
14604
15879
|
for (const waiter of [...waiters]) waiter();
|
|
14605
15880
|
};
|
|
@@ -14616,7 +15891,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14616
15891
|
if (failed5 === void 0) failed5 = err;
|
|
14617
15892
|
}).finally(() => {
|
|
14618
15893
|
pending -= 1;
|
|
14619
|
-
if (
|
|
15894
|
+
if (quiet5()) wake();
|
|
14620
15895
|
});
|
|
14621
15896
|
};
|
|
14622
15897
|
const settling = (signal) => new Promise((resolve2, reject) => {
|
|
@@ -14634,7 +15909,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14634
15909
|
signal?.addEventListener("abort", stop, { once: true });
|
|
14635
15910
|
});
|
|
14636
15911
|
const settled = async () => {
|
|
14637
|
-
while (!
|
|
15912
|
+
while (!quiet5()) await settling();
|
|
14638
15913
|
};
|
|
14639
15914
|
const changed = async (signal) => {
|
|
14640
15915
|
const round = new AbortController();
|
|
@@ -14650,7 +15925,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14650
15925
|
};
|
|
14651
15926
|
const turn = async (signal) => {
|
|
14652
15927
|
for (let waited = false; ; waited = true) {
|
|
14653
|
-
if (
|
|
15928
|
+
if (quiet5() && (pollFile(file).writable || !file.changed)) {
|
|
14654
15929
|
busy = true;
|
|
14655
15930
|
return;
|
|
14656
15931
|
}
|
|
@@ -14679,13 +15954,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14679
15954
|
...bound2(file, "stat"),
|
|
14680
15955
|
poll: () => {
|
|
14681
15956
|
const state = pollFile(file);
|
|
14682
|
-
return
|
|
15957
|
+
return quiet5() ? state : { ...state, writable: false };
|
|
14683
15958
|
},
|
|
14684
15959
|
changed,
|
|
14685
15960
|
...file.tty ? { tty: file.tty } : {},
|
|
14686
15961
|
identity: file.identity ?? file,
|
|
14687
15962
|
...file.clipboard ? { clipboard: file.clipboard } : {},
|
|
14688
15963
|
...file.embedder ? { embedder: file.embedder } : {},
|
|
15964
|
+
...file.device ? { device: file.device } : {},
|
|
14689
15965
|
...file.write ? {
|
|
14690
15966
|
write: async (bytes, signal) => {
|
|
14691
15967
|
await turn(signal);
|
|
@@ -14697,7 +15973,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
14697
15973
|
await send2(stream2.push(taken), signal);
|
|
14698
15974
|
} finally {
|
|
14699
15975
|
busy = false;
|
|
14700
|
-
if (
|
|
15976
|
+
if (quiet5()) wake();
|
|
14701
15977
|
}
|
|
14702
15978
|
timer = setTimeout(release, idleMs);
|
|
14703
15979
|
return taken.length;
|
|
@@ -14756,9 +16032,9 @@ function scrubStdio(fds, scrubber) {
|
|
|
14756
16032
|
// src/kernel/net/sealed-scrubs.ts
|
|
14757
16033
|
function addTo(tree2, host, secrets) {
|
|
14758
16034
|
const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
|
|
14759
|
-
const
|
|
14760
|
-
if (
|
|
14761
|
-
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];
|
|
14762
16038
|
entry.version += 1;
|
|
14763
16039
|
entry.scrubber = void 0;
|
|
14764
16040
|
tree2.set(host, entry);
|
|
@@ -14921,7 +16197,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
|
|
|
14921
16197
|
var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
14922
16198
|
var TARGET = /^[\x21-\x7e]+$/;
|
|
14923
16199
|
var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
|
|
14924
|
-
var
|
|
16200
|
+
var HOST2 = /^[a-z0-9.:-]+$/;
|
|
14925
16201
|
var UNSAFE = /[\r\n\0]/;
|
|
14926
16202
|
var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
|
|
14927
16203
|
function verifiedByOk(f) {
|
|
@@ -14975,7 +16251,7 @@ function scramOk(f) {
|
|
|
14975
16251
|
return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
|
|
14976
16252
|
}
|
|
14977
16253
|
function hostOk(host) {
|
|
14978
|
-
return typeof host === "string" &&
|
|
16254
|
+
return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
|
|
14979
16255
|
}
|
|
14980
16256
|
function sessionAskable(f, now) {
|
|
14981
16257
|
if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
|
|
@@ -15065,20 +16341,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
|
|
|
15065
16341
|
"caller",
|
|
15066
16342
|
"requester"
|
|
15067
16343
|
]);
|
|
15068
|
-
var
|
|
16344
|
+
var encoder7 = new TextEncoder();
|
|
15069
16345
|
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
15070
16346
|
function parseSealed(bytes) {
|
|
15071
16347
|
try {
|
|
15072
16348
|
if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
|
|
15073
|
-
let
|
|
16349
|
+
let parsed3;
|
|
15074
16350
|
try {
|
|
15075
|
-
|
|
16351
|
+
parsed3 = JSON.parse(decoder3.decode(bytes));
|
|
15076
16352
|
} catch {
|
|
15077
16353
|
throw new KernelError("EINVAL");
|
|
15078
16354
|
}
|
|
15079
|
-
if (typeof
|
|
16355
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
15080
16356
|
throw new KernelError("EINVAL");
|
|
15081
|
-
const req =
|
|
16357
|
+
const req = parsed3;
|
|
15082
16358
|
if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
|
|
15083
16359
|
throw new KernelError("EINVAL");
|
|
15084
16360
|
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
@@ -15174,7 +16450,7 @@ var SealedChannel = class {
|
|
|
15174
16450
|
await this.broker.tickets.record(req, reply, caller.pid);
|
|
15175
16451
|
if (this.inflight !== flight) return;
|
|
15176
16452
|
this.inflight = void 0;
|
|
15177
|
-
if (reply) this.reply =
|
|
16453
|
+
if (reply) this.reply = encoder7.encode(serialized(reply));
|
|
15178
16454
|
});
|
|
15179
16455
|
this.inflight = flight;
|
|
15180
16456
|
return length2;
|
|
@@ -15450,7 +16726,7 @@ function shown(text) {
|
|
|
15450
16726
|
const chars = [...safe];
|
|
15451
16727
|
return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
|
|
15452
16728
|
}
|
|
15453
|
-
function
|
|
16729
|
+
function unescaped2(double) {
|
|
15454
16730
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
15455
16731
|
}
|
|
15456
16732
|
function splitWords(text) {
|
|
@@ -15466,7 +16742,7 @@ function splitWords(text) {
|
|
|
15466
16742
|
word = void 0;
|
|
15467
16743
|
continue;
|
|
15468
16744
|
}
|
|
15469
|
-
word = (word ?? "") + (single ?? (double !== void 0 ?
|
|
16745
|
+
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
|
|
15470
16746
|
}
|
|
15471
16747
|
if (word !== void 0) words3.push(word);
|
|
15472
16748
|
return words3;
|
|
@@ -15503,7 +16779,7 @@ function splitPatterns(text) {
|
|
|
15503
16779
|
}
|
|
15504
16780
|
word ??= [];
|
|
15505
16781
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
15506
|
-
else addPart(word, single ?? (double !== void 0 ?
|
|
16782
|
+
else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
|
|
15507
16783
|
}
|
|
15508
16784
|
if (word !== void 0) words3.push(word);
|
|
15509
16785
|
return words3;
|
|
@@ -15554,13 +16830,13 @@ function commandMatches(cmnd, argv) {
|
|
|
15554
16830
|
if (cmnd === "*") return true;
|
|
15555
16831
|
const [path, ...args] = argv;
|
|
15556
16832
|
if (path !== cmnd.path) return false;
|
|
15557
|
-
const
|
|
15558
|
-
if (
|
|
16833
|
+
const want2 = cmnd.args;
|
|
16834
|
+
if (want2 === void 0) return true;
|
|
15559
16835
|
return globMatch(
|
|
15560
|
-
|
|
16836
|
+
want2.length,
|
|
15561
16837
|
args.length,
|
|
15562
|
-
(i) => isAny(
|
|
15563
|
-
(i, j) => wordMatch(
|
|
16838
|
+
(i) => isAny(want2[i]),
|
|
16839
|
+
(i, j) => wordMatch(want2[i], args[j])
|
|
15564
16840
|
);
|
|
15565
16841
|
}
|
|
15566
16842
|
function commandText(cmnd) {
|
|
@@ -15857,11 +17133,11 @@ function parsePolicy(text, file) {
|
|
|
15857
17133
|
const defaults2 = [];
|
|
15858
17134
|
const errors = [];
|
|
15859
17135
|
for (const [n, line] of text.split("\n").entries()) {
|
|
15860
|
-
const
|
|
15861
|
-
if (
|
|
15862
|
-
if ("error" in
|
|
15863
|
-
else if ("defaults" in
|
|
15864
|
-
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);
|
|
15865
17141
|
}
|
|
15866
17142
|
return { rules, defaults: defaults2, errors };
|
|
15867
17143
|
}
|
|
@@ -15881,18 +17157,18 @@ var HOME_DIR = /^\/home\/[^/]+$/;
|
|
|
15881
17157
|
var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
|
|
15882
17158
|
var PNPM_CONFIG = "minimumReleaseAge: 1440\n";
|
|
15883
17159
|
var isId = (text) => text !== void 0 && /^\d+$/.test(text);
|
|
15884
|
-
function
|
|
17160
|
+
function lines2(text) {
|
|
15885
17161
|
return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
|
|
15886
17162
|
}
|
|
15887
17163
|
function parsePasswd(text) {
|
|
15888
|
-
return
|
|
17164
|
+
return lines2(text).map((line) => {
|
|
15889
17165
|
const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
|
|
15890
17166
|
if (!name2 || !isId(uid) || !isId(gid)) return line;
|
|
15891
17167
|
return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
|
|
15892
17168
|
});
|
|
15893
17169
|
}
|
|
15894
17170
|
function parseGroup(text) {
|
|
15895
|
-
return
|
|
17171
|
+
return lines2(text).map((line) => {
|
|
15896
17172
|
const [name2, , gid, members = ""] = line.split(":");
|
|
15897
17173
|
if (!name2 || !isId(gid)) return line;
|
|
15898
17174
|
return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
|
|
@@ -15908,7 +17184,7 @@ function formatGroup(rows) {
|
|
|
15908
17184
|
return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
|
|
15909
17185
|
`;
|
|
15910
17186
|
}
|
|
15911
|
-
var
|
|
17187
|
+
var parsed2 = (rows) => rows.filter((r) => typeof r !== "string");
|
|
15912
17188
|
function homeOf(home) {
|
|
15913
17189
|
if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
|
|
15914
17190
|
throw fsError("EINVAL", `not a home directory: ${String(home)}`);
|
|
@@ -16101,20 +17377,20 @@ var UserDb = class {
|
|
|
16101
17377
|
return (await this.lookupTerminal(user))?.account;
|
|
16102
17378
|
}
|
|
16103
17379
|
async ttyGroup() {
|
|
16104
|
-
return
|
|
17380
|
+
return parsed2(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
|
|
16105
17381
|
}
|
|
16106
17382
|
async lookupTerminal(user) {
|
|
16107
17383
|
const { passwd, group } = await this.tables();
|
|
16108
17384
|
const key = typeof user === "number" ? "uid" : "name";
|
|
16109
|
-
const row =
|
|
16110
|
-
const groups =
|
|
17385
|
+
const row = parsed2(passwd).find((r) => r[key] === user);
|
|
17386
|
+
const groups = parsed2(group);
|
|
16111
17387
|
const tty = groups.find((g) => g.name === "tty")?.gid;
|
|
16112
17388
|
return row && { account: this.account(row, groups), tty };
|
|
16113
17389
|
}
|
|
16114
17390
|
async list() {
|
|
16115
17391
|
const { passwd, group } = await this.tables();
|
|
16116
|
-
const groups =
|
|
16117
|
-
return
|
|
17392
|
+
const groups = parsed2(group);
|
|
17393
|
+
return parsed2(passwd).map((row) => this.account(row, groups));
|
|
16118
17394
|
}
|
|
16119
17395
|
add(options) {
|
|
16120
17396
|
return this.serial(async () => {
|
|
@@ -16126,8 +17402,8 @@ var UserDb = class {
|
|
|
16126
17402
|
if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
|
|
16127
17403
|
};
|
|
16128
17404
|
const { passwd, group } = await this.tables();
|
|
16129
|
-
const users =
|
|
16130
|
-
const groups =
|
|
17405
|
+
const users = parsed2(passwd);
|
|
17406
|
+
const groups = parsed2(group);
|
|
16131
17407
|
const found = users.find((r) => r.name === name2);
|
|
16132
17408
|
const shared2 = homeGroupOf(groups, options.homeGroup);
|
|
16133
17409
|
const homeMode = homeModeOf(options.homeMode);
|
|
@@ -16152,7 +17428,7 @@ var UserDb = class {
|
|
|
16152
17428
|
joined3(name2, extra);
|
|
16153
17429
|
group.push({ name: name2, gid: uid, members: [] });
|
|
16154
17430
|
passwd.push(row);
|
|
16155
|
-
const
|
|
17431
|
+
const fresh4 = !await this.fs.exists(home);
|
|
16156
17432
|
await this.fs.mkdir(home, { recursive: true });
|
|
16157
17433
|
try {
|
|
16158
17434
|
await this.provision(row, this.umaskFor(options), shared2);
|
|
@@ -16160,16 +17436,16 @@ var UserDb = class {
|
|
|
16160
17436
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
16161
17437
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
16162
17438
|
} catch (err) {
|
|
16163
|
-
if (
|
|
17439
|
+
if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
|
|
16164
17440
|
throw err;
|
|
16165
17441
|
}
|
|
16166
17442
|
remember();
|
|
16167
|
-
return this.account(row,
|
|
17443
|
+
return this.account(row, parsed2(group));
|
|
16168
17444
|
});
|
|
16169
17445
|
}
|
|
16170
17446
|
async groups() {
|
|
16171
17447
|
const { group } = await this.tables();
|
|
16172
|
-
return
|
|
17448
|
+
return parsed2(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
|
|
16173
17449
|
}
|
|
16174
17450
|
addGroup(name2, gid) {
|
|
16175
17451
|
return this.serial(async () => {
|
|
@@ -16180,7 +17456,7 @@ var UserDb = class {
|
|
|
16180
17456
|
throw fsError("EINVAL", `not a group id: ${String(gid)}`);
|
|
16181
17457
|
}
|
|
16182
17458
|
const { group } = await this.tables();
|
|
16183
|
-
const rows =
|
|
17459
|
+
const rows = parsed2(group);
|
|
16184
17460
|
const found = rows.find((g) => g.name === name2);
|
|
16185
17461
|
if (found) return { name: found.name, gid: found.gid, members: [...found.members] };
|
|
16186
17462
|
const taken = rows.find((g) => g.gid === gid);
|
|
@@ -16200,9 +17476,9 @@ var UserDb = class {
|
|
|
16200
17476
|
member(name2, user, on) {
|
|
16201
17477
|
return this.serial(async () => {
|
|
16202
17478
|
const { passwd, group } = await this.tables();
|
|
16203
|
-
const row =
|
|
17479
|
+
const row = parsed2(group).find((g) => g.name === name2);
|
|
16204
17480
|
if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
|
|
16205
|
-
const who =
|
|
17481
|
+
const who = parsed2(passwd).find((u) => u.name === user);
|
|
16206
17482
|
if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
16207
17483
|
if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
|
|
16208
17484
|
if (row.members.includes(user) !== on) {
|
|
@@ -16215,7 +17491,7 @@ var UserDb = class {
|
|
|
16215
17491
|
remove(name2, options = {}) {
|
|
16216
17492
|
return this.serial(async () => {
|
|
16217
17493
|
const { passwd, group } = await this.tables();
|
|
16218
|
-
const row =
|
|
17494
|
+
const row = parsed2(passwd).find((r) => r.name === name2);
|
|
16219
17495
|
if (!row) return false;
|
|
16220
17496
|
if (row.uid < FIRST_UID || row.uid > LAST_UID) {
|
|
16221
17497
|
throw fsError("EPERM", `${name2} is a system account`);
|
|
@@ -16223,9 +17499,9 @@ var UserDb = class {
|
|
|
16223
17499
|
const keptGroups = group.filter(
|
|
16224
17500
|
(r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
|
|
16225
17501
|
);
|
|
16226
|
-
for (const r of
|
|
17502
|
+
for (const r of parsed2(keptGroups)) r.members = r.members.filter((m) => m !== name2);
|
|
16227
17503
|
const home = this.fs.resolvePath("/", row.home);
|
|
16228
|
-
const shared2 =
|
|
17504
|
+
const shared2 = parsed2(passwd).some(
|
|
16229
17505
|
(r) => r !== row && this.fs.resolvePath("/", r.home) === home
|
|
16230
17506
|
);
|
|
16231
17507
|
const removeHome = !options.keepHome && !shared2 && HOME_DIR.test(home) && await this.fs.exists(home);
|
|
@@ -16291,11 +17567,11 @@ var REPLAY_TAGS_MAX = 64;
|
|
|
16291
17567
|
var REPLAY_ARGV_MAX = 4096;
|
|
16292
17568
|
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
16293
17569
|
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
16294
|
-
function
|
|
17570
|
+
function strings2(value2, max) {
|
|
16295
17571
|
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
16296
17572
|
}
|
|
16297
17573
|
function words(value2) {
|
|
16298
|
-
if (!
|
|
17574
|
+
if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
16299
17575
|
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
16300
17576
|
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
16301
17577
|
}
|
|
@@ -16330,7 +17606,7 @@ function replayOf(value2) {
|
|
|
16330
17606
|
const bound3 = filesOf(r.userWritable);
|
|
16331
17607
|
const named = (s) => typeof s === "string" && s !== "";
|
|
16332
17608
|
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
16333
|
-
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;
|
|
16334
17610
|
if (!valid2) return failed2("EINVAL", NOT_A_REQUEST);
|
|
16335
17611
|
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
16336
17612
|
return {
|
|
@@ -16422,28 +17698,28 @@ function recordedCommand(argv) {
|
|
|
16422
17698
|
const [path = "", ...args] = argv;
|
|
16423
17699
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
16424
17700
|
}
|
|
16425
|
-
function flags(
|
|
17701
|
+
function flags(parsed3, arg, next3) {
|
|
16426
17702
|
for (let j = 1; j < arg.length; j++) {
|
|
16427
17703
|
const flag = arg[j];
|
|
16428
|
-
if (flag === "n")
|
|
16429
|
-
else if (flag === "l")
|
|
16430
|
-
else if (flag === "v")
|
|
16431
|
-
else if (flag === "k")
|
|
16432
|
-
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;
|
|
16433
17709
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
16434
17710
|
${USAGE}`;
|
|
16435
17711
|
else {
|
|
16436
17712
|
const user = arg.slice(j + 1) || next3;
|
|
16437
17713
|
if (!user) return `sudo: option requires an argument -- 'u'
|
|
16438
17714
|
${USAGE}`;
|
|
16439
|
-
|
|
17715
|
+
parsed3.user = user;
|
|
16440
17716
|
return arg.slice(j + 1) ? 0 : 1;
|
|
16441
17717
|
}
|
|
16442
17718
|
}
|
|
16443
17719
|
return 0;
|
|
16444
17720
|
}
|
|
16445
17721
|
function parseArgs(args) {
|
|
16446
|
-
const
|
|
17722
|
+
const parsed3 = {
|
|
16447
17723
|
list: false,
|
|
16448
17724
|
nonInteractive: false,
|
|
16449
17725
|
validate: false,
|
|
@@ -16460,19 +17736,19 @@ function parseArgs(args) {
|
|
|
16460
17736
|
break;
|
|
16461
17737
|
}
|
|
16462
17738
|
if (!arg.startsWith("-") || arg === "-") break;
|
|
16463
|
-
const used = flags(
|
|
17739
|
+
const used = flags(parsed3, arg, args[i + 1]);
|
|
16464
17740
|
if (typeof used === "string") return used;
|
|
16465
17741
|
i += used;
|
|
16466
17742
|
}
|
|
16467
|
-
|
|
16468
|
-
return usable(
|
|
17743
|
+
parsed3.command = args.slice(i);
|
|
17744
|
+
return usable(parsed3) ? parsed3 : USAGE;
|
|
16469
17745
|
}
|
|
16470
|
-
function usable(
|
|
16471
|
-
const command =
|
|
16472
|
-
const modes = [
|
|
16473
|
-
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;
|
|
16474
17750
|
if (modes === 1) return !command;
|
|
16475
|
-
return command ||
|
|
17751
|
+
return command || parsed3.reset;
|
|
16476
17752
|
}
|
|
16477
17753
|
function policyOf(settings) {
|
|
16478
17754
|
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
@@ -16616,28 +17892,28 @@ var Sudo = class {
|
|
|
16616
17892
|
};
|
|
16617
17893
|
}
|
|
16618
17894
|
async check(cred, call) {
|
|
16619
|
-
const
|
|
17895
|
+
const lines3 = [];
|
|
16620
17896
|
let ok4 = true;
|
|
16621
17897
|
const report = ({ path, problems, ok: read }) => {
|
|
16622
17898
|
for (const problem of problems) {
|
|
16623
17899
|
const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
|
|
16624
|
-
|
|
17900
|
+
lines3.push([2, `visudo: ${where} ${problem.message}`]);
|
|
16625
17901
|
}
|
|
16626
|
-
if (read && problems.length === 0)
|
|
17902
|
+
if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
|
|
16627
17903
|
if (problems.length > 0) ok4 = false;
|
|
16628
17904
|
};
|
|
16629
17905
|
const values = (defaults2) => {
|
|
16630
|
-
for (const line of defaultsText(defaults2))
|
|
17906
|
+
for (const line of defaultsText(defaults2)) lines3.push([1, line]);
|
|
16631
17907
|
};
|
|
16632
17908
|
if (call.files !== void 0) {
|
|
16633
17909
|
const defaults2 = [];
|
|
16634
17910
|
for (const { name: name2, text } of call.files) {
|
|
16635
|
-
const
|
|
16636
|
-
defaults2.push(...
|
|
16637
|
-
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 });
|
|
16638
17914
|
}
|
|
16639
17915
|
if (defaults2.length > 0) values(defaults2);
|
|
16640
|
-
return { lines:
|
|
17916
|
+
return { lines: lines3, ok: ok4 };
|
|
16641
17917
|
}
|
|
16642
17918
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
16643
17919
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
@@ -16646,7 +17922,7 @@ var Sudo = class {
|
|
|
16646
17922
|
const files = await this.policy();
|
|
16647
17923
|
for (const file of files) report(file);
|
|
16648
17924
|
values(files.flatMap((f) => f.defaults ?? []));
|
|
16649
|
-
return { lines:
|
|
17925
|
+
return { lines: lines3, ok: ok4 };
|
|
16650
17926
|
}
|
|
16651
17927
|
async groupIds() {
|
|
16652
17928
|
return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
|
|
@@ -16711,13 +17987,13 @@ var Sudo = class {
|
|
|
16711
17987
|
return void 0;
|
|
16712
17988
|
}
|
|
16713
17989
|
async request(caller, call, signal) {
|
|
16714
|
-
const
|
|
16715
|
-
if (typeof
|
|
17990
|
+
const parsed3 = parseArgs(call.args);
|
|
17991
|
+
if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
|
|
16716
17992
|
if (this.o.mode !== "enforce") {
|
|
16717
17993
|
const mode = this.o.mode;
|
|
16718
|
-
if (
|
|
17994
|
+
if (parsed3.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
16719
17995
|
` };
|
|
16720
|
-
return
|
|
17996
|
+
return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
|
|
16721
17997
|
}
|
|
16722
17998
|
const { cred } = caller;
|
|
16723
17999
|
const policy = await this.policyRules(caller);
|
|
@@ -16730,22 +18006,22 @@ var Sudo = class {
|
|
|
16730
18006
|
};
|
|
16731
18007
|
const applies = await this.applies(self, cred);
|
|
16732
18008
|
const settings = resolveDefaults(policy.defaults, applies);
|
|
16733
|
-
if (
|
|
18009
|
+
if (parsed3.removeAll) {
|
|
16734
18010
|
this.timestamps.remove(owner.uid);
|
|
16735
18011
|
return DONE;
|
|
16736
18012
|
}
|
|
16737
|
-
if (
|
|
18013
|
+
if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
|
|
16738
18014
|
this.timestamps.invalidate(owner, settings.timestampType);
|
|
16739
18015
|
return DONE;
|
|
16740
18016
|
}
|
|
16741
|
-
const target = await this.lookup(
|
|
16742
|
-
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}`);
|
|
16743
18019
|
const root = cred.euid === 0;
|
|
16744
18020
|
const rules = root ? [] : policy.rules;
|
|
16745
18021
|
const mine = rules.filter((rule) => applies(rule.who));
|
|
16746
|
-
if (
|
|
16747
|
-
const session = { caller, self, target, owner, policy: policyOf(settings), parsed:
|
|
16748
|
-
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);
|
|
16749
18025
|
return this.command(session, root, mine, call);
|
|
16750
18026
|
}
|
|
16751
18027
|
async replay(value2) {
|
|
@@ -16804,9 +18080,9 @@ var Sudo = class {
|
|
|
16804
18080
|
);
|
|
16805
18081
|
}
|
|
16806
18082
|
async command(s, root, mine, call) {
|
|
16807
|
-
const { caller, self, target, parsed:
|
|
18083
|
+
const { caller, self, target, parsed: parsed3 } = s;
|
|
16808
18084
|
const { cred } = caller;
|
|
16809
|
-
const [word = "", ...args] =
|
|
18085
|
+
const [word = "", ...args] = parsed3.command;
|
|
16810
18086
|
const cwd = await this.canonical(call.cwd);
|
|
16811
18087
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
16812
18088
|
return failed2(
|
|
@@ -16821,7 +18097,7 @@ var Sudo = class {
|
|
|
16821
18097
|
const matching = mine.filter(
|
|
16822
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)
|
|
16823
18099
|
);
|
|
16824
|
-
const grant = () => this.grant(self, target, cred,
|
|
18100
|
+
const grant = () => this.grant(self, target, cred, parsed3.command, path, cwd, call.term, bound3);
|
|
16825
18101
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
16826
18102
|
const quoted = argv.map(quoteWord).join(" ");
|
|
16827
18103
|
if (matching.length === 0) {
|
|
@@ -16830,38 +18106,38 @@ var Sudo = class {
|
|
|
16830
18106
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
16831
18107
|
);
|
|
16832
18108
|
}
|
|
16833
|
-
if (!
|
|
18109
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
|
|
16834
18110
|
this.timestamps.stamp(s.owner, s.policy, bound3);
|
|
16835
18111
|
this.logged(s, cwd, argv);
|
|
16836
18112
|
return grant();
|
|
16837
18113
|
}
|
|
16838
|
-
if (
|
|
18114
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16839
18115
|
return this.pendingFor(cred.ruid, async () => {
|
|
16840
|
-
const answer2 = await this.ask(s, { argv:
|
|
18116
|
+
const answer2 = await this.ask(s, { argv: parsed3.command, path, cwd, bound: bound3 });
|
|
16841
18117
|
if ("error" in answer2) return answer2;
|
|
16842
18118
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
16843
18119
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
16844
18120
|
}
|
|
16845
18121
|
const now = await this.bound(path, cwd);
|
|
16846
18122
|
if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
16847
|
-
const shown2 = recordable(cwd, [...
|
|
18123
|
+
const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
|
|
16848
18124
|
const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
|
|
16849
18125
|
if (!("error" in result)) this.stamped(s, answer2, bound3);
|
|
16850
18126
|
return result;
|
|
16851
18127
|
});
|
|
16852
18128
|
}
|
|
16853
18129
|
async validate(s, root, mine, dir) {
|
|
16854
|
-
const { self, parsed:
|
|
18130
|
+
const { self, parsed: parsed3 } = s;
|
|
16855
18131
|
if (root) return DONE;
|
|
16856
18132
|
if (mine.length === 0) {
|
|
16857
18133
|
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
16858
18134
|
}
|
|
16859
18135
|
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
16860
|
-
if (!
|
|
18136
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
16861
18137
|
this.timestamps.stamp(s.owner, s.policy);
|
|
16862
18138
|
return DONE;
|
|
16863
18139
|
}
|
|
16864
|
-
if (
|
|
18140
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16865
18141
|
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
16866
18142
|
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
16867
18143
|
const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
|
|
@@ -16916,7 +18192,7 @@ var Sudo = class {
|
|
|
16916
18192
|
}
|
|
16917
18193
|
const shown2 = [];
|
|
16918
18194
|
for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
|
|
16919
|
-
const
|
|
18195
|
+
const lines3 = shown2.join("\n");
|
|
16920
18196
|
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
16921
18197
|
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
16922
18198
|
${params}
|
|
@@ -16924,7 +18200,7 @@ var Sudo = class {
|
|
|
16924
18200
|
`;
|
|
16925
18201
|
return {
|
|
16926
18202
|
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
16927
|
-
${
|
|
18203
|
+
${lines3}
|
|
16928
18204
|
`
|
|
16929
18205
|
};
|
|
16930
18206
|
}
|
|
@@ -17341,7 +18617,7 @@ async function epollSyscall(req, proc) {
|
|
|
17341
18617
|
|
|
17342
18618
|
// src/kernel/locks.ts
|
|
17343
18619
|
var overlaps = (a, b) => a.start < b.end && b.start < a.end;
|
|
17344
|
-
var clashes = (lock, owner,
|
|
18620
|
+
var clashes = (lock, owner, want2) => lock.owner !== owner && overlaps(lock, want2) && (lock.kind === "write" || want2.kind === "write");
|
|
17345
18621
|
function lockRange(start2, len) {
|
|
17346
18622
|
if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
|
|
17347
18623
|
const from = len < 0 ? start2 + len : start2;
|
|
@@ -17427,8 +18703,8 @@ var LockTable = class {
|
|
|
17427
18703
|
blocked = /* @__PURE__ */ new Map();
|
|
17428
18704
|
wakers = /* @__PURE__ */ new Map();
|
|
17429
18705
|
held = /* @__PURE__ */ new Map();
|
|
17430
|
-
holder(key, owner,
|
|
17431
|
-
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));
|
|
17432
18708
|
}
|
|
17433
18709
|
set(key, owner, kind, start2, end) {
|
|
17434
18710
|
if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
|
|
@@ -17447,10 +18723,10 @@ var LockTable = class {
|
|
|
17447
18723
|
return true;
|
|
17448
18724
|
}
|
|
17449
18725
|
async wait(key, owner, kind, range2, blocking) {
|
|
17450
|
-
const
|
|
18726
|
+
const want2 = { key, kind, ...range2 };
|
|
17451
18727
|
let signal;
|
|
17452
18728
|
for (; ; ) {
|
|
17453
|
-
const holder = this.holder(key, owner,
|
|
18729
|
+
const holder = this.holder(key, owner, want2);
|
|
17454
18730
|
if (!holder) {
|
|
17455
18731
|
this.set(key, owner, kind, range2.start, range2.end);
|
|
17456
18732
|
return;
|
|
@@ -17458,7 +18734,7 @@ var LockTable = class {
|
|
|
17458
18734
|
if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
|
|
17459
18735
|
signal ??= blocking();
|
|
17460
18736
|
if (signal.aborted) throw new KernelError("EINTR");
|
|
17461
|
-
this.blocked.set(owner,
|
|
18737
|
+
this.blocked.set(owner, want2);
|
|
17462
18738
|
try {
|
|
17463
18739
|
await this.changed(key, signal);
|
|
17464
18740
|
} finally {
|
|
@@ -17470,9 +18746,9 @@ var LockTable = class {
|
|
|
17470
18746
|
const seen = /* @__PURE__ */ new Set();
|
|
17471
18747
|
for (let at = from; at !== void 0 && !seen.has(at); ) {
|
|
17472
18748
|
seen.add(at);
|
|
17473
|
-
const
|
|
17474
|
-
if (!
|
|
17475
|
-
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);
|
|
17476
18752
|
if (!holder) return false;
|
|
17477
18753
|
if (holder.owner === target) return true;
|
|
17478
18754
|
at = holder.owner;
|
|
@@ -17837,24 +19113,24 @@ function errno2(code, message3) {
|
|
|
17837
19113
|
}
|
|
17838
19114
|
function requestHead(method, url, headers, extra, first = []) {
|
|
17839
19115
|
if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
|
|
17840
|
-
const
|
|
17841
|
-
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}`);
|
|
17842
19118
|
for (const [name2, value2] of headers) {
|
|
17843
19119
|
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
17844
19120
|
if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
|
|
17845
|
-
|
|
19121
|
+
lines3.push(`${name2}: ${value2}`);
|
|
17846
19122
|
}
|
|
17847
|
-
for (const [name2, value2] of extra)
|
|
17848
|
-
return latin1Bytes(`${
|
|
19123
|
+
for (const [name2, value2] of extra) lines3.push(`${name2}: ${value2}`);
|
|
19124
|
+
return latin1Bytes(`${lines3.join("\r\n")}\r
|
|
17849
19125
|
\r
|
|
17850
19126
|
`);
|
|
17851
19127
|
}
|
|
17852
19128
|
function parseHead(bytes) {
|
|
17853
|
-
const [statusLine = "", ...
|
|
19129
|
+
const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
|
|
17854
19130
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
17855
19131
|
if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
17856
19132
|
const headers = [];
|
|
17857
|
-
for (const line of
|
|
19133
|
+
for (const line of lines3) {
|
|
17858
19134
|
const colon = line.indexOf(":");
|
|
17859
19135
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
17860
19136
|
}
|
|
@@ -17944,8 +19220,8 @@ var KernelLoopback = class {
|
|
|
17944
19220
|
);
|
|
17945
19221
|
} catch (err) {
|
|
17946
19222
|
socket.close();
|
|
17947
|
-
const
|
|
17948
|
-
throw
|
|
19223
|
+
const refused5 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
19224
|
+
throw refused5 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
17949
19225
|
}
|
|
17950
19226
|
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
17951
19227
|
return socket;
|
|
@@ -18334,9 +19610,9 @@ function pem(der2) {
|
|
|
18334
19610
|
for (let i = 0; i < der2.length; i += 32768) {
|
|
18335
19611
|
b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
|
|
18336
19612
|
}
|
|
18337
|
-
const
|
|
19613
|
+
const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
|
|
18338
19614
|
return `-----BEGIN CERTIFICATE-----
|
|
18339
|
-
${
|
|
19615
|
+
${lines3.join("\n")}
|
|
18340
19616
|
-----END CERTIFICATE-----
|
|
18341
19617
|
`;
|
|
18342
19618
|
}
|
|
@@ -19075,13 +20351,13 @@ var HOSTS_PATH = "/etc/hosts";
|
|
|
19075
20351
|
function parseHosts(text) {
|
|
19076
20352
|
const table = /* @__PURE__ */ new Map();
|
|
19077
20353
|
for (const raw2 of text.split("\n")) {
|
|
19078
|
-
const [
|
|
19079
|
-
if (!
|
|
19080
|
-
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;
|
|
19081
20357
|
for (const name2 of names2) {
|
|
19082
20358
|
const key = name2.toLowerCase().replace(/\.$/, "");
|
|
19083
20359
|
const known = table.get(key) ?? [];
|
|
19084
|
-
if (!known.includes(
|
|
20360
|
+
if (!known.includes(address2)) known.push(address2);
|
|
19085
20361
|
table.set(key, known);
|
|
19086
20362
|
}
|
|
19087
20363
|
}
|
|
@@ -19169,14 +20445,14 @@ var Resolver = class {
|
|
|
19169
20445
|
if (hostsEntry(key, this.own) !== void 0) return true;
|
|
19170
20446
|
return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
|
|
19171
20447
|
}
|
|
19172
|
-
async reverse(
|
|
19173
|
-
const v6 = ipv6Groups(
|
|
19174
|
-
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;
|
|
19175
20451
|
const file = await this.hosts?.().catch(() => void 0);
|
|
19176
20452
|
for (const table of [file, this.builtin]) {
|
|
19177
20453
|
for (const [name2, addresses] of table ?? []) if (addresses.some(same2)) return name2;
|
|
19178
20454
|
}
|
|
19179
|
-
if (ipv4Bytes(
|
|
20455
|
+
if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
|
|
19180
20456
|
return void 0;
|
|
19181
20457
|
}
|
|
19182
20458
|
async resolve(name2, family, blocking) {
|
|
@@ -19230,13 +20506,13 @@ var Resolver = class {
|
|
|
19230
20506
|
if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
|
|
19231
20507
|
return addresses;
|
|
19232
20508
|
}
|
|
19233
|
-
usable(name2,
|
|
19234
|
-
const v4 = ipv4Bytes(
|
|
19235
|
-
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;
|
|
19236
20512
|
if (v4 && family === 6) return false;
|
|
19237
|
-
const kind = this.routes.classify(
|
|
20513
|
+
const kind = this.routes.classify(address2);
|
|
19238
20514
|
if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
|
|
19239
|
-
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}`);
|
|
19240
20516
|
return false;
|
|
19241
20517
|
}
|
|
19242
20518
|
remember(id2, addresses, ttl) {
|
|
@@ -19270,275 +20546,6 @@ function waitFor(lookup4, signal) {
|
|
|
19270
20546
|
});
|
|
19271
20547
|
}
|
|
19272
20548
|
|
|
19273
|
-
// src/kernel/tty.ts
|
|
19274
|
-
var ICRNL = 256;
|
|
19275
|
-
var IUTF8 = 16384;
|
|
19276
|
-
var OPOST = 1;
|
|
19277
|
-
var ONLCR = 4;
|
|
19278
|
-
var ISIG = 1;
|
|
19279
|
-
var ICANON = 2;
|
|
19280
|
-
var ECHO = 8;
|
|
19281
|
-
var ECHOE = 16;
|
|
19282
|
-
var ECHOCTL = 512;
|
|
19283
|
-
var NOFLSH = 128;
|
|
19284
|
-
var IEXTEN = 32768;
|
|
19285
|
-
var VINTR = 0;
|
|
19286
|
-
var VQUIT = 1;
|
|
19287
|
-
var VERASE = 2;
|
|
19288
|
-
var VKILL = 3;
|
|
19289
|
-
var VEOF = 4;
|
|
19290
|
-
var VSUSP = 10;
|
|
19291
|
-
var VWERASE = 14;
|
|
19292
|
-
function defaultTermios() {
|
|
19293
|
-
const cCc = new Array(32).fill(0);
|
|
19294
|
-
Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
|
|
19295
|
-
Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
|
|
19296
|
-
Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
|
|
19297
|
-
return {
|
|
19298
|
-
c_iflag: ICRNL | 1024 | 8192 | IUTF8,
|
|
19299
|
-
c_oflag: OPOST | ONLCR,
|
|
19300
|
-
c_cflag: 15 | 48 | 128,
|
|
19301
|
-
c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
|
|
19302
|
-
c_cc: cCc
|
|
19303
|
-
};
|
|
19304
|
-
}
|
|
19305
|
-
var encoder7 = new TextEncoder();
|
|
19306
|
-
function isContinuation(byte) {
|
|
19307
|
-
return (byte & 192) === 128;
|
|
19308
|
-
}
|
|
19309
|
-
var KernelTty = class {
|
|
19310
|
-
termios = defaultTermios();
|
|
19311
|
-
cols = 80;
|
|
19312
|
-
rows = 24;
|
|
19313
|
-
readable = [];
|
|
19314
|
-
line = [];
|
|
19315
|
-
waiters = [];
|
|
19316
|
-
hungUp = false;
|
|
19317
|
-
name;
|
|
19318
|
-
owner;
|
|
19319
|
-
onRead;
|
|
19320
|
-
readers = 0;
|
|
19321
|
-
screen;
|
|
19322
|
-
signal;
|
|
19323
|
-
constructor(screen, signal) {
|
|
19324
|
-
this.screen = screen;
|
|
19325
|
-
this.signal = signal;
|
|
19326
|
-
}
|
|
19327
|
-
receive(bytes) {
|
|
19328
|
-
for (const byte of bytes) this.key(byte);
|
|
19329
|
-
this.wake();
|
|
19330
|
-
}
|
|
19331
|
-
resize(cols, rows) {
|
|
19332
|
-
if (cols === this.cols && rows === this.rows) return;
|
|
19333
|
-
this.setSize(cols, rows);
|
|
19334
|
-
this.signal(SIG.WINCH);
|
|
19335
|
-
}
|
|
19336
|
-
hangup() {
|
|
19337
|
-
this.hungUp = true;
|
|
19338
|
-
this.wake();
|
|
19339
|
-
}
|
|
19340
|
-
signalHangup() {
|
|
19341
|
-
this.signal(SIG.HUP);
|
|
19342
|
-
}
|
|
19343
|
-
setSize(cols, rows) {
|
|
19344
|
-
this.cols = cols;
|
|
19345
|
-
this.rows = rows;
|
|
19346
|
-
}
|
|
19347
|
-
tcgets() {
|
|
19348
|
-
return { ...this.termios, c_cc: [...this.termios.c_cc] };
|
|
19349
|
-
}
|
|
19350
|
-
tcsets(termios, flush = false) {
|
|
19351
|
-
const wasCanonical = this.canonical;
|
|
19352
|
-
this.termios = { ...termios, c_cc: [...termios.c_cc] };
|
|
19353
|
-
if (flush) {
|
|
19354
|
-
this.line = [];
|
|
19355
|
-
this.readable = [];
|
|
19356
|
-
}
|
|
19357
|
-
if (wasCanonical && !this.canonical && this.line.length > 0) {
|
|
19358
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
19359
|
-
this.line = [];
|
|
19360
|
-
this.wake();
|
|
19361
|
-
}
|
|
19362
|
-
}
|
|
19363
|
-
winsize() {
|
|
19364
|
-
return [this.rows, this.cols];
|
|
19365
|
-
}
|
|
19366
|
-
file() {
|
|
19367
|
-
return new OpenFile({
|
|
19368
|
-
read: (max, signal) => this.read(max, signal),
|
|
19369
|
-
write: async (bytes) => {
|
|
19370
|
-
if (this.hungUp) throw new KernelError("EIO");
|
|
19371
|
-
this.output(bytes);
|
|
19372
|
-
return bytes.length;
|
|
19373
|
-
},
|
|
19374
|
-
poll: () => ({
|
|
19375
|
-
readable: this.readable.length > 0 || this.hungUp,
|
|
19376
|
-
writable: true,
|
|
19377
|
-
hangup: this.hungUp,
|
|
19378
|
-
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
19379
|
-
}),
|
|
19380
|
-
changed: (signal) => this.changed(signal),
|
|
19381
|
-
close: () => {
|
|
19382
|
-
},
|
|
19383
|
-
tty: this
|
|
19384
|
-
});
|
|
19385
|
-
}
|
|
19386
|
-
get canonical() {
|
|
19387
|
-
return (this.termios.c_lflag & ICANON) !== 0;
|
|
19388
|
-
}
|
|
19389
|
-
cc(slot2) {
|
|
19390
|
-
return this.termios.c_cc[slot2] ?? 0;
|
|
19391
|
-
}
|
|
19392
|
-
key(input) {
|
|
19393
|
-
const { c_iflag, c_lflag } = this.termios;
|
|
19394
|
-
const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
|
|
19395
|
-
if (c_lflag & ISIG && this.signalKey(byte)) return;
|
|
19396
|
-
if (!this.canonical) {
|
|
19397
|
-
this.readable.push(Uint8Array.of(byte));
|
|
19398
|
-
if (c_lflag & ECHO) this.echo(byte);
|
|
19399
|
-
return;
|
|
19400
|
-
}
|
|
19401
|
-
this.edit(byte);
|
|
19402
|
-
}
|
|
19403
|
-
signalKey(byte) {
|
|
19404
|
-
const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
|
|
19405
|
-
if (!sig || byte === 0) return false;
|
|
19406
|
-
if (this.termios.c_lflag & ECHO) this.echo(byte);
|
|
19407
|
-
if (!(this.termios.c_lflag & NOFLSH)) {
|
|
19408
|
-
this.line = [];
|
|
19409
|
-
this.readable = [];
|
|
19410
|
-
}
|
|
19411
|
-
this.signal(sig);
|
|
19412
|
-
return true;
|
|
19413
|
-
}
|
|
19414
|
-
edit(byte) {
|
|
19415
|
-
const echo = (this.termios.c_lflag & ECHO) !== 0;
|
|
19416
|
-
if (byte === this.cc(VERASE) || byte === 8) {
|
|
19417
|
-
this.rubOut(this.lastCharLength());
|
|
19418
|
-
} else if (byte === this.cc(VKILL)) {
|
|
19419
|
-
this.rubOut(this.line.length);
|
|
19420
|
-
} else if (byte === this.cc(VWERASE)) {
|
|
19421
|
-
this.rubOut(this.lastWordLength());
|
|
19422
|
-
} else if (byte === this.cc(VEOF)) {
|
|
19423
|
-
this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
|
|
19424
|
-
this.line = [];
|
|
19425
|
-
} else if (byte === 10) {
|
|
19426
|
-
this.line.push(byte);
|
|
19427
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
19428
|
-
this.line = [];
|
|
19429
|
-
if (echo) this.output(encoder7.encode("\n"));
|
|
19430
|
-
} else {
|
|
19431
|
-
this.line.push(byte);
|
|
19432
|
-
if (echo) this.echo(byte);
|
|
19433
|
-
}
|
|
19434
|
-
}
|
|
19435
|
-
lastWordLength() {
|
|
19436
|
-
let end = this.line.length;
|
|
19437
|
-
while (end > 0 && this.line[end - 1] === 32) end--;
|
|
19438
|
-
while (end > 0 && this.line[end - 1] !== 32) end--;
|
|
19439
|
-
return this.line.length - end;
|
|
19440
|
-
}
|
|
19441
|
-
lastCharLength() {
|
|
19442
|
-
const { line } = this;
|
|
19443
|
-
let n = Math.min(1, line.length);
|
|
19444
|
-
if (this.termios.c_iflag & IUTF8) {
|
|
19445
|
-
while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
|
|
19446
|
-
}
|
|
19447
|
-
return n;
|
|
19448
|
-
}
|
|
19449
|
-
rubOut(n) {
|
|
19450
|
-
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
19451
|
-
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
19452
|
-
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
19453
|
-
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
19454
|
-
this.output(encoder7.encode("\b \b".repeat(cells)), false);
|
|
19455
|
-
}
|
|
19456
|
-
}
|
|
19457
|
-
echo(byte) {
|
|
19458
|
-
if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
|
|
19459
|
-
this.output(Uint8Array.of(94, byte + 64), false);
|
|
19460
|
-
return;
|
|
19461
|
-
}
|
|
19462
|
-
this.output(Uint8Array.of(byte));
|
|
19463
|
-
}
|
|
19464
|
-
output(bytes, translate2 = true) {
|
|
19465
|
-
const { c_oflag } = this.termios;
|
|
19466
|
-
if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
|
|
19467
|
-
this.screen.write(bytes);
|
|
19468
|
-
return;
|
|
19469
|
-
}
|
|
19470
|
-
const out = [];
|
|
19471
|
-
for (const byte of bytes) {
|
|
19472
|
-
if (byte === 10) out.push(13);
|
|
19473
|
-
out.push(byte);
|
|
19474
|
-
}
|
|
19475
|
-
this.screen.write(Uint8Array.from(out));
|
|
19476
|
-
}
|
|
19477
|
-
get reading() {
|
|
19478
|
-
return this.readers > 0;
|
|
19479
|
-
}
|
|
19480
|
-
async read(max, signal) {
|
|
19481
|
-
this.onRead?.();
|
|
19482
|
-
this.readers++;
|
|
19483
|
-
try {
|
|
19484
|
-
return await this.take(max, signal);
|
|
19485
|
-
} finally {
|
|
19486
|
-
this.readers--;
|
|
19487
|
-
}
|
|
19488
|
-
}
|
|
19489
|
-
async take(max, signal) {
|
|
19490
|
-
while (this.readable.length === 0) {
|
|
19491
|
-
if (this.hungUp) return new Uint8Array(0);
|
|
19492
|
-
await this.changed(signal);
|
|
19493
|
-
}
|
|
19494
|
-
const head = this.readable[0];
|
|
19495
|
-
if (head === null) {
|
|
19496
|
-
this.readable.shift();
|
|
19497
|
-
return new Uint8Array(0);
|
|
19498
|
-
}
|
|
19499
|
-
if (head === void 0) return new Uint8Array(0);
|
|
19500
|
-
const chunks2 = [];
|
|
19501
|
-
let n = 0;
|
|
19502
|
-
while (this.readable.length > 0 && n < max) {
|
|
19503
|
-
const next3 = this.readable[0];
|
|
19504
|
-
if (next3 === null || next3 === void 0) break;
|
|
19505
|
-
const take = next3.subarray(0, max - n);
|
|
19506
|
-
chunks2.push(take);
|
|
19507
|
-
n += take.length;
|
|
19508
|
-
if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
|
|
19509
|
-
else this.readable.shift();
|
|
19510
|
-
if (this.canonical) break;
|
|
19511
|
-
}
|
|
19512
|
-
const out = new Uint8Array(n);
|
|
19513
|
-
let at = 0;
|
|
19514
|
-
for (const chunk2 of chunks2) {
|
|
19515
|
-
out.set(chunk2, at);
|
|
19516
|
-
at += chunk2.length;
|
|
19517
|
-
}
|
|
19518
|
-
return out;
|
|
19519
|
-
}
|
|
19520
|
-
changed(signal) {
|
|
19521
|
-
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
19522
|
-
return new Promise((resolve2, reject) => {
|
|
19523
|
-
const onAbort = () => {
|
|
19524
|
-
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
19525
|
-
reject(new KernelError("EINTR"));
|
|
19526
|
-
};
|
|
19527
|
-
const waiter = () => {
|
|
19528
|
-
signal?.removeEventListener("abort", onAbort);
|
|
19529
|
-
resolve2();
|
|
19530
|
-
};
|
|
19531
|
-
this.waiters.push(waiter);
|
|
19532
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19533
|
-
});
|
|
19534
|
-
}
|
|
19535
|
-
wake() {
|
|
19536
|
-
const waiters = this.waiters;
|
|
19537
|
-
this.waiters = [];
|
|
19538
|
-
for (const resolve2 of waiters) resolve2();
|
|
19539
|
-
}
|
|
19540
|
-
};
|
|
19541
|
-
|
|
19542
20549
|
// src/kernel/pty.ts
|
|
19543
20550
|
var PtyPair = class {
|
|
19544
20551
|
slave;
|
|
@@ -19731,6 +20738,7 @@ function ptySyscall(req, ctx) {
|
|
|
19731
20738
|
return done3;
|
|
19732
20739
|
case "pty-ctty": {
|
|
19733
20740
|
const tty = terminalOf(ctx, req.fd);
|
|
20741
|
+
if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
|
|
19734
20742
|
const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
|
|
19735
20743
|
if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done3;
|
|
19736
20744
|
if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
|
|
@@ -20512,10 +21520,10 @@ function basicOf(value2) {
|
|
|
20512
21520
|
function placeholdersIn(text) {
|
|
20513
21521
|
return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
|
|
20514
21522
|
}
|
|
20515
|
-
function scan(
|
|
21523
|
+
function scan(lines3) {
|
|
20516
21524
|
const found = [];
|
|
20517
|
-
for (let i = 1; i <
|
|
20518
|
-
const m = HEADER_LINE.exec(
|
|
21525
|
+
for (let i = 1; i < lines3.length; i++) {
|
|
21526
|
+
const m = HEADER_LINE.exec(lines3[i]);
|
|
20519
21527
|
if (!m) return void 0;
|
|
20520
21528
|
const value2 = m[2];
|
|
20521
21529
|
const basic = basicOf(value2);
|
|
@@ -20538,10 +21546,10 @@ function usable2(values) {
|
|
|
20538
21546
|
}
|
|
20539
21547
|
async function substituteHead(head, ask) {
|
|
20540
21548
|
if (!head.endsWith("\r\n\r\n")) return void 0;
|
|
20541
|
-
const
|
|
20542
|
-
if (
|
|
20543
|
-
const request = REQUEST_LINE2.exec(
|
|
20544
|
-
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;
|
|
20545
21553
|
if (!request || !found || found.length !== 1) return void 0;
|
|
20546
21554
|
const at = found[0];
|
|
20547
21555
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
@@ -20553,8 +21561,8 @@ async function substituteHead(head, ask) {
|
|
|
20553
21561
|
);
|
|
20554
21562
|
if (Object.keys(values).length === 0) return void 0;
|
|
20555
21563
|
const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
|
|
20556
|
-
const out = [...
|
|
20557
|
-
out[at.line] = `${
|
|
21564
|
+
const out = [...lines3];
|
|
21565
|
+
out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
|
|
20558
21566
|
return { head: `${out.join("\r\n")}\r
|
|
20559
21567
|
\r
|
|
20560
21568
|
`, values };
|
|
@@ -20877,9 +21885,9 @@ function serverHeader(header5, server) {
|
|
|
20877
21885
|
return server.plus && server.binding ? END_POINT : "n,,";
|
|
20878
21886
|
}
|
|
20879
21887
|
function rewritten(first, header5) {
|
|
20880
|
-
const
|
|
20881
|
-
if (!
|
|
20882
|
-
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}` };
|
|
20883
21891
|
}
|
|
20884
21892
|
var PgClient = class {
|
|
20885
21893
|
clientFirst;
|
|
@@ -20899,10 +21907,10 @@ var PgClient = class {
|
|
|
20899
21907
|
}
|
|
20900
21908
|
if (server.sasl && this.clientFirst === void 0) {
|
|
20901
21909
|
const first = initial(message3);
|
|
20902
|
-
const
|
|
20903
|
-
if (!first || !
|
|
21910
|
+
const parsed3 = first && clientFirstOf(first.data);
|
|
21911
|
+
if (!first || !parsed3) return { bytes: message3, done: true };
|
|
20904
21912
|
this.clientFirst = first.data;
|
|
20905
|
-
this.header = serverHeader(
|
|
21913
|
+
this.header = serverHeader(parsed3.header, server);
|
|
20906
21914
|
const sent = rewritten(first, this.header);
|
|
20907
21915
|
if (sent === first) return { bytes: message3, done: false };
|
|
20908
21916
|
return {
|
|
@@ -21067,7 +22075,7 @@ function headEnd2(bytes) {
|
|
|
21067
22075
|
}
|
|
21068
22076
|
return -1;
|
|
21069
22077
|
}
|
|
21070
|
-
function
|
|
22078
|
+
function modeOf3(start2) {
|
|
21071
22079
|
const sniffs = [
|
|
21072
22080
|
["http1", sniffHttp1(start2)],
|
|
21073
22081
|
["smtp", sniffSmtp(start2)],
|
|
@@ -21176,7 +22184,7 @@ var SessionWriter = class {
|
|
|
21176
22184
|
joined5.set(bytes, this.buffer.length);
|
|
21177
22185
|
this.buffer.fill(0);
|
|
21178
22186
|
this.buffer = joined5;
|
|
21179
|
-
if (this.mode === "sniff") this.mode =
|
|
22187
|
+
if (this.mode === "sniff") this.mode = modeOf3(latin12(joined5.subarray(0, 8))) ?? "sniff";
|
|
21180
22188
|
if (this.mode === "pg" && !this.sawPg) {
|
|
21181
22189
|
this.sawPg = true;
|
|
21182
22190
|
if (!this.s.verified) void this.proof("pg");
|
|
@@ -21663,12 +22671,12 @@ function secure(prepared, seal) {
|
|
|
21663
22671
|
prepared.own?.release();
|
|
21664
22672
|
}
|
|
21665
22673
|
};
|
|
21666
|
-
const
|
|
22674
|
+
const abandon2 = () => {
|
|
21667
22675
|
if (ran) return;
|
|
21668
22676
|
prepared.options.session.free();
|
|
21669
22677
|
prepared.own?.release();
|
|
21670
22678
|
};
|
|
21671
|
-
return { make, abandon };
|
|
22679
|
+
return { make, abandon: abandon2 };
|
|
21672
22680
|
}
|
|
21673
22681
|
function supportOf(net) {
|
|
21674
22682
|
const support = net.tlsClient;
|
|
@@ -21685,19 +22693,19 @@ async function uplinkAddress(proc, host) {
|
|
|
21685
22693
|
async function connectSecure(proc, socket, host, port, req) {
|
|
21686
22694
|
const servername = serverName(req.servername || host);
|
|
21687
22695
|
if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
|
|
21688
|
-
const
|
|
21689
|
-
const route2 = proc.net.uplinkFor(
|
|
22696
|
+
const address2 = await uplinkAddress(proc, host);
|
|
22697
|
+
const route2 = proc.net.uplinkFor(address2);
|
|
21690
22698
|
if (!route2)
|
|
21691
22699
|
throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
|
|
21692
22700
|
const support = supportOf(proc.net);
|
|
21693
22701
|
let info;
|
|
21694
|
-
const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
|
|
22702
|
+
const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
|
|
21695
22703
|
try {
|
|
21696
|
-
await socket.connectSecure({ family: "inet", host:
|
|
22704
|
+
await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
|
|
21697
22705
|
info = await conn.upgrade(make);
|
|
21698
22706
|
});
|
|
21699
22707
|
} finally {
|
|
21700
|
-
|
|
22708
|
+
abandon2();
|
|
21701
22709
|
}
|
|
21702
22710
|
return sessionOf(info, servername);
|
|
21703
22711
|
}
|
|
@@ -21730,14 +22738,14 @@ async function upgradeSecure(proc, socket, req) {
|
|
|
21730
22738
|
const host = req.servername || (peer?.family === "inet" ? peer.host : "");
|
|
21731
22739
|
const servername = serverName(host);
|
|
21732
22740
|
const port = peer?.family === "inet" ? peer.port : 0;
|
|
21733
|
-
const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
22741
|
+
const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
21734
22742
|
proc,
|
|
21735
22743
|
port
|
|
21736
22744
|
});
|
|
21737
22745
|
try {
|
|
21738
22746
|
return sessionOf(await socket.upgradeSecure(make), servername);
|
|
21739
22747
|
} finally {
|
|
21740
|
-
|
|
22748
|
+
abandon2();
|
|
21741
22749
|
}
|
|
21742
22750
|
}
|
|
21743
22751
|
|
|
@@ -23661,6 +24669,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
23661
24669
|
"fd-open-clipboard",
|
|
23662
24670
|
"fd-clipboard-abort",
|
|
23663
24671
|
"fd-open-embedder",
|
|
24672
|
+
"fd-dev-nodes",
|
|
24673
|
+
"fd-open-dev",
|
|
24674
|
+
"fd-dev-ioctl",
|
|
24675
|
+
"fd-dev-meta",
|
|
23664
24676
|
"tty-get",
|
|
23665
24677
|
"tty-set",
|
|
23666
24678
|
"tty-winsz",
|
|
@@ -24037,7 +25049,7 @@ var WasmProcess = class {
|
|
|
24037
25049
|
async dispatch(req, cancelled) {
|
|
24038
25050
|
try {
|
|
24039
25051
|
if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
|
|
24040
|
-
if (isTtySyscall(req)) return this.ttySyscall(req);
|
|
25052
|
+
if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
|
|
24041
25053
|
if (isJobSyscall(req)) return this.jobSyscall(req);
|
|
24042
25054
|
if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
|
|
24043
25055
|
if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
|
|
@@ -24101,7 +25113,7 @@ var WasmProcess = class {
|
|
|
24101
25113
|
return owner;
|
|
24102
25114
|
}
|
|
24103
25115
|
pathKeys = /* @__PURE__ */ new Map();
|
|
24104
|
-
async lockPath(target,
|
|
25116
|
+
async lockPath(target, want2) {
|
|
24105
25117
|
if (target.fd !== void 0) {
|
|
24106
25118
|
const st = await this.fds.get(target.fd).file.stat?.();
|
|
24107
25119
|
if (!st) throw new KernelError("EBADF");
|
|
@@ -24111,9 +25123,9 @@ var WasmProcess = class {
|
|
|
24111
25123
|
if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
|
|
24112
25124
|
const path = target.path;
|
|
24113
25125
|
const g = this.guarded();
|
|
24114
|
-
const checked = `${
|
|
24115
|
-
if (g &&
|
|
24116
|
-
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);
|
|
24117
25129
|
this.lockChecked.add(checked);
|
|
24118
25130
|
}
|
|
24119
25131
|
return path;
|
|
@@ -24126,8 +25138,8 @@ var WasmProcess = class {
|
|
|
24126
25138
|
const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
|
|
24127
25139
|
if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
|
|
24128
25140
|
if (access2 && !access2.stat) return objectKey(access2);
|
|
24129
|
-
const
|
|
24130
|
-
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);
|
|
24131
25143
|
if (known !== void 0) return known;
|
|
24132
25144
|
const st = await this.options.fs.stat?.(path).catch((err) => {
|
|
24133
25145
|
if (!file) throw err;
|
|
@@ -24213,7 +25225,7 @@ var WasmProcess = class {
|
|
|
24213
25225
|
const file = this.fds.get(req.fd).file;
|
|
24214
25226
|
const read = req.peek ? file.peek : file.read;
|
|
24215
25227
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
24216
|
-
if (file.tty) this.checkForeground(file.tty);
|
|
25228
|
+
if (file.tty && !file.device) this.checkForeground(file.tty);
|
|
24217
25229
|
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
24218
25230
|
if (file.embedder) return this.readEmbedder(file, req, cancelled);
|
|
24219
25231
|
if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
|
|
@@ -24310,6 +25322,46 @@ var WasmProcess = class {
|
|
|
24310
25322
|
throw err;
|
|
24311
25323
|
});
|
|
24312
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
|
+
}
|
|
24313
25365
|
async openEmbedder(flags2) {
|
|
24314
25366
|
const broker = this.options.embedder;
|
|
24315
25367
|
if (!broker) throw new KernelError("ENOENT");
|
|
@@ -24397,11 +25449,11 @@ var WasmProcess = class {
|
|
|
24397
25449
|
} else if (req.create) {
|
|
24398
25450
|
await nodes.onEntry(path, async () => {
|
|
24399
25451
|
const marks = this.options.owners || plain3 !== void 0;
|
|
24400
|
-
const
|
|
25452
|
+
const fresh4 = marks && !await present3(fs, path, true);
|
|
24401
25453
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
|
|
24402
25454
|
await this.fds.get(fd).file.stat?.();
|
|
24403
|
-
if (
|
|
24404
|
-
else if (
|
|
25455
|
+
if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
|
|
25456
|
+
else if (fresh4) {
|
|
24405
25457
|
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
24406
25458
|
}
|
|
24407
25459
|
});
|
|
@@ -24500,8 +25552,8 @@ var WasmProcess = class {
|
|
|
24500
25552
|
if (req.exclusive) return inParent(g, "open", req.path);
|
|
24501
25553
|
if (req.created && guard2.created?.take(req.path)) return;
|
|
24502
25554
|
const access2 = req.flags & WRITE_ACCESS;
|
|
24503
|
-
const
|
|
24504
|
-
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);
|
|
24505
25557
|
}
|
|
24506
25558
|
openTty(name2) {
|
|
24507
25559
|
const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
|
|
@@ -24565,7 +25617,7 @@ var WasmProcess = class {
|
|
|
24565
25617
|
}
|
|
24566
25618
|
terminalByName(name2) {
|
|
24567
25619
|
const n = ptsNumber(name2);
|
|
24568
|
-
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;
|
|
24569
25621
|
if (!tty) throw new KernelError("ENOENT");
|
|
24570
25622
|
return tty;
|
|
24571
25623
|
}
|
|
@@ -24687,6 +25739,16 @@ var WasmProcess = class {
|
|
|
24687
25739
|
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
24688
25740
|
case "fd-open-embedder":
|
|
24689
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
|
+
}
|
|
24690
25752
|
case "fd-clipboard-abort": {
|
|
24691
25753
|
const file = this.fds.get(req.fd);
|
|
24692
25754
|
file.file.clipboard?.abort();
|
|
@@ -24731,6 +25793,11 @@ var WasmProcess = class {
|
|
|
24731
25793
|
return { ok: true, kind: "void" };
|
|
24732
25794
|
}
|
|
24733
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
|
+
}
|
|
24734
25801
|
if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
|
|
24735
25802
|
switch (req.op) {
|
|
24736
25803
|
case "fd-pread":
|
|
@@ -24900,14 +25967,19 @@ var WasmProcess = class {
|
|
|
24900
25967
|
...file.fifo !== void 0 ? { fifo: file.fifo } : {}
|
|
24901
25968
|
};
|
|
24902
25969
|
}
|
|
24903
|
-
ttySyscall(req) {
|
|
25970
|
+
async ttySyscall(req, cancelled) {
|
|
24904
25971
|
const tty = this.tty(req.fd);
|
|
24905
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");
|
|
24906
25975
|
switch (req.op) {
|
|
24907
25976
|
case "tty-get":
|
|
24908
25977
|
return { ok: true, kind: "json", json: tty.tcgets() };
|
|
24909
25978
|
case "tty-set":
|
|
24910
|
-
|
|
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);
|
|
24911
25983
|
return { ok: true, kind: "void" };
|
|
24912
25984
|
case "tty-winsz": {
|
|
24913
25985
|
const size = tty.winsize();
|
|
@@ -25788,6 +26860,7 @@ function processOptions(opts, header5, hooks, created4) {
|
|
|
25788
26860
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
25789
26861
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
25790
26862
|
...embedderOptions(opts),
|
|
26863
|
+
devices: opts.devices,
|
|
25791
26864
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
25792
26865
|
...opts.source ? { source: opts.source } : {},
|
|
25793
26866
|
noLinks: opts.noLinks,
|
|
@@ -26542,15 +27615,15 @@ var RemoteWatch = class {
|
|
|
26542
27615
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
26543
27616
|
}
|
|
26544
27617
|
hold(add, drop, wait2) {
|
|
26545
|
-
const
|
|
27618
|
+
const fresh4 = add.filter(
|
|
26546
27619
|
([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
|
|
26547
27620
|
);
|
|
26548
27621
|
const gone = drop.filter((path) => this.myHolds.has(path));
|
|
26549
27622
|
for (const path of gone) this.myHolds.delete(path);
|
|
26550
|
-
for (const [path, fid] of
|
|
26551
|
-
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)
|
|
26552
27625
|
return Promise.resolve();
|
|
26553
|
-
const seq2 = this.sendHolds(
|
|
27626
|
+
const seq2 = this.sendHolds(fresh4, gone);
|
|
26554
27627
|
if (!wait2) return Promise.resolve();
|
|
26555
27628
|
const heard = new Set(
|
|
26556
27629
|
[...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
|
|
@@ -26974,14 +28047,14 @@ var LockService = class {
|
|
|
26974
28047
|
);
|
|
26975
28048
|
});
|
|
26976
28049
|
}
|
|
26977
|
-
async holder(key, owner,
|
|
28050
|
+
async holder(key, owner, want2) {
|
|
26978
28051
|
return await this.ask({
|
|
26979
28052
|
op: "holder",
|
|
26980
28053
|
key,
|
|
26981
28054
|
owner,
|
|
26982
|
-
kind:
|
|
26983
|
-
start:
|
|
26984
|
-
end:
|
|
28055
|
+
kind: want2.kind,
|
|
28056
|
+
start: want2.start,
|
|
28057
|
+
end: want2.end
|
|
26985
28058
|
}) ?? void 0;
|
|
26986
28059
|
}
|
|
26987
28060
|
async set(key, owner, kind, start2, end) {
|
|
@@ -27260,9 +28333,9 @@ function local(name2) {
|
|
|
27260
28333
|
function literal2(name2) {
|
|
27261
28334
|
return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
|
|
27262
28335
|
}
|
|
27263
|
-
function addressData(
|
|
27264
|
-
if (type === TYPE_A) return ipv4Bytes(
|
|
27265
|
-
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;
|
|
27266
28339
|
return groups?.flatMap((g) => [g >> 8, g & 255]);
|
|
27267
28340
|
}
|
|
27268
28341
|
var DnsStub = class {
|
|
@@ -27275,10 +28348,10 @@ var DnsStub = class {
|
|
|
27275
28348
|
this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
|
|
27276
28349
|
}
|
|
27277
28350
|
async answer(query, udp) {
|
|
27278
|
-
const
|
|
27279
|
-
if (!
|
|
27280
|
-
if (!
|
|
27281
|
-
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;
|
|
27282
28355
|
const reply = await this.reply(query, q).catch(
|
|
27283
28356
|
() => answerReply(query, q, SERVFAIL, [], false)
|
|
27284
28357
|
);
|
|
@@ -27287,8 +28360,8 @@ var DnsStub = class {
|
|
|
27287
28360
|
async reply(query, q) {
|
|
27288
28361
|
if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
|
|
27289
28362
|
if (q.type === TYPE_PTR) {
|
|
27290
|
-
const
|
|
27291
|
-
const name2 =
|
|
28363
|
+
const address2 = reverseAddress(q.name);
|
|
28364
|
+
const name2 = address2 !== void 0 ? await this.reverse(address2) : void 0;
|
|
27292
28365
|
if (name2 !== void 0)
|
|
27293
28366
|
return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
|
|
27294
28367
|
return this.forward(query, q);
|
|
@@ -27318,8 +28391,8 @@ var DnsStub = class {
|
|
|
27318
28391
|
known(name2) {
|
|
27319
28392
|
return this.resolver.known(name2).catch(() => false);
|
|
27320
28393
|
}
|
|
27321
|
-
reverse(
|
|
27322
|
-
return this.resolver.reverse(
|
|
28394
|
+
reverse(address2) {
|
|
28395
|
+
return this.resolver.reverse(address2).catch(() => void 0);
|
|
27323
28396
|
}
|
|
27324
28397
|
async forward(query, q) {
|
|
27325
28398
|
if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
|
|
@@ -27498,7 +28571,7 @@ function honors(traits, redirect) {
|
|
|
27498
28571
|
const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
|
|
27499
28572
|
return redirect === "manual" && raw2;
|
|
27500
28573
|
}
|
|
27501
|
-
function
|
|
28574
|
+
function refused4(message3) {
|
|
27502
28575
|
return new TransportError(message3, 502, void 0, "transport");
|
|
27503
28576
|
}
|
|
27504
28577
|
var RemoteTransport = class {
|
|
@@ -27589,7 +28662,7 @@ var RemoteTransport = class {
|
|
|
27589
28662
|
const { signal, redirect } = request;
|
|
27590
28663
|
signal.throwIfAborted();
|
|
27591
28664
|
if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
|
|
27592
|
-
throw
|
|
28665
|
+
throw refused4(`this transport cannot honor redirect: ${redirect}`);
|
|
27593
28666
|
}
|
|
27594
28667
|
let finished = false;
|
|
27595
28668
|
const abort = () => {
|
|
@@ -27640,7 +28713,7 @@ var RemoteTransport = class {
|
|
|
27640
28713
|
};
|
|
27641
28714
|
if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
|
|
27642
28715
|
await cancel();
|
|
27643
|
-
throw
|
|
28716
|
+
throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
|
|
27644
28717
|
}
|
|
27645
28718
|
return {
|
|
27646
28719
|
...url !== void 0 ? { url } : {},
|
|
@@ -28160,6 +29233,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
|
|
|
28160
29233
|
"proc-list",
|
|
28161
29234
|
"proc-identity",
|
|
28162
29235
|
"fd-info",
|
|
29236
|
+
"fd-dev-nodes",
|
|
28163
29237
|
"sig-mask",
|
|
28164
29238
|
"sig-inherited",
|
|
28165
29239
|
"proc-setpgid",
|
|
@@ -28256,8 +29330,8 @@ var WasmMemories = class {
|
|
|
28256
29330
|
const learned = this.learned;
|
|
28257
29331
|
if (!learned) return this.base;
|
|
28258
29332
|
const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
|
|
28259
|
-
const
|
|
28260
|
-
if (
|
|
29333
|
+
const raised2 = learned.ceiling + steps * DECAY_STEP;
|
|
29334
|
+
if (raised2 < (this.base ?? DEFAULT_CEILING)) return raised2;
|
|
28261
29335
|
this.learned = void 0;
|
|
28262
29336
|
return this.base;
|
|
28263
29337
|
}
|
|
@@ -28400,9 +29474,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
28400
29474
|
let open = false;
|
|
28401
29475
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
28402
29476
|
async function hello(message3) {
|
|
28403
|
-
const
|
|
28404
|
-
if (
|
|
28405
|
-
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" } });
|
|
28406
29480
|
return;
|
|
28407
29481
|
}
|
|
28408
29482
|
try {
|
|
@@ -28455,8 +29529,8 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
28455
29529
|
function noticeOf(data) {
|
|
28456
29530
|
if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
|
|
28457
29531
|
const notice = data ?? {};
|
|
28458
|
-
const
|
|
28459
|
-
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 };
|
|
28460
29534
|
}
|
|
28461
29535
|
|
|
28462
29536
|
// src/fs/opfs.ts
|
|
@@ -28950,7 +30024,7 @@ var OpfsFs = class {
|
|
|
28950
30024
|
const parent = entries.at(-1);
|
|
28951
30025
|
const setgid = ((parent?.mode ?? 0) & S_ISGID4) !== 0;
|
|
28952
30026
|
const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
|
|
28953
|
-
const
|
|
30027
|
+
const want2 = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
|
|
28954
30028
|
let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
|
|
28955
30029
|
if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
|
|
28956
30030
|
const fields = {};
|
|
@@ -28958,7 +30032,7 @@ var OpfsFs = class {
|
|
|
28958
30032
|
fields.mode = mode;
|
|
28959
30033
|
}
|
|
28960
30034
|
if (self) fields.heir = void 0;
|
|
28961
|
-
if (ownOf(self) || !sameOwner(
|
|
30035
|
+
if (ownOf(self) || !sameOwner(want2, inherit(entries))) Object.assign(fields, want2);
|
|
28962
30036
|
return Object.keys(fields).length > 0 ? fields : void 0;
|
|
28963
30037
|
}
|
|
28964
30038
|
async stampNew(path, owner, directory) {
|
|
@@ -29195,9 +30269,9 @@ var OpfsFs = class {
|
|
|
29195
30269
|
this.lookups++;
|
|
29196
30270
|
if (split2(path).length === 0) return this.root;
|
|
29197
30271
|
const cached = this.files.get(path);
|
|
29198
|
-
const
|
|
29199
|
-
if (cached && !
|
|
29200
|
-
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) {
|
|
29201
30275
|
if (this.glanced(cached)) return this.remember(path, cached);
|
|
29202
30276
|
try {
|
|
29203
30277
|
await this.glance(cached);
|
|
@@ -29415,8 +30489,8 @@ var OpfsFs = class {
|
|
|
29415
30489
|
glanced(handle) {
|
|
29416
30490
|
if (this.exact > 0) return void 0;
|
|
29417
30491
|
const seen = this.glances.get(handle);
|
|
29418
|
-
const
|
|
29419
|
-
return
|
|
30492
|
+
const fresh4 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
|
|
30493
|
+
return fresh4 ? seen.file : void 0;
|
|
29420
30494
|
}
|
|
29421
30495
|
async glance(handle) {
|
|
29422
30496
|
const at = performance.now();
|
|
@@ -29618,13 +30692,13 @@ var OpfsFs = class {
|
|
|
29618
30692
|
async keptFor(path) {
|
|
29619
30693
|
const at = normalizePath(path);
|
|
29620
30694
|
const cached = this.files.get(at);
|
|
29621
|
-
const
|
|
29622
|
-
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);
|
|
29623
30697
|
if (!existed) return [void 0, target, false];
|
|
29624
30698
|
this.refuse(target);
|
|
29625
30699
|
const [entry] = await this.meta.get([target]);
|
|
29626
30700
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
29627
|
-
const kept =
|
|
30701
|
+
const kept = fresh4 ? cached : this.files.get(target);
|
|
29628
30702
|
const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
29629
30703
|
return [usable3 ? kept : void 0, target, true];
|
|
29630
30704
|
}
|
|
@@ -31178,7 +32252,7 @@ function unescape(field4) {
|
|
|
31178
32252
|
);
|
|
31179
32253
|
}
|
|
31180
32254
|
function parseFstab(text) {
|
|
31181
|
-
const
|
|
32255
|
+
const lines3 = [];
|
|
31182
32256
|
text.split("\n").forEach((raw2, index) => {
|
|
31183
32257
|
const content = raw2.replace(/#.*$/, "").trim();
|
|
31184
32258
|
if (!content) return;
|
|
@@ -31187,12 +32261,12 @@ function parseFstab(text) {
|
|
|
31187
32261
|
if (options.split(",").includes("noauto")) return;
|
|
31188
32262
|
const spec = { source, target, type, options: {} };
|
|
31189
32263
|
try {
|
|
31190
|
-
|
|
32264
|
+
lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
31191
32265
|
} catch (err) {
|
|
31192
|
-
|
|
32266
|
+
lines3.push({ line: index + 1, spec, invalid: err });
|
|
31193
32267
|
}
|
|
31194
32268
|
});
|
|
31195
|
-
return
|
|
32269
|
+
return lines3;
|
|
31196
32270
|
}
|
|
31197
32271
|
function codeOf6(err) {
|
|
31198
32272
|
const code = err?.code;
|
|
@@ -31210,20 +32284,20 @@ function wait(ms, signal) {
|
|
|
31210
32284
|
signal.addEventListener("abort", done4, { once: true });
|
|
31211
32285
|
});
|
|
31212
32286
|
}
|
|
31213
|
-
async function mountFstab(
|
|
32287
|
+
async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
31214
32288
|
}, settled = () => {
|
|
31215
32289
|
}) {
|
|
31216
32290
|
return Promise.all(
|
|
31217
|
-
|
|
32291
|
+
lines3.map(async (each) => {
|
|
31218
32292
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
31219
32293
|
settled(each, result);
|
|
31220
32294
|
return result;
|
|
31221
32295
|
})
|
|
31222
32296
|
);
|
|
31223
32297
|
}
|
|
31224
|
-
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
31225
|
-
let failed5 =
|
|
31226
|
-
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++) {
|
|
31227
32301
|
try {
|
|
31228
32302
|
const entry = await mount(spec);
|
|
31229
32303
|
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
@@ -31391,14 +32465,14 @@ var RESTORE_AGAIN_MS = 3e4;
|
|
|
31391
32465
|
function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
31392
32466
|
const handles = /* @__PURE__ */ new Map();
|
|
31393
32467
|
let nextFh = 0;
|
|
31394
|
-
const
|
|
32468
|
+
const fresh4 = (remote) => {
|
|
31395
32469
|
if (remote.client.token === remote.token && !remote.client.gone) return remote;
|
|
31396
32470
|
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
31397
32471
|
};
|
|
31398
32472
|
const live2 = (fh) => {
|
|
31399
32473
|
const remote = handles.get(fh);
|
|
31400
32474
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
31401
|
-
return
|
|
32475
|
+
return fresh4(remote);
|
|
31402
32476
|
};
|
|
31403
32477
|
const call = (op, body = {}) => current().call(op, body);
|
|
31404
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);
|
|
@@ -31480,7 +32554,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
31480
32554
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
31481
32555
|
handles.delete(fh);
|
|
31482
32556
|
try {
|
|
31483
|
-
await
|
|
32557
|
+
await fresh4(remote).client.call("release", { fh: remote.fh }, true);
|
|
31484
32558
|
} finally {
|
|
31485
32559
|
if (remote.restore) await settle(current(), remote.restore);
|
|
31486
32560
|
}
|
|
@@ -31530,7 +32604,7 @@ async function resize(h, path, size) {
|
|
|
31530
32604
|
await h.release(fh);
|
|
31531
32605
|
}
|
|
31532
32606
|
}
|
|
31533
|
-
function
|
|
32607
|
+
function sleep2(ms, signal) {
|
|
31534
32608
|
return new Promise((resolve2) => {
|
|
31535
32609
|
const done4 = () => {
|
|
31536
32610
|
clearTimeout(timer);
|
|
@@ -31558,22 +32632,22 @@ function keepWatching(loop, signal) {
|
|
|
31558
32632
|
const stop = () => session2.abort();
|
|
31559
32633
|
signal.addEventListener("abort", stop);
|
|
31560
32634
|
let timer;
|
|
31561
|
-
const
|
|
32635
|
+
const quiet5 = () => {
|
|
31562
32636
|
clearTimeout(timer);
|
|
31563
32637
|
timer = setTimeout(stop, silenceMs);
|
|
31564
32638
|
};
|
|
31565
32639
|
let opened2 = false;
|
|
31566
32640
|
try {
|
|
31567
32641
|
const client = await loop.connect();
|
|
31568
|
-
|
|
32642
|
+
quiet5();
|
|
31569
32643
|
await client.watch(
|
|
31570
32644
|
session2.signal,
|
|
31571
32645
|
(event) => {
|
|
31572
|
-
|
|
32646
|
+
quiet5();
|
|
31573
32647
|
if (!event.ping) loop.event(event);
|
|
31574
32648
|
},
|
|
31575
32649
|
() => {
|
|
31576
|
-
|
|
32650
|
+
quiet5();
|
|
31577
32651
|
opened2 = true;
|
|
31578
32652
|
up = true;
|
|
31579
32653
|
loop.up(client);
|
|
@@ -31602,7 +32676,7 @@ function keepWatching(loop, signal) {
|
|
|
31602
32676
|
loop.down();
|
|
31603
32677
|
}
|
|
31604
32678
|
ready2.resolve();
|
|
31605
|
-
await
|
|
32679
|
+
await sleep2(wait2, signal);
|
|
31606
32680
|
wait2 = Math.min(wait2 * 2, most);
|
|
31607
32681
|
}
|
|
31608
32682
|
})();
|
|
@@ -31844,7 +32918,7 @@ var PortTransport = class {
|
|
|
31844
32918
|
}).finally(() => this.fail("the folder's watch ended"));
|
|
31845
32919
|
}
|
|
31846
32920
|
};
|
|
31847
|
-
var
|
|
32921
|
+
var errorOf3 = (err) => ({
|
|
31848
32922
|
code: typeof codeOf7(err) === "string" ? codeOf7(err) : "EIO",
|
|
31849
32923
|
message: err?.message ?? String(err)
|
|
31850
32924
|
});
|
|
@@ -31894,7 +32968,7 @@ function serveHostfsPort(port, transport) {
|
|
|
31894
32968
|
);
|
|
31895
32969
|
send2({ id: id2, end: true });
|
|
31896
32970
|
} catch (err) {
|
|
31897
|
-
send2({ id: id2, error:
|
|
32971
|
+
send2({ id: id2, error: errorOf3(err) });
|
|
31898
32972
|
} finally {
|
|
31899
32973
|
watches.delete(id2);
|
|
31900
32974
|
}
|
|
@@ -31922,7 +32996,7 @@ function serveHostfsPort(port, transport) {
|
|
|
31922
32996
|
if (op !== "call") throw fsError2("EINVAL", `unknown hostfs port op ${op}`);
|
|
31923
32997
|
send2({ id: id2, result: await answer2(data.name, data.body) });
|
|
31924
32998
|
} catch (err) {
|
|
31925
|
-
send2({ id: id2, error:
|
|
32999
|
+
send2({ id: id2, error: errorOf3(err) });
|
|
31926
33000
|
}
|
|
31927
33001
|
};
|
|
31928
33002
|
const stop = () => {
|
|
@@ -33144,6 +34218,7 @@ var Launcher = class {
|
|
|
33144
34218
|
scrubs = /* @__PURE__ */ new Map();
|
|
33145
34219
|
clipboard;
|
|
33146
34220
|
embedder;
|
|
34221
|
+
devices;
|
|
33147
34222
|
rootFs;
|
|
33148
34223
|
fileWatches;
|
|
33149
34224
|
nextPid = 1e3;
|
|
@@ -33168,6 +34243,7 @@ var Launcher = class {
|
|
|
33168
34243
|
);
|
|
33169
34244
|
this.embedder = this.embedderBroker(options.embedder);
|
|
33170
34245
|
this.embedder.denied = options.onDenial;
|
|
34246
|
+
this.devices = this.deviceNodes(options.devices);
|
|
33171
34247
|
this.sealed = new SealedBroker(
|
|
33172
34248
|
options.sealed,
|
|
33173
34249
|
this.usersMode,
|
|
@@ -33383,16 +34459,16 @@ var Launcher = class {
|
|
|
33383
34459
|
if (!found) {
|
|
33384
34460
|
found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
|
|
33385
34461
|
const tiers = await this.tiers(scope, appended2);
|
|
33386
|
-
const
|
|
34462
|
+
const refused5 = [];
|
|
33387
34463
|
const commands = await scanCommands(
|
|
33388
34464
|
this.scanned,
|
|
33389
34465
|
tiers,
|
|
33390
34466
|
tiers.length - appended2.length,
|
|
33391
34467
|
void 0,
|
|
33392
|
-
|
|
34468
|
+
refused5,
|
|
33393
34469
|
await this.globalRoots(scope)
|
|
33394
34470
|
);
|
|
33395
|
-
this.refusedOf.set(commands,
|
|
34471
|
+
this.refusedOf.set(commands, refused5.map(refusing));
|
|
33396
34472
|
return commands;
|
|
33397
34473
|
}).then(
|
|
33398
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)
|
|
@@ -33599,14 +34675,14 @@ var Launcher = class {
|
|
|
33599
34675
|
return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
|
|
33600
34676
|
}
|
|
33601
34677
|
async refusedAt(path, name2, scope) {
|
|
33602
|
-
const
|
|
33603
|
-
if (
|
|
34678
|
+
const refused5 = this.refusedOf.get(await this.commands(scope)) ?? [];
|
|
34679
|
+
if (refused5.length === 0) return void 0;
|
|
33604
34680
|
const st = await this.base.stat(path).catch(() => void 0);
|
|
33605
34681
|
if (!st?.isFile) return void 0;
|
|
33606
34682
|
const real = await physicalPathAsync(this.base, path).catch(() => path);
|
|
33607
34683
|
const same2 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
|
|
33608
34684
|
const owners = [];
|
|
33609
|
-
for (const c of
|
|
34685
|
+
for (const c of refused5) {
|
|
33610
34686
|
const origin = c.origin;
|
|
33611
34687
|
for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
|
|
33612
34688
|
if (!await same2(own)) continue;
|
|
@@ -33616,11 +34692,11 @@ var Launcher = class {
|
|
|
33616
34692
|
}
|
|
33617
34693
|
const stem = name2.replace(/\.wasm$/, "");
|
|
33618
34694
|
const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
|
|
33619
|
-
return named ?? owners[0] ?? await this.refusedCopy(real, name2,
|
|
34695
|
+
return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
|
|
33620
34696
|
}
|
|
33621
|
-
async refusedCopy(real, name2,
|
|
34697
|
+
async refusedCopy(real, name2, refused5) {
|
|
33622
34698
|
const dir = real.slice(0, real.lastIndexOf("/"));
|
|
33623
|
-
for (const c of
|
|
34699
|
+
for (const c of refused5) {
|
|
33624
34700
|
const origin = c.origin;
|
|
33625
34701
|
if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
|
|
33626
34702
|
const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
|
|
@@ -33787,8 +34863,8 @@ var Launcher = class {
|
|
|
33787
34863
|
const via = planned?.via ?? [];
|
|
33788
34864
|
for (const [i, file] of via.entries()) {
|
|
33789
34865
|
if (!byPath(file)) continue;
|
|
33790
|
-
const
|
|
33791
|
-
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);
|
|
33792
34868
|
}
|
|
33793
34869
|
for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
|
|
33794
34870
|
if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
|
|
@@ -33828,12 +34904,12 @@ var Launcher = class {
|
|
|
33828
34904
|
return void 0;
|
|
33829
34905
|
}
|
|
33830
34906
|
async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
|
|
33831
|
-
const
|
|
34907
|
+
const refused5 = (err) => {
|
|
33832
34908
|
throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
|
|
33833
34909
|
};
|
|
33834
|
-
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);
|
|
33835
34911
|
const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
|
|
33836
|
-
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(
|
|
34912
|
+
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
|
|
33837
34913
|
if (planned) return planned;
|
|
33838
34914
|
throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
|
|
33839
34915
|
}
|
|
@@ -34469,6 +35545,7 @@ var Launcher = class {
|
|
|
34469
35545
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
34470
35546
|
clipboard: this.clipboard,
|
|
34471
35547
|
embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
|
|
35548
|
+
devices: this.devices,
|
|
34472
35549
|
...this.dac ? { dac: this.dac } : {},
|
|
34473
35550
|
program: req.program,
|
|
34474
35551
|
changes: this.changes.sab,
|
|
@@ -34652,8 +35729,8 @@ var Launcher = class {
|
|
|
34652
35729
|
this.sampledAt = Math.max(this.sampledAt, now);
|
|
34653
35730
|
this.load = this.load.map((avg, i) => {
|
|
34654
35731
|
const window = LOAD_WINDOWS_MS[i];
|
|
34655
|
-
const
|
|
34656
|
-
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;
|
|
34657
35734
|
});
|
|
34658
35735
|
}
|
|
34659
35736
|
idleSince;
|
|
@@ -35000,7 +36077,7 @@ var Launcher = class {
|
|
|
35000
36077
|
forker(ppid, parent) {
|
|
35001
36078
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
35002
36079
|
return async (state, given) => {
|
|
35003
|
-
const
|
|
36080
|
+
const fresh4 = (fds, quietOom) => this.start({
|
|
35004
36081
|
...image,
|
|
35005
36082
|
quietOom,
|
|
35006
36083
|
cwd: state.cwd ?? parent.cwd,
|
|
@@ -35016,7 +36093,7 @@ var Launcher = class {
|
|
|
35016
36093
|
tagBase: this.tagBases.get(ppid),
|
|
35017
36094
|
links: this.links.get(ppid)
|
|
35018
36095
|
});
|
|
35019
|
-
return childHandle(await this.admitted(parent.program.abi !== "js", given,
|
|
36096
|
+
return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
|
|
35020
36097
|
};
|
|
35021
36098
|
}
|
|
35022
36099
|
keySignal(tty, fallback, sig, send2) {
|
|
@@ -35057,6 +36134,14 @@ var Launcher = class {
|
|
|
35057
36134
|
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
35058
36135
|
this.sealed.factsOf = (pid) => this.processFacts(pid);
|
|
35059
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
|
+
}
|
|
35060
36145
|
embedderBroker(hook) {
|
|
35061
36146
|
const broker = new EmbedderBroker(
|
|
35062
36147
|
this.usersMode,
|
|
@@ -35346,8 +36431,8 @@ var Launcher = class {
|
|
|
35346
36431
|
for (const [path, mode] of layout) {
|
|
35347
36432
|
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
35348
36433
|
const real = await this.realLayoutPath(path);
|
|
35349
|
-
const
|
|
35350
|
-
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 });
|
|
35351
36436
|
}
|
|
35352
36437
|
}
|
|
35353
36438
|
async realLayoutPath(given) {
|
|
@@ -35481,10 +36566,10 @@ var Launcher = class {
|
|
|
35481
36566
|
}
|
|
35482
36567
|
async mountFstab() {
|
|
35483
36568
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
35484
|
-
const
|
|
35485
|
-
for (const { spec } of
|
|
36569
|
+
const lines3 = parseFstab(text);
|
|
36570
|
+
for (const { spec } of lines3) this.mounts.note(spec, "pending");
|
|
35486
36571
|
return mountFstab(
|
|
35487
|
-
|
|
36572
|
+
lines3,
|
|
35488
36573
|
(spec) => this.mount(spec).catch((err) => {
|
|
35489
36574
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
35490
36575
|
throw err;
|
|
@@ -36190,13 +37275,13 @@ function unsendableHeaders(url, names2, hint) {
|
|
|
36190
37275
|
function refuseUnsendable(url, out, hint) {
|
|
36191
37276
|
if (!platformFilters()) return;
|
|
36192
37277
|
const kept = new Request(PROBE, { headers: out }).headers;
|
|
36193
|
-
const
|
|
37278
|
+
const refused5 = [];
|
|
36194
37279
|
for (const name2 of new Set(out.keys())) {
|
|
36195
37280
|
if (kept.has(name2)) continue;
|
|
36196
37281
|
if (TRANSPORT_OWN.has(name2)) out.delete(name2);
|
|
36197
|
-
else
|
|
37282
|
+
else refused5.push(name2);
|
|
36198
37283
|
}
|
|
36199
|
-
if (
|
|
37284
|
+
if (refused5.length > 0) throw unsendableHeaders(url, refused5, hint);
|
|
36200
37285
|
}
|
|
36201
37286
|
function streamOf(body) {
|
|
36202
37287
|
const it = body[Symbol.asyncIterator]();
|
|
@@ -36508,7 +37593,7 @@ function serveTransport(peer, transport, {
|
|
|
36508
37593
|
}
|
|
36509
37594
|
|
|
36510
37595
|
// src/client/protocol.ts
|
|
36511
|
-
var PROTOCOL = [1,
|
|
37596
|
+
var PROTOCOL = [1, 18];
|
|
36512
37597
|
var FS_METHODS = [
|
|
36513
37598
|
"readFile",
|
|
36514
37599
|
"writeFile",
|
|
@@ -36768,8 +37853,10 @@ function hostRegistries(send2, gone, port) {
|
|
|
36768
37853
|
const cdps = cdpRegistry(send2, gone);
|
|
36769
37854
|
const facades = facadeRegistry(send2, gone);
|
|
36770
37855
|
const requests = requestBinding(send2, port);
|
|
37856
|
+
const devices = deviceBinding(send2, port);
|
|
36771
37857
|
return {
|
|
36772
37858
|
requests,
|
|
37859
|
+
devices,
|
|
36773
37860
|
answer(l, req) {
|
|
36774
37861
|
if (req.op === "serve-cdp") return cdps.serve(l, req);
|
|
36775
37862
|
if (req.op === "unserve-cdp") return cdps.drop(req.registration);
|
|
@@ -36778,6 +37865,7 @@ function hostRegistries(send2, gone, port) {
|
|
|
36778
37865
|
},
|
|
36779
37866
|
close() {
|
|
36780
37867
|
requests.close();
|
|
37868
|
+
devices.close();
|
|
36781
37869
|
cdps.close();
|
|
36782
37870
|
facades.close();
|
|
36783
37871
|
}
|
|
@@ -36891,6 +37979,28 @@ function answered(pending, req, reply, detach) {
|
|
|
36891
37979
|
}
|
|
36892
37980
|
);
|
|
36893
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
|
+
}
|
|
36894
38004
|
function serveClient(port, host) {
|
|
36895
38005
|
let state = "new";
|
|
36896
38006
|
let self;
|
|
@@ -36924,9 +38034,9 @@ function serveClient(port, host) {
|
|
|
36924
38034
|
ended.resolve();
|
|
36925
38035
|
}
|
|
36926
38036
|
async function hello(message3) {
|
|
36927
|
-
const
|
|
36928
|
-
if (
|
|
36929
|
-
send2({ hello: { protocol: PROTOCOL, error:
|
|
38037
|
+
const refused5 = await helloRefused(host, hosts, message3);
|
|
38038
|
+
if (refused5) {
|
|
38039
|
+
send2({ hello: { protocol: PROTOCOL, error: refused5 } });
|
|
36930
38040
|
detach();
|
|
36931
38041
|
return;
|
|
36932
38042
|
}
|
|
@@ -37121,14 +38231,14 @@ async function followRedirects(send2, request) {
|
|
|
37121
38231
|
var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
|
|
37122
38232
|
var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
|
|
37123
38233
|
var resolveHost = async (host) => (await lookup2(host, { all: true, verbatim: true })).map((a) => a.address);
|
|
37124
|
-
function ipv4Place(
|
|
37125
|
-
const [a, b] =
|
|
38234
|
+
function ipv4Place(address2) {
|
|
38235
|
+
const [a, b] = address2.split(".").map(Number);
|
|
37126
38236
|
if (a === 127 || a === 0) return "loopback";
|
|
37127
38237
|
if (a === 169 && b === 254) return "link-local";
|
|
37128
38238
|
return void 0;
|
|
37129
38239
|
}
|
|
37130
|
-
function placeOf(
|
|
37131
|
-
const plain3 =
|
|
38240
|
+
function placeOf(address2) {
|
|
38241
|
+
const plain3 = address2.replace(/^\[(.*)\]$/, "$1").toLowerCase();
|
|
37132
38242
|
const kind = isIP3(plain3);
|
|
37133
38243
|
if (kind === 4) return ipv4Place(plain3);
|
|
37134
38244
|
if (kind !== 6) return void 0;
|
|
@@ -37144,12 +38254,12 @@ function ipv4Of(words3) {
|
|
|
37144
38254
|
const [high = 0, low = 0] = words3.slice(6);
|
|
37145
38255
|
return [high >> 8, high & 255, low >> 8, low & 255].join(".");
|
|
37146
38256
|
}
|
|
37147
|
-
function hextets2(
|
|
37148
|
-
const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(
|
|
37149
|
-
let text =
|
|
38257
|
+
function hextets2(address2) {
|
|
38258
|
+
const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address2)?.[1];
|
|
38259
|
+
let text = address2;
|
|
37150
38260
|
if (dotted) {
|
|
37151
38261
|
const [a, b, c, d] = dotted.split(".").map(Number);
|
|
37152
|
-
text = `${
|
|
38262
|
+
text = `${address2.slice(0, -dotted.length)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
|
|
37153
38263
|
}
|
|
37154
38264
|
const [head = "", tail2] = text.split("::");
|
|
37155
38265
|
const part = (side) => side ? side.split(":").map((w) => Number.parseInt(w, 16)) : [];
|
|
@@ -37170,13 +38280,13 @@ function allows(policy, port) {
|
|
|
37170
38280
|
if (Array.isArray(policy)) return policy.includes(port);
|
|
37171
38281
|
return policy === true && !DEVTOOLS_PORTS.includes(port);
|
|
37172
38282
|
}
|
|
37173
|
-
function refusal2(url,
|
|
37174
|
-
const where = `${
|
|
38283
|
+
function refusal2(url, address2, place) {
|
|
38284
|
+
const where = `${address2}:${portOf(url)}`;
|
|
37175
38285
|
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
37176
38286
|
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
37177
38287
|
}
|
|
37178
|
-
function connectRefusal(
|
|
37179
|
-
const where = `${
|
|
38288
|
+
function connectRefusal(address2, port, place) {
|
|
38289
|
+
const where = `${address2}:${port}`;
|
|
37180
38290
|
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
37181
38291
|
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
37182
38292
|
}
|
|
@@ -37188,11 +38298,11 @@ async function addressesOf(host, resolve2) {
|
|
|
37188
38298
|
}
|
|
37189
38299
|
async function checkDestination(target, policy, resolve2 = resolveHost) {
|
|
37190
38300
|
const url = new URL(target);
|
|
37191
|
-
for (const
|
|
37192
|
-
const place = placeOf(
|
|
38301
|
+
for (const address2 of await addressesOf(url.hostname, resolve2)) {
|
|
38302
|
+
const place = placeOf(address2);
|
|
37193
38303
|
if (!place) continue;
|
|
37194
38304
|
if (place === "loopback" && allows(policy, portOf(url))) continue;
|
|
37195
|
-
throw refusal2(url,
|
|
38305
|
+
throw refusal2(url, address2, place);
|
|
37196
38306
|
}
|
|
37197
38307
|
}
|
|
37198
38308
|
function guarded3(check, signal) {
|
|
@@ -37216,9 +38326,9 @@ function envProxyEnabled() {
|
|
|
37216
38326
|
}
|
|
37217
38327
|
function normaliseProxyUrl(url) {
|
|
37218
38328
|
try {
|
|
37219
|
-
const
|
|
37220
|
-
const host =
|
|
37221
|
-
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");
|
|
37222
38332
|
return `${host}:${port}`;
|
|
37223
38333
|
} catch {
|
|
37224
38334
|
return void 0;
|
|
@@ -37254,8 +38364,8 @@ function ensureSubscribed() {
|
|
|
37254
38364
|
const entry = loopbackStore.getStore();
|
|
37255
38365
|
if (!entry) return;
|
|
37256
38366
|
const { socket, connectParams } = message3;
|
|
37257
|
-
const
|
|
37258
|
-
if (!
|
|
38367
|
+
const address2 = socket.remoteAddress;
|
|
38368
|
+
if (!address2) {
|
|
37259
38369
|
const err2 = connectRefusal("unknown", 0, "loopback");
|
|
37260
38370
|
entry.refused = err2;
|
|
37261
38371
|
socket.destroy(err2);
|
|
@@ -37270,11 +38380,11 @@ function ensureSubscribed() {
|
|
|
37270
38380
|
}
|
|
37271
38381
|
return;
|
|
37272
38382
|
}
|
|
37273
|
-
const place = placeOf(
|
|
38383
|
+
const place = placeOf(address2);
|
|
37274
38384
|
if (!place) return;
|
|
37275
38385
|
const port = socket.remotePort ?? 0;
|
|
37276
38386
|
if (place === "loopback" && allows(entry.policy, port)) return;
|
|
37277
|
-
const err = connectRefusal(
|
|
38387
|
+
const err = connectRefusal(address2, port, place);
|
|
37278
38388
|
entry.refused = err;
|
|
37279
38389
|
socket.destroy(err);
|
|
37280
38390
|
});
|
|
@@ -37810,11 +38920,11 @@ function indexOf(haystack, needle) {
|
|
|
37810
38920
|
return -1;
|
|
37811
38921
|
}
|
|
37812
38922
|
function parseHead2(bytes) {
|
|
37813
|
-
const [statusLine = "", ...
|
|
38923
|
+
const [statusLine = "", ...lines3] = latin13.decode(bytes).split("\r\n");
|
|
37814
38924
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
37815
38925
|
if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
37816
38926
|
const headers = [];
|
|
37817
|
-
for (const line of
|
|
38927
|
+
for (const line of lines3) {
|
|
37818
38928
|
const colon = line.indexOf(":");
|
|
37819
38929
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
37820
38930
|
}
|
|
@@ -37883,13 +38993,13 @@ function stream(chunks2, socket, signal) {
|
|
|
37883
38993
|
async function requestBytes(request) {
|
|
37884
38994
|
const url = new URL(request.url);
|
|
37885
38995
|
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
|
|
37886
|
-
const
|
|
38996
|
+
const lines3 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
37887
38997
|
request.headers.forEach((value2, name2) => {
|
|
37888
|
-
if (!OWN_HEADERS2.has(name2))
|
|
38998
|
+
if (!OWN_HEADERS2.has(name2)) lines3.push(`${name2}: ${value2}`);
|
|
37889
38999
|
});
|
|
37890
|
-
if (body)
|
|
37891
|
-
|
|
37892
|
-
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"));
|
|
37893
39003
|
const out = new Uint8Array(head.length + (body?.length ?? 0));
|
|
37894
39004
|
out.set(head);
|
|
37895
39005
|
if (body) out.set(body, head.length);
|
|
@@ -38030,8 +39140,10 @@ var Channel = class {
|
|
|
38030
39140
|
return this.request(call).done;
|
|
38031
39141
|
}
|
|
38032
39142
|
requests;
|
|
39143
|
+
devices;
|
|
38033
39144
|
receive(data) {
|
|
38034
39145
|
if (this.requests?.handle(data)) return;
|
|
39146
|
+
if (this.devices?.handle(data)) return;
|
|
38035
39147
|
if (data.traits && data.id === void 0) {
|
|
38036
39148
|
if (this.remote) this.remote.traits = data.traits;
|
|
38037
39149
|
for (const listener of [...this.traitsListeners]) listener(data.traits);
|
|
@@ -38236,7 +39348,7 @@ async function sudoRunOn(channel, replay, { stdout, stderr, tag }) {
|
|
|
38236
39348
|
return { pid, pgid: pid, ended: endedOf(done4, () => endedBy) };
|
|
38237
39349
|
}
|
|
38238
39350
|
function endedOf(done4, signal) {
|
|
38239
|
-
return
|
|
39351
|
+
return quiet2(
|
|
38240
39352
|
done4.then((status) => {
|
|
38241
39353
|
const by = signal();
|
|
38242
39354
|
return { status, ...by ? { signal: by } : {} };
|
|
@@ -38366,9 +39478,14 @@ function since(channel, minor, what, run2) {
|
|
|
38366
39478
|
}
|
|
38367
39479
|
return run2();
|
|
38368
39480
|
}
|
|
38369
|
-
async function greet(channel, lock, timeoutMs, requests) {
|
|
39481
|
+
async function greet(channel, lock, timeoutMs, requests, devices) {
|
|
38370
39482
|
channel.post({
|
|
38371
|
-
hello: {
|
|
39483
|
+
hello: {
|
|
39484
|
+
protocol: PROTOCOL,
|
|
39485
|
+
...lock ? { lock } : {},
|
|
39486
|
+
...requests ? { requests } : {},
|
|
39487
|
+
...devices ? { devices: true } : {}
|
|
39488
|
+
}
|
|
38372
39489
|
});
|
|
38373
39490
|
const timer = setTimeout(
|
|
38374
39491
|
() => channel.end(
|
|
@@ -38382,9 +39499,9 @@ async function greet(channel, lock, timeoutMs, requests) {
|
|
|
38382
39499
|
} finally {
|
|
38383
39500
|
clearTimeout(timer);
|
|
38384
39501
|
}
|
|
38385
|
-
const
|
|
38386
|
-
if (
|
|
38387
|
-
const error = new Error(
|
|
39502
|
+
const refused5 = hello.error ?? versionError(hello.protocol);
|
|
39503
|
+
if (refused5) {
|
|
39504
|
+
const error = new Error(refused5);
|
|
38388
39505
|
channel.end(error);
|
|
38389
39506
|
throw error;
|
|
38390
39507
|
}
|
|
@@ -38396,11 +39513,14 @@ async function attachKernel(port, options = {}) {
|
|
|
38396
39513
|
const channel = new Channel(port, own.release);
|
|
38397
39514
|
const requests = options.requests;
|
|
38398
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);
|
|
38399
39518
|
const hello = await greet(
|
|
38400
39519
|
channel,
|
|
38401
39520
|
own.name,
|
|
38402
39521
|
options.timeoutMs ?? 1e4,
|
|
38403
|
-
requests ? kindsOf(requests) : void 0
|
|
39522
|
+
requests ? kindsOf(requests) : void 0,
|
|
39523
|
+
devices !== void 0
|
|
38404
39524
|
);
|
|
38405
39525
|
channel.protocol = hello.protocol;
|
|
38406
39526
|
if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));
|
|
@@ -38727,7 +39847,8 @@ function pidAlive(pid) {
|
|
|
38727
39847
|
}
|
|
38728
39848
|
}
|
|
38729
39849
|
var nodeSessions = 0;
|
|
38730
|
-
|
|
39850
|
+
var portSessions = /* @__PURE__ */ new WeakMap();
|
|
39851
|
+
function nodeClient(launcher, clients, { user, tag, requests, devices }) {
|
|
38731
39852
|
tagOption(tag);
|
|
38732
39853
|
const { port1, port2 } = new MessageChannel3();
|
|
38733
39854
|
const session = ++nodeSessions;
|
|
@@ -38736,17 +39857,27 @@ function nodeClient(launcher, clients, { user, tag, requests }) {
|
|
|
38736
39857
|
signal: signalNumber,
|
|
38737
39858
|
session,
|
|
38738
39859
|
...requests === "attach" ? { attachRequests: true } : {},
|
|
39860
|
+
...devices === "attach" ? { attachDevices: true } : {},
|
|
38739
39861
|
...user !== void 0 ? { user } : {},
|
|
38740
39862
|
...tag !== void 0 ? { tag } : {}
|
|
38741
39863
|
});
|
|
38742
39864
|
clients.add(served2);
|
|
38743
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;
|
|
38744
39867
|
void served2.closed.then(() => {
|
|
38745
39868
|
clients.delete(served2);
|
|
38746
39869
|
unbind?.();
|
|
39870
|
+
unbindDevices?.();
|
|
38747
39871
|
});
|
|
39872
|
+
portSessions.set(port2, session);
|
|
38748
39873
|
return port2;
|
|
38749
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
|
+
}
|
|
38750
39881
|
function checkedRequests(hook) {
|
|
38751
39882
|
const kinds = kindsOf(hook);
|
|
38752
39883
|
return { kinds, handle: (request) => hook.handle(request) };
|
|
@@ -38773,7 +39904,23 @@ async function nodeSudoRun(launcher, guard2, replay, options) {
|
|
|
38773
39904
|
const { status, signal } = await running.ended;
|
|
38774
39905
|
return { status, ...signalField(signal) };
|
|
38775
39906
|
});
|
|
38776
|
-
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
|
+
};
|
|
38777
39924
|
}
|
|
38778
39925
|
async function createNodeKernel(options = {}) {
|
|
38779
39926
|
const hooks = sealedHooks(options.sealed);
|
|
@@ -38819,9 +39966,7 @@ async function createNodeKernel(options = {}) {
|
|
|
38819
39966
|
...options.protect ? { protect: options.protect } : {},
|
|
38820
39967
|
...hooks.request ? { sealed: hooks.request } : {},
|
|
38821
39968
|
...hooks.fetch ? { sealedFetch: hooks.fetch } : {},
|
|
38822
|
-
...options
|
|
38823
|
-
...options.requests ? { embedder: checkedRequests(options.requests) } : {},
|
|
38824
|
-
...options.approve ? { approve: options.approve } : {},
|
|
39969
|
+
...pageHooks(options),
|
|
38825
39970
|
...options.sudo?.clock ? { sudoClock: options.sudo.clock } : {},
|
|
38826
39971
|
...options.wasmMemories ? { wasmMemories: options.wasmMemories } : {},
|
|
38827
39972
|
cron: { lock: cronLock(root), ...options.cron?.clock ? { clock: options.cron.clock } : {} }
|
|
@@ -38866,8 +40011,8 @@ async function createNodeKernel(options = {}) {
|
|
|
38866
40011
|
);
|
|
38867
40012
|
return {
|
|
38868
40013
|
pid: session.pid,
|
|
38869
|
-
exited:
|
|
38870
|
-
ended:
|
|
40014
|
+
exited: quiet2(guard2(() => session.exited)),
|
|
40015
|
+
ended: quiet2(
|
|
38871
40016
|
guard2(async () => ({ status: await session.exited, ...signalField(session.termsig()) }))
|
|
38872
40017
|
),
|
|
38873
40018
|
get onData() {
|
|
@@ -38905,6 +40050,7 @@ async function createNodeKernel(options = {}) {
|
|
|
38905
40050
|
updateTransportTraits: () => guard2(async () => launcher.updateTransportTraits()),
|
|
38906
40051
|
...nodeAccounts(launcher, guard2),
|
|
38907
40052
|
sudoRun: (replay, sudoOptions = {}) => nodeSudoRun(launcher, guard2, replay, sudoOptions),
|
|
40053
|
+
devices: nodeDevices(launcher, guard2),
|
|
38908
40054
|
async connect(connectOptions = {}) {
|
|
38909
40055
|
if (terminated) throw new Error(TERMINATED);
|
|
38910
40056
|
const { user } = connectOptions;
|