@ai-ecoverse/slicc-kernel 1.152.2 → 1.154.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 +28 -3
- package/dist/driver-worker.js +147 -10
- package/dist/driver-worker.js.map +4 -4
- package/dist/index.js +168 -14
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +844 -167
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +147 -10
- package/dist/node-driver-worker.js.map +4 -4
- package/dist/node-process-worker.js +81 -24
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +982 -182
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +81 -24
- package/dist/process-worker.js.map +3 -3
- package/dist/types/client/attach.d.ts +2 -0
- package/dist/types/client/protocol.d.ts +4 -0
- package/dist/types/client/serve-client.d.ts +2 -0
- package/dist/types/embedder-relay.d.ts +58 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/kernel/embedder.d.ts +100 -0
- package/dist/types/kernel/fd-table.d.ts +3 -1
- package/dist/types/kernel/host.d.ts +8 -0
- package/dist/types/kernel/net/tls-client.d.ts +4 -1
- package/dist/types/kernel/net/tls-engine.d.ts +5 -2
- package/dist/types/kernel/net/tls-server.d.ts +2 -0
- package/dist/types/kernel/net/tls-uplink.d.ts +2 -0
- package/dist/types/kernel/process.d.ts +17 -0
- package/dist/types/kernel/socket-syscalls.d.ts +3 -0
- package/dist/types/launcher.d.ts +13 -0
- package/dist/types/node.d.ts +3 -0
- package/dist/types/process/js/js-net.d.ts +3 -0
- package/dist/types/process/kernel-streams.d.ts +2 -1
- package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
- package/dist/types/serve.d.ts +3 -0
- package/package.json +4 -4
package/dist/kernel-worker.js
CHANGED
|
@@ -1922,9 +1922,9 @@ var OpfsFs = class {
|
|
|
1922
1922
|
this.lookups++;
|
|
1923
1923
|
if (split(path).length === 0) return this.root;
|
|
1924
1924
|
const cached = this.files.get(path);
|
|
1925
|
-
const
|
|
1926
|
-
if (cached && !
|
|
1927
|
-
if (cached &&
|
|
1925
|
+
const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
|
|
1926
|
+
if (cached && !fresh3) this.files.delete(path);
|
|
1927
|
+
if (cached && fresh3) {
|
|
1928
1928
|
if (this.glanced(cached)) return this.remember(path, cached);
|
|
1929
1929
|
try {
|
|
1930
1930
|
await this.glance(cached);
|
|
@@ -2142,8 +2142,8 @@ var OpfsFs = class {
|
|
|
2142
2142
|
glanced(handle) {
|
|
2143
2143
|
if (this.exact > 0) return void 0;
|
|
2144
2144
|
const seen = this.glances.get(handle);
|
|
2145
|
-
const
|
|
2146
|
-
return
|
|
2145
|
+
const fresh3 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
|
|
2146
|
+
return fresh3 ? seen.file : void 0;
|
|
2147
2147
|
}
|
|
2148
2148
|
async glance(handle) {
|
|
2149
2149
|
const at = performance.now();
|
|
@@ -2333,13 +2333,13 @@ var OpfsFs = class {
|
|
|
2333
2333
|
async keptFor(path) {
|
|
2334
2334
|
const at = normalizePath(path);
|
|
2335
2335
|
const cached = this.files.get(at);
|
|
2336
|
-
const
|
|
2337
|
-
const [target, existed] =
|
|
2336
|
+
const fresh3 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
|
|
2337
|
+
const [target, existed] = fresh3 ? [at, true] : await this.writeTarget(path);
|
|
2338
2338
|
if (!existed) return [void 0, target, false];
|
|
2339
2339
|
this.refuse(target);
|
|
2340
2340
|
const [entry] = await this.meta.get([target]);
|
|
2341
2341
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
2342
|
-
const kept =
|
|
2342
|
+
const kept = fresh3 ? cached : this.files.get(target);
|
|
2343
2343
|
const usable2 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
2344
2344
|
return [usable2 ? kept : void 0, target, true];
|
|
2345
2345
|
}
|
|
@@ -4520,6 +4520,102 @@ function portCdpHook(ask, gone) {
|
|
|
4520
4520
|
});
|
|
4521
4521
|
}
|
|
4522
4522
|
|
|
4523
|
+
// src/embedder-relay.ts
|
|
4524
|
+
function fresh2(bytes) {
|
|
4525
|
+
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
|
|
4526
|
+
}
|
|
4527
|
+
function embedderAsker(port) {
|
|
4528
|
+
const asks = /* @__PURE__ */ new Map();
|
|
4529
|
+
let next3 = 0;
|
|
4530
|
+
const post = (message3, transfer = []) => port.postMessage({ embedder: message3 }, transfer);
|
|
4531
|
+
const push = (asked, item) => {
|
|
4532
|
+
asked.chunks.push(item);
|
|
4533
|
+
asked.wake?.();
|
|
4534
|
+
};
|
|
4535
|
+
const reply = (id2, asked) => new ReadableStream(
|
|
4536
|
+
{
|
|
4537
|
+
pull: async (controller) => {
|
|
4538
|
+
if (asked.chunks.length === 0) {
|
|
4539
|
+
post({ id: id2, pull: true });
|
|
4540
|
+
await new Promise((resolve2) => {
|
|
4541
|
+
asked.wake = resolve2;
|
|
4542
|
+
});
|
|
4543
|
+
}
|
|
4544
|
+
const item = asked.chunks.shift();
|
|
4545
|
+
if (item instanceof Uint8Array) controller.enqueue(item);
|
|
4546
|
+
else if (item === null || item === void 0) {
|
|
4547
|
+
asks.delete(id2);
|
|
4548
|
+
controller.close();
|
|
4549
|
+
} else {
|
|
4550
|
+
asks.delete(id2);
|
|
4551
|
+
controller.error(Object.assign(new Error(item.message), { name: item.name }));
|
|
4552
|
+
}
|
|
4553
|
+
},
|
|
4554
|
+
cancel: (reason) => {
|
|
4555
|
+
asks.delete(id2);
|
|
4556
|
+
post({ id: id2, cancel: String(reason ?? "closed") });
|
|
4557
|
+
}
|
|
4558
|
+
},
|
|
4559
|
+
{ highWaterMark: 0 }
|
|
4560
|
+
);
|
|
4561
|
+
const sendBody = async (id2, asked) => {
|
|
4562
|
+
const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
|
|
4563
|
+
if (got.done) post({ id: id2, bodyEnd: true });
|
|
4564
|
+
else {
|
|
4565
|
+
const bytes = fresh2(got.value);
|
|
4566
|
+
post({ id: id2, body: bytes }, [bytes.buffer]);
|
|
4567
|
+
}
|
|
4568
|
+
};
|
|
4569
|
+
const hookFor = (client, kinds) => ({
|
|
4570
|
+
kinds,
|
|
4571
|
+
handle: async (request3) => {
|
|
4572
|
+
const id2 = ++next3;
|
|
4573
|
+
const asked = {
|
|
4574
|
+
ready: Promise.withResolvers(),
|
|
4575
|
+
chunks: [],
|
|
4576
|
+
wake: void 0,
|
|
4577
|
+
body: request3.body.getReader(),
|
|
4578
|
+
progress: request3.progress
|
|
4579
|
+
};
|
|
4580
|
+
asks.set(id2, asked);
|
|
4581
|
+
const cancel = () => {
|
|
4582
|
+
asks.delete(id2);
|
|
4583
|
+
post({
|
|
4584
|
+
id: id2,
|
|
4585
|
+
cancel: String(request3.signal.reason?.message ?? "closed")
|
|
4586
|
+
});
|
|
4587
|
+
asked.ready.reject(request3.signal.reason);
|
|
4588
|
+
push(asked, { name: "AbortError", message: "cancelled" });
|
|
4589
|
+
};
|
|
4590
|
+
request3.signal.addEventListener("abort", cancel, { once: true });
|
|
4591
|
+
const { kind, options, stamp } = request3;
|
|
4592
|
+
post({ ask: { id: id2, client, kind, options, stamp } });
|
|
4593
|
+
await asked.ready.promise;
|
|
4594
|
+
return reply(id2, asked);
|
|
4595
|
+
}
|
|
4596
|
+
});
|
|
4597
|
+
const answer2 = (message3) => {
|
|
4598
|
+
const asked = asks.get(message3.id);
|
|
4599
|
+
if (!asked) return;
|
|
4600
|
+
if ("ready" in message3) asked.ready.resolve();
|
|
4601
|
+
else if ("chunk" in message3) push(asked, message3.chunk);
|
|
4602
|
+
else if ("done" in message3) push(asked, null);
|
|
4603
|
+
else if ("error" in message3) {
|
|
4604
|
+
asked.ready.reject(
|
|
4605
|
+
Object.assign(new Error(message3.error.message), { name: message3.error.name })
|
|
4606
|
+
);
|
|
4607
|
+
asks.delete(message3.id);
|
|
4608
|
+
void asked.body.cancel(message3.error.message).catch(() => void 0);
|
|
4609
|
+
push(asked, message3.error);
|
|
4610
|
+
} else if ("progress" in message3) asked.progress(message3.progress);
|
|
4611
|
+
else void sendBody(message3.id, asked);
|
|
4612
|
+
};
|
|
4613
|
+
return { hookFor, answer: answer2 };
|
|
4614
|
+
}
|
|
4615
|
+
function embedderReplyOf(data) {
|
|
4616
|
+
return data?.embedderReply;
|
|
4617
|
+
}
|
|
4618
|
+
|
|
4523
4619
|
// src/facade/port.ts
|
|
4524
4620
|
var FACADE_GONE = "the facade host is gone";
|
|
4525
4621
|
function portFacadeHook(ask, gone) {
|
|
@@ -5347,6 +5443,7 @@ function kernelFdKind(file) {
|
|
|
5347
5443
|
if (file.tty) return "tty";
|
|
5348
5444
|
if (file.sealed) return "sealed";
|
|
5349
5445
|
if (file.clipboard) return "clipboard";
|
|
5446
|
+
if (file.embedder) return "embedder";
|
|
5350
5447
|
return file.seek ? "file" : "stream";
|
|
5351
5448
|
}
|
|
5352
5449
|
var FdTable = class _FdTable {
|
|
@@ -7231,7 +7328,7 @@ var UserDb = class {
|
|
|
7231
7328
|
joined(name2, extra);
|
|
7232
7329
|
group.push({ name: name2, gid: uid, members: [] });
|
|
7233
7330
|
passwd.push(row);
|
|
7234
|
-
const
|
|
7331
|
+
const fresh3 = !await this.fs.exists(home);
|
|
7235
7332
|
await this.fs.mkdir(home, { recursive: true });
|
|
7236
7333
|
try {
|
|
7237
7334
|
await this.provision(row, this.umaskFor(options), shared);
|
|
@@ -7239,7 +7336,7 @@ var UserDb = class {
|
|
|
7239
7336
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
7240
7337
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
7241
7338
|
} catch (err) {
|
|
7242
|
-
if (
|
|
7339
|
+
if (fresh3) await this.fs.rm(home, { recursive: true, force: true });
|
|
7243
7340
|
throw err;
|
|
7244
7341
|
}
|
|
7245
7342
|
remember();
|
|
@@ -7303,7 +7400,7 @@ var UserDb = class {
|
|
|
7303
7400
|
};
|
|
7304
7401
|
|
|
7305
7402
|
// src/client/protocol.ts
|
|
7306
|
-
var PROTOCOL = [1,
|
|
7403
|
+
var PROTOCOL = [1, 13];
|
|
7307
7404
|
var FS_METHODS = [
|
|
7308
7405
|
"readFile",
|
|
7309
7406
|
"writeFile",
|
|
@@ -7470,14 +7567,15 @@ async function runAs(l, self, requested) {
|
|
|
7470
7567
|
function clientCred(self) {
|
|
7471
7568
|
return self ? credOf(self) : copyCred(ROOT);
|
|
7472
7569
|
}
|
|
7473
|
-
async function runTagged(l, self, options, tag) {
|
|
7570
|
+
async function runTagged(l, self, options, tag, session = -1) {
|
|
7474
7571
|
tagOption(options.tag);
|
|
7475
7572
|
const tags = withTag(withTag(void 0, tag), options.tag);
|
|
7476
7573
|
return {
|
|
7477
7574
|
user: await runAs(l, self, options.user),
|
|
7478
7575
|
tag,
|
|
7479
7576
|
tags,
|
|
7480
|
-
tagBase: tag === void 0 ? 0 : 1
|
|
7577
|
+
tagBase: tag === void 0 ? 0 : 1,
|
|
7578
|
+
session
|
|
7481
7579
|
};
|
|
7482
7580
|
}
|
|
7483
7581
|
function processes(l, self) {
|
|
@@ -7558,10 +7656,12 @@ function facadeRegistry(send2, gone) {
|
|
|
7558
7656
|
}
|
|
7559
7657
|
};
|
|
7560
7658
|
}
|
|
7561
|
-
function hostRegistries(send2, gone) {
|
|
7659
|
+
function hostRegistries(send2, gone, port) {
|
|
7562
7660
|
const cdps = cdpRegistry(send2, gone);
|
|
7563
7661
|
const facades = facadeRegistry(send2, gone);
|
|
7662
|
+
const requests = requestBinding(send2, port);
|
|
7564
7663
|
return {
|
|
7664
|
+
requests,
|
|
7565
7665
|
answer(l, req) {
|
|
7566
7666
|
if (req.op === "serve-cdp") return cdps.serve(l, req);
|
|
7567
7667
|
if (req.op === "unserve-cdp") return cdps.drop(req.registration);
|
|
@@ -7569,6 +7669,7 @@ function hostRegistries(send2, gone) {
|
|
|
7569
7669
|
return facades.drop(req.registration);
|
|
7570
7670
|
},
|
|
7571
7671
|
close() {
|
|
7672
|
+
requests.close();
|
|
7572
7673
|
cdps.close();
|
|
7573
7674
|
facades.close();
|
|
7574
7675
|
}
|
|
@@ -7603,6 +7704,28 @@ var TRAITS_WATCH = -1;
|
|
|
7603
7704
|
function traitsWatch(launcher, transport, send2) {
|
|
7604
7705
|
return launcher.onTraits(() => send2({ traits: transport.traits }));
|
|
7605
7706
|
}
|
|
7707
|
+
function requestBinding(send2, port) {
|
|
7708
|
+
const asker = embedderAsker({ postMessage: (m, t) => send2(m, t) });
|
|
7709
|
+
let unbind;
|
|
7710
|
+
port.addEventListener("message", (event) => {
|
|
7711
|
+
const reply = embedderReplyOf(event.data);
|
|
7712
|
+
if (reply) asker.answer(reply);
|
|
7713
|
+
});
|
|
7714
|
+
return {
|
|
7715
|
+
async accept(host, hello) {
|
|
7716
|
+
if (!hello.requests) return void 0;
|
|
7717
|
+
if (!host.attachRequests || host.session === void 0)
|
|
7718
|
+
return "EPERM: this port may not answer requests";
|
|
7719
|
+
const launcher = await host.launcher();
|
|
7720
|
+
unbind = launcher.embedder.register(
|
|
7721
|
+
host.session,
|
|
7722
|
+
asker.hookFor(host.session, hello.requests)
|
|
7723
|
+
);
|
|
7724
|
+
return void 0;
|
|
7725
|
+
},
|
|
7726
|
+
close: () => unbind?.()
|
|
7727
|
+
};
|
|
7728
|
+
}
|
|
7606
7729
|
function serveClient(port, host) {
|
|
7607
7730
|
let state = "new";
|
|
7608
7731
|
let self;
|
|
@@ -7617,7 +7740,7 @@ function serveClient(port, host) {
|
|
|
7617
7740
|
if (state !== "closed") port.postMessage(message3, transfer);
|
|
7618
7741
|
};
|
|
7619
7742
|
const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
|
|
7620
|
-
const hosts = hostRegistries(send2, ended.promise);
|
|
7743
|
+
const hosts = hostRegistries(send2, ended.promise, port);
|
|
7621
7744
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
7622
7745
|
if (state === "closed") return;
|
|
7623
7746
|
send2({ bye });
|
|
@@ -7634,7 +7757,7 @@ function serveClient(port, host) {
|
|
|
7634
7757
|
ended.resolve();
|
|
7635
7758
|
}
|
|
7636
7759
|
async function hello(message3) {
|
|
7637
|
-
const refused3 = versionError(message3.protocol);
|
|
7760
|
+
const refused3 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
|
|
7638
7761
|
if (refused3) {
|
|
7639
7762
|
send2({ hello: { protocol: PROTOCOL, error: refused3 } });
|
|
7640
7763
|
detach();
|
|
@@ -7659,7 +7782,7 @@ function serveClient(port, host) {
|
|
|
7659
7782
|
async function terminal(req, l) {
|
|
7660
7783
|
const session = await l.openTerminal(req.argv, {
|
|
7661
7784
|
...req.options,
|
|
7662
|
-
...await runTagged(l, self, req.options, host.tag),
|
|
7785
|
+
...await runTagged(l, self, req.options, host.tag, host.session),
|
|
7663
7786
|
onData: (bytes) => reply(req.id, { fd: 1, bytes: bytes.slice() })
|
|
7664
7787
|
});
|
|
7665
7788
|
if (state === "closed") {
|
|
@@ -7682,7 +7805,7 @@ function serveClient(port, host) {
|
|
|
7682
7805
|
let leader;
|
|
7683
7806
|
const result = await l.run(req.argv, {
|
|
7684
7807
|
...req.options,
|
|
7685
|
-
...await runTagged(l, self, req.options, host.tag),
|
|
7808
|
+
...await runTagged(l, self, req.options, host.tag, host.session),
|
|
7686
7809
|
collect: false,
|
|
7687
7810
|
onStdout: out(1),
|
|
7688
7811
|
onStderr: out(2),
|
|
@@ -7926,7 +8049,7 @@ function satisfies(version, range2) {
|
|
|
7926
8049
|
}
|
|
7927
8050
|
|
|
7928
8051
|
// src/version.ts
|
|
7929
|
-
var KERNEL_VERSION = true ? "1.
|
|
8052
|
+
var KERNEL_VERSION = true ? "1.154.0" : "0.0.0-dev";
|
|
7930
8053
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7931
8054
|
function procVersion() {
|
|
7932
8055
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -8771,9 +8894,9 @@ var Crontabs = class {
|
|
|
8771
8894
|
const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
|
|
8772
8895
|
if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
|
|
8773
8896
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
8774
|
-
const
|
|
8775
|
-
await fs.writeFile(
|
|
8776
|
-
await fs.rename(
|
|
8897
|
+
const fresh3 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
|
|
8898
|
+
await fs.writeFile(fresh3, json5, owner);
|
|
8899
|
+
await fs.rename(fresh3, TAGS_PATH);
|
|
8777
8900
|
}
|
|
8778
8901
|
async install(path, target, text, caller) {
|
|
8779
8902
|
if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
|
|
@@ -8787,11 +8910,11 @@ var Crontabs = class {
|
|
|
8787
8910
|
await fs.mkdir(SPOOL_DIR, { recursive: true });
|
|
8788
8911
|
if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
|
|
8789
8912
|
}
|
|
8790
|
-
const
|
|
8913
|
+
const fresh3 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
|
|
8791
8914
|
const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
|
|
8792
8915
|
await this.retag(path, text, caller);
|
|
8793
|
-
await fs.writeFile(
|
|
8794
|
-
await fs.rename(
|
|
8916
|
+
await fs.writeFile(fresh3, text, owner);
|
|
8917
|
+
await fs.rename(fresh3, path);
|
|
8795
8918
|
return { text: "" };
|
|
8796
8919
|
}
|
|
8797
8920
|
};
|
|
@@ -9067,15 +9190,15 @@ var UnlinkedKeeper = class {
|
|
|
9067
9190
|
const releases = [];
|
|
9068
9191
|
const take = async () => {
|
|
9069
9192
|
for (; ; ) {
|
|
9070
|
-
const
|
|
9071
|
-
if (
|
|
9072
|
-
|
|
9073
|
-
if (paths.some((path) => path >
|
|
9193
|
+
const fresh3 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
|
|
9194
|
+
if (fresh3.length === 0) return;
|
|
9195
|
+
fresh3.sort();
|
|
9196
|
+
if (paths.some((path) => path > fresh3[0])) {
|
|
9074
9197
|
for (const release of releases.splice(0)) release();
|
|
9075
|
-
|
|
9076
|
-
|
|
9198
|
+
fresh3.push(...paths.splice(0));
|
|
9199
|
+
fresh3.sort();
|
|
9077
9200
|
}
|
|
9078
|
-
for (const path of
|
|
9201
|
+
for (const path of fresh3) {
|
|
9079
9202
|
paths.push(path);
|
|
9080
9203
|
releases.push(await this.gate.acquire(path));
|
|
9081
9204
|
}
|
|
@@ -9410,10 +9533,10 @@ var CronService = class {
|
|
|
9410
9533
|
const text = JSON.stringify(stamp);
|
|
9411
9534
|
const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
|
|
9412
9535
|
if (old === text || old === void 0 && text === EMPTY_STAMP) return;
|
|
9413
|
-
const
|
|
9536
|
+
const fresh3 = `${STAMP_PATH}.new`;
|
|
9414
9537
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
9415
|
-
await fs.writeFile(
|
|
9416
|
-
await fs.rename(
|
|
9538
|
+
await fs.writeFile(fresh3, text, owner);
|
|
9539
|
+
await fs.rename(fresh3, STAMP_PATH);
|
|
9417
9540
|
}
|
|
9418
9541
|
async load() {
|
|
9419
9542
|
const { fs } = this.host;
|
|
@@ -10763,10 +10886,10 @@ var FreshModules = class {
|
|
|
10763
10886
|
this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
|
|
10764
10887
|
}
|
|
10765
10888
|
forget(pid) {
|
|
10766
|
-
for (const [path,
|
|
10889
|
+
for (const [path, fresh3] of this.fresh) if (fresh3.pid === pid) this.fresh.delete(path);
|
|
10767
10890
|
}
|
|
10768
|
-
drop(
|
|
10769
|
-
for (const [key, note] of [...this.fresh]) if (note ===
|
|
10891
|
+
drop(fresh3) {
|
|
10892
|
+
for (const [key, note] of [...this.fresh]) if (note === fresh3) this.fresh.delete(key);
|
|
10770
10893
|
}
|
|
10771
10894
|
moved(from, to) {
|
|
10772
10895
|
const source = normalizePath(from);
|
|
@@ -10775,10 +10898,10 @@ var FreshModules = class {
|
|
|
10775
10898
|
for (const key of [...this.fresh.keys()]) {
|
|
10776
10899
|
if (key === target || within5(key, target)) this.fresh.delete(key);
|
|
10777
10900
|
}
|
|
10778
|
-
for (const [key,
|
|
10901
|
+
for (const [key, fresh3] of [...this.fresh]) {
|
|
10779
10902
|
if (key !== source && !within5(key, source)) continue;
|
|
10780
10903
|
this.fresh.delete(key);
|
|
10781
|
-
this.fresh.set(target + key.slice(source.length),
|
|
10904
|
+
this.fresh.set(target + key.slice(source.length), fresh3);
|
|
10782
10905
|
}
|
|
10783
10906
|
}
|
|
10784
10907
|
wrap(fs) {
|
|
@@ -10788,15 +10911,15 @@ var FreshModules = class {
|
|
|
10788
10911
|
};
|
|
10789
10912
|
const decide = async (given, known) => {
|
|
10790
10913
|
const path = normalizePath(given);
|
|
10791
|
-
const
|
|
10792
|
-
if (!
|
|
10914
|
+
const fresh3 = this.fresh.get(path);
|
|
10915
|
+
if (!fresh3) return;
|
|
10793
10916
|
const bytes = known && known.length >= MAGIC.length ? known : await head(path);
|
|
10794
|
-
const want = isModule(bytes) ? 73 & ~
|
|
10795
|
-
|
|
10796
|
-
if (want ===
|
|
10917
|
+
const want = isModule(bytes) ? 73 & ~fresh3.umask : 0;
|
|
10918
|
+
fresh3.setup = false;
|
|
10919
|
+
if (want === fresh3.added) return;
|
|
10797
10920
|
const mode = (await fs.stat(path)).mode & 4095;
|
|
10798
|
-
await fs.chmod(path, mode & ~
|
|
10799
|
-
|
|
10921
|
+
await fs.chmod(path, mode & ~fresh3.added | want);
|
|
10922
|
+
fresh3.added = want;
|
|
10800
10923
|
};
|
|
10801
10924
|
const overrides = {
|
|
10802
10925
|
writeFile: async (path, content, owner) => {
|
|
@@ -10809,9 +10932,9 @@ var FreshModules = class {
|
|
|
10809
10932
|
}
|
|
10810
10933
|
},
|
|
10811
10934
|
chmod: async (path, mode) => {
|
|
10812
|
-
const
|
|
10813
|
-
if (
|
|
10814
|
-
else if (
|
|
10935
|
+
const fresh3 = this.fresh.get(normalizePath(path));
|
|
10936
|
+
if (fresh3?.setup && (mode & 4095) === (438 & ~fresh3.umask)) fresh3.setup = false;
|
|
10937
|
+
else if (fresh3) this.drop(fresh3);
|
|
10815
10938
|
await fs.chmod(path, mode);
|
|
10816
10939
|
},
|
|
10817
10940
|
rm: async (path, options) => {
|
|
@@ -10828,8 +10951,8 @@ var FreshModules = class {
|
|
|
10828
10951
|
link: async (from, to, follow2) => {
|
|
10829
10952
|
await fs.link(from, to, follow2);
|
|
10830
10953
|
const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
|
|
10831
|
-
const
|
|
10832
|
-
if (
|
|
10954
|
+
const fresh3 = this.fresh.get(normalizePath(source));
|
|
10955
|
+
if (fresh3) this.fresh.set(normalizePath(to), fresh3);
|
|
10833
10956
|
}
|
|
10834
10957
|
};
|
|
10835
10958
|
const ranged2 = rangedOps(fs);
|
|
@@ -11147,7 +11270,7 @@ var FsWatchers = class {
|
|
|
11147
11270
|
};
|
|
11148
11271
|
const content = (run2, types) => async (...args) => {
|
|
11149
11272
|
const at = normalizePath(args[0]);
|
|
11150
|
-
const
|
|
11273
|
+
const fresh3 = this.covers(at) && !await fs.exists(at).catch(() => true);
|
|
11151
11274
|
let done5 = false;
|
|
11152
11275
|
try {
|
|
11153
11276
|
await run2(...args);
|
|
@@ -11156,11 +11279,11 @@ var FsWatchers = class {
|
|
|
11156
11279
|
const real = await realOf(at);
|
|
11157
11280
|
const paths = real === at ? [at] : [at, real];
|
|
11158
11281
|
this.report(paths);
|
|
11159
|
-
if (done5) for (const type of types(
|
|
11282
|
+
if (done5) for (const type of types(fresh3, args)) this.emit(type, paths);
|
|
11160
11283
|
}
|
|
11161
11284
|
};
|
|
11162
|
-
const written2 = (
|
|
11163
|
-
const overwritten = (
|
|
11285
|
+
const written2 = (fresh3) => fresh3 ? ["rename", "change"] : ["change"];
|
|
11286
|
+
const overwritten = (fresh3, args) => fresh3 || args[1].length === 0 ? written2(fresh3) : ["change", "change"];
|
|
11164
11287
|
const reshaping = (run2) => async (...args) => {
|
|
11165
11288
|
this.reshaped();
|
|
11166
11289
|
try {
|
|
@@ -11187,14 +11310,14 @@ var FsWatchers = class {
|
|
|
11187
11310
|
if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
|
|
11188
11311
|
}
|
|
11189
11312
|
}
|
|
11190
|
-
const
|
|
11313
|
+
const fresh3 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
|
|
11191
11314
|
let done5 = false;
|
|
11192
11315
|
try {
|
|
11193
11316
|
await fs.mkdir(path, options);
|
|
11194
11317
|
done5 = true;
|
|
11195
11318
|
} finally {
|
|
11196
11319
|
this.report([...created4, path]);
|
|
11197
|
-
if (done5) this.emit("rename",
|
|
11320
|
+
if (done5) this.emit("rename", fresh3 ? [...created4, path] : created4);
|
|
11198
11321
|
}
|
|
11199
11322
|
},
|
|
11200
11323
|
rm: reshaping(after(fs.rm.bind(fs), (path) => [path], "rename")),
|
|
@@ -11650,6 +11773,403 @@ var ClipboardBroker = class {
|
|
|
11650
11773
|
}
|
|
11651
11774
|
};
|
|
11652
11775
|
|
|
11776
|
+
// src/kernel/embedder.ts
|
|
11777
|
+
var EMBEDDER_PATH = "/dev/embedder";
|
|
11778
|
+
var EMBEDDER_HEADER_MAX = 64 * 1024;
|
|
11779
|
+
var EMBEDDER_WINDOW = 1024 * 1024;
|
|
11780
|
+
var EMBEDDER_BUDGET = 256 * 1024 * 1024;
|
|
11781
|
+
var EMBEDDER_PER_PROCESS = 16;
|
|
11782
|
+
var EMBEDDER_PROGRESS_PER_SECOND = 4;
|
|
11783
|
+
var KIND = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$/;
|
|
11784
|
+
var NL = 10;
|
|
11785
|
+
var READY2 = { readable: true, writable: true, hangup: false };
|
|
11786
|
+
function lineOf2(err) {
|
|
11787
|
+
const text = err instanceof Error ? err.message : String(err);
|
|
11788
|
+
return (text.split("\n")[0] ?? "").slice(0, 200);
|
|
11789
|
+
}
|
|
11790
|
+
var ERRNOS = {
|
|
11791
|
+
NotAllowedError: "EACCES",
|
|
11792
|
+
AbortError: "EACCES",
|
|
11793
|
+
NotSupportedError: "ENOTSUP",
|
|
11794
|
+
NotFoundError: "ENOENT"
|
|
11795
|
+
};
|
|
11796
|
+
function embedderFailure(err) {
|
|
11797
|
+
if (err instanceof KernelError) return err;
|
|
11798
|
+
const name2 = err?.name;
|
|
11799
|
+
return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
|
|
11800
|
+
}
|
|
11801
|
+
function headerOf(bytes) {
|
|
11802
|
+
let parsed2;
|
|
11803
|
+
try {
|
|
11804
|
+
parsed2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
11805
|
+
} catch {
|
|
11806
|
+
throw new KernelError("EINVAL", "the request header is not JSON");
|
|
11807
|
+
}
|
|
11808
|
+
if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
|
|
11809
|
+
throw new KernelError("EINVAL", "the request header is not an object");
|
|
11810
|
+
const { kind, ...options } = parsed2;
|
|
11811
|
+
if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
|
|
11812
|
+
throw new KernelError("EINVAL", "the request has no valid kind");
|
|
11813
|
+
return { kind, options };
|
|
11814
|
+
}
|
|
11815
|
+
function joined3(a, b) {
|
|
11816
|
+
const out = new Uint8Array(a.length + b.length);
|
|
11817
|
+
out.set(a);
|
|
11818
|
+
out.set(b, a.length);
|
|
11819
|
+
return out;
|
|
11820
|
+
}
|
|
11821
|
+
function aborted(signal) {
|
|
11822
|
+
if (!signal) return void 0;
|
|
11823
|
+
return new Promise((_, reject) => {
|
|
11824
|
+
const stop = () => reject(new KernelError("EINTR"));
|
|
11825
|
+
if (signal.aborted) stop();
|
|
11826
|
+
else signal.addEventListener("abort", stop, { once: true });
|
|
11827
|
+
});
|
|
11828
|
+
}
|
|
11829
|
+
async function interruptible2(work, signal) {
|
|
11830
|
+
const stop = aborted(signal);
|
|
11831
|
+
if (!stop) return work;
|
|
11832
|
+
stop.catch(() => void 0);
|
|
11833
|
+
return Promise.race([work, stop]);
|
|
11834
|
+
}
|
|
11835
|
+
var Budget2 = class {
|
|
11836
|
+
used = 0;
|
|
11837
|
+
limit;
|
|
11838
|
+
constructor(limit) {
|
|
11839
|
+
this.limit = limit;
|
|
11840
|
+
}
|
|
11841
|
+
take(n) {
|
|
11842
|
+
if (this.used + n > this.limit) return false;
|
|
11843
|
+
this.used += n;
|
|
11844
|
+
return true;
|
|
11845
|
+
}
|
|
11846
|
+
give(n) {
|
|
11847
|
+
this.used = Math.max(0, this.used - n);
|
|
11848
|
+
}
|
|
11849
|
+
};
|
|
11850
|
+
var BodyPipe = class {
|
|
11851
|
+
queue = [];
|
|
11852
|
+
size = 0;
|
|
11853
|
+
ended = false;
|
|
11854
|
+
wake;
|
|
11855
|
+
room;
|
|
11856
|
+
stream;
|
|
11857
|
+
constructor() {
|
|
11858
|
+
this.stream = new ReadableStream(
|
|
11859
|
+
{
|
|
11860
|
+
pull: async (controller) => {
|
|
11861
|
+
while (this.queue.length === 0 && !this.ended) {
|
|
11862
|
+
await new Promise((resolve2) => {
|
|
11863
|
+
this.wake = resolve2;
|
|
11864
|
+
});
|
|
11865
|
+
}
|
|
11866
|
+
const next3 = this.queue.shift();
|
|
11867
|
+
if (next3) {
|
|
11868
|
+
this.size -= next3.length;
|
|
11869
|
+
this.room?.();
|
|
11870
|
+
controller.enqueue(next3);
|
|
11871
|
+
} else controller.close();
|
|
11872
|
+
}
|
|
11873
|
+
},
|
|
11874
|
+
{ highWaterMark: 0 }
|
|
11875
|
+
);
|
|
11876
|
+
}
|
|
11877
|
+
get full() {
|
|
11878
|
+
return this.size >= EMBEDDER_WINDOW;
|
|
11879
|
+
}
|
|
11880
|
+
push(bytes) {
|
|
11881
|
+
if (bytes.length === 0) return;
|
|
11882
|
+
this.queue.push(bytes.slice());
|
|
11883
|
+
this.size += bytes.length;
|
|
11884
|
+
this.wake?.();
|
|
11885
|
+
}
|
|
11886
|
+
waitRoom() {
|
|
11887
|
+
return new Promise((resolve2) => {
|
|
11888
|
+
this.room = resolve2;
|
|
11889
|
+
});
|
|
11890
|
+
}
|
|
11891
|
+
end() {
|
|
11892
|
+
this.ended = true;
|
|
11893
|
+
this.wake?.();
|
|
11894
|
+
this.room?.();
|
|
11895
|
+
}
|
|
11896
|
+
};
|
|
11897
|
+
function replyStream(reply) {
|
|
11898
|
+
if (reply instanceof ReadableStream) return reply;
|
|
11899
|
+
const bytes = typeof reply === "string" ? new TextEncoder().encode(reply) : reply instanceof Uint8Array ? reply : new Uint8Array(0);
|
|
11900
|
+
return new ReadableStream({
|
|
11901
|
+
start(controller) {
|
|
11902
|
+
if (bytes.length > 0) controller.enqueue(bytes);
|
|
11903
|
+
controller.close();
|
|
11904
|
+
}
|
|
11905
|
+
});
|
|
11906
|
+
}
|
|
11907
|
+
var Throttle = class {
|
|
11908
|
+
window = 0;
|
|
11909
|
+
count = 0;
|
|
11910
|
+
allow(now) {
|
|
11911
|
+
if (now - this.window >= 1e3) {
|
|
11912
|
+
this.window = now;
|
|
11913
|
+
this.count = 0;
|
|
11914
|
+
}
|
|
11915
|
+
this.count += 1;
|
|
11916
|
+
return this.count <= EMBEDDER_PROGRESS_PER_SECOND;
|
|
11917
|
+
}
|
|
11918
|
+
};
|
|
11919
|
+
var EmbedderChannel = class {
|
|
11920
|
+
access;
|
|
11921
|
+
owner;
|
|
11922
|
+
caller;
|
|
11923
|
+
broker;
|
|
11924
|
+
header = new Uint8Array(0);
|
|
11925
|
+
body;
|
|
11926
|
+
reply;
|
|
11927
|
+
replied;
|
|
11928
|
+
rest;
|
|
11929
|
+
done = false;
|
|
11930
|
+
failure;
|
|
11931
|
+
reported = false;
|
|
11932
|
+
abort = new AbortController();
|
|
11933
|
+
closed = false;
|
|
11934
|
+
throttle = new Throttle();
|
|
11935
|
+
constructor(broker, access2, caller) {
|
|
11936
|
+
this.broker = broker;
|
|
11937
|
+
this.access = access2;
|
|
11938
|
+
this.caller = caller;
|
|
11939
|
+
this.owner = caller.pid;
|
|
11940
|
+
}
|
|
11941
|
+
get dispatched() {
|
|
11942
|
+
return this.body !== void 0;
|
|
11943
|
+
}
|
|
11944
|
+
fail(err) {
|
|
11945
|
+
this.failure ??= embedderFailure(err);
|
|
11946
|
+
return this.failure;
|
|
11947
|
+
}
|
|
11948
|
+
report(err) {
|
|
11949
|
+
this.reported = true;
|
|
11950
|
+
return this.fail(err);
|
|
11951
|
+
}
|
|
11952
|
+
async dispatch(bytes) {
|
|
11953
|
+
const { kind, options } = headerOf(bytes);
|
|
11954
|
+
const stamp = await this.broker.admit(kind, this.caller);
|
|
11955
|
+
const hook = this.broker.hookFor(this.caller.client);
|
|
11956
|
+
const body = new BodyPipe();
|
|
11957
|
+
this.body = body;
|
|
11958
|
+
const caller = this.caller;
|
|
11959
|
+
const progress = (line) => {
|
|
11960
|
+
if (this.throttle.allow(Date.now())) caller.progress(lineOf2(line));
|
|
11961
|
+
};
|
|
11962
|
+
const asked = Promise.resolve().then(
|
|
11963
|
+
() => hook.handle({
|
|
11964
|
+
kind,
|
|
11965
|
+
options,
|
|
11966
|
+
body: body.stream,
|
|
11967
|
+
stamp,
|
|
11968
|
+
signal: this.abort.signal,
|
|
11969
|
+
progress
|
|
11970
|
+
})
|
|
11971
|
+
).then((reply) => replyStream(reply).getReader());
|
|
11972
|
+
this.replied = asked.catch((err) => {
|
|
11973
|
+
body.end();
|
|
11974
|
+
throw this.fail(err);
|
|
11975
|
+
});
|
|
11976
|
+
this.replied.catch(() => void 0);
|
|
11977
|
+
}
|
|
11978
|
+
async write(bytes, signal) {
|
|
11979
|
+
if (this.access === "read") throw new KernelError("EBADF");
|
|
11980
|
+
if (this.failure) throw this.report(this.failure);
|
|
11981
|
+
if (this.reply || this.done) throw new KernelError("EBADF");
|
|
11982
|
+
if (this.body) return this.writeBody(bytes, signal);
|
|
11983
|
+
const at = bytes.indexOf(NL);
|
|
11984
|
+
const head = at < 0 ? bytes : bytes.subarray(0, at);
|
|
11985
|
+
if (this.header.length + head.length > EMBEDDER_HEADER_MAX)
|
|
11986
|
+
throw this.report(new KernelError("EINVAL", "the request header is too long"));
|
|
11987
|
+
this.header = joined3(this.header, head);
|
|
11988
|
+
if (at < 0) return bytes.length;
|
|
11989
|
+
try {
|
|
11990
|
+
await this.dispatch(this.header);
|
|
11991
|
+
} catch (err) {
|
|
11992
|
+
throw this.report(err);
|
|
11993
|
+
}
|
|
11994
|
+
this.body?.push(bytes.subarray(at + 1));
|
|
11995
|
+
return bytes.length;
|
|
11996
|
+
}
|
|
11997
|
+
async writeBody(bytes, signal) {
|
|
11998
|
+
const body = this.body;
|
|
11999
|
+
while (body.full && !this.failure) {
|
|
12000
|
+
await interruptible2(body.waitRoom(), signal).catch((err) => {
|
|
12001
|
+
this.cancel("interrupted");
|
|
12002
|
+
throw err;
|
|
12003
|
+
});
|
|
12004
|
+
}
|
|
12005
|
+
if (this.failure) throw this.report(this.failure);
|
|
12006
|
+
body.push(bytes);
|
|
12007
|
+
return bytes.length;
|
|
12008
|
+
}
|
|
12009
|
+
async readerOf(signal) {
|
|
12010
|
+
if (this.reply) return this.reply;
|
|
12011
|
+
if (!this.body) {
|
|
12012
|
+
try {
|
|
12013
|
+
await this.dispatch(this.header);
|
|
12014
|
+
} catch (err) {
|
|
12015
|
+
throw this.fail(err);
|
|
12016
|
+
}
|
|
12017
|
+
}
|
|
12018
|
+
this.body?.end();
|
|
12019
|
+
this.reply = await interruptible2(
|
|
12020
|
+
this.replied,
|
|
12021
|
+
signal
|
|
12022
|
+
);
|
|
12023
|
+
return this.reply;
|
|
12024
|
+
}
|
|
12025
|
+
async read(max, signal) {
|
|
12026
|
+
if (this.access === "write") throw new KernelError("EBADF");
|
|
12027
|
+
if (this.failure) throw this.report(this.failure);
|
|
12028
|
+
if (this.done || max <= 0) return new Uint8Array(0);
|
|
12029
|
+
try {
|
|
12030
|
+
return await this.next(max, signal);
|
|
12031
|
+
} catch (err) {
|
|
12032
|
+
if (err instanceof KernelError && err.code === "EINTR") {
|
|
12033
|
+
this.cancel("interrupted");
|
|
12034
|
+
throw err;
|
|
12035
|
+
}
|
|
12036
|
+
throw this.report(err);
|
|
12037
|
+
}
|
|
12038
|
+
}
|
|
12039
|
+
async next(max, signal) {
|
|
12040
|
+
const reader = await this.readerOf(signal);
|
|
12041
|
+
let chunk2 = this.rest;
|
|
12042
|
+
this.rest = void 0;
|
|
12043
|
+
if (!chunk2) {
|
|
12044
|
+
const got = await interruptible2(reader.read(), signal);
|
|
12045
|
+
if (got.done) {
|
|
12046
|
+
this.done = true;
|
|
12047
|
+
return new Uint8Array(0);
|
|
12048
|
+
}
|
|
12049
|
+
chunk2 = got.value;
|
|
12050
|
+
}
|
|
12051
|
+
if (chunk2.length > max) this.rest = chunk2.subarray(max);
|
|
12052
|
+
return chunk2.subarray(0, max);
|
|
12053
|
+
}
|
|
12054
|
+
poll() {
|
|
12055
|
+
return this.body?.full ? { ...READY2, writable: false } : READY2;
|
|
12056
|
+
}
|
|
12057
|
+
cancel(reason) {
|
|
12058
|
+
if (this.abort.signal.aborted) return;
|
|
12059
|
+
this.abort.abort(new DOMException(reason, "AbortError"));
|
|
12060
|
+
this.body?.end();
|
|
12061
|
+
void this.reply?.cancel(reason).catch(() => void 0);
|
|
12062
|
+
}
|
|
12063
|
+
async close() {
|
|
12064
|
+
if (this.closed) return;
|
|
12065
|
+
const signal = this.caller.closing();
|
|
12066
|
+
this.closed = true;
|
|
12067
|
+
try {
|
|
12068
|
+
if (this.access === "read") return;
|
|
12069
|
+
if (this.failure) {
|
|
12070
|
+
if (this.reported) return;
|
|
12071
|
+
throw this.report(this.failure);
|
|
12072
|
+
}
|
|
12073
|
+
if (this.access === "both" && this.dispatched) return;
|
|
12074
|
+
if (!this.dispatched && this.header.length === 0) return;
|
|
12075
|
+
await this.drain(signal);
|
|
12076
|
+
} finally {
|
|
12077
|
+
if (!this.done) this.cancel("closed");
|
|
12078
|
+
this.broker.release(this);
|
|
12079
|
+
}
|
|
12080
|
+
}
|
|
12081
|
+
async drain(signal) {
|
|
12082
|
+
try {
|
|
12083
|
+
const reader = await this.readerOf(signal);
|
|
12084
|
+
for (; ; ) {
|
|
12085
|
+
const got = await interruptible2(reader.read(), signal);
|
|
12086
|
+
if (got.done) break;
|
|
12087
|
+
}
|
|
12088
|
+
this.done = true;
|
|
12089
|
+
} catch (err) {
|
|
12090
|
+
if (err instanceof KernelError && err.code === "EINTR") this.cancel("interrupted");
|
|
12091
|
+
throw this.fail(err);
|
|
12092
|
+
}
|
|
12093
|
+
}
|
|
12094
|
+
};
|
|
12095
|
+
var EmbedderBroker = class {
|
|
12096
|
+
users;
|
|
12097
|
+
lookup;
|
|
12098
|
+
hooks = /* @__PURE__ */ new Map();
|
|
12099
|
+
budget;
|
|
12100
|
+
open = /* @__PURE__ */ new Map();
|
|
12101
|
+
tagsOf;
|
|
12102
|
+
constructor(users, lookup2, tagsOf = () => void 0, budget = EMBEDDER_BUDGET) {
|
|
12103
|
+
this.users = users;
|
|
12104
|
+
this.tagsOf = tagsOf;
|
|
12105
|
+
this.lookup = lookup2;
|
|
12106
|
+
this.budget = new Budget2(budget);
|
|
12107
|
+
}
|
|
12108
|
+
register(client, hook) {
|
|
12109
|
+
if (!hook) return () => void 0;
|
|
12110
|
+
this.hooks.set(client, hook);
|
|
12111
|
+
return () => {
|
|
12112
|
+
if (this.hooks.get(client) === hook) this.hooks.delete(client);
|
|
12113
|
+
};
|
|
12114
|
+
}
|
|
12115
|
+
hookFor(client) {
|
|
12116
|
+
return this.hooks.get(client);
|
|
12117
|
+
}
|
|
12118
|
+
async allowed(policy, caller) {
|
|
12119
|
+
if (caller.euid === 0 || caller.euid === caller.sessionUid) return true;
|
|
12120
|
+
if (policy.group !== void 0) {
|
|
12121
|
+
const gid = await this.lookup.groupId(policy.group);
|
|
12122
|
+
if (gid !== void 0 && (caller.egid === gid || caller.groups.includes(gid))) return true;
|
|
12123
|
+
}
|
|
12124
|
+
for (const user of policy.users ?? []) {
|
|
12125
|
+
const uid = typeof user === "number" ? user : await this.lookup.userId(user);
|
|
12126
|
+
if (uid === caller.euid) return true;
|
|
12127
|
+
}
|
|
12128
|
+
return false;
|
|
12129
|
+
}
|
|
12130
|
+
async admit(kind, caller) {
|
|
12131
|
+
const hook = this.hooks.get(caller.client);
|
|
12132
|
+
if (!hook) throw new KernelError("ENOTSUP", "no page answers requests for this session");
|
|
12133
|
+
const policy = Object.hasOwn(hook.kinds, kind) ? hook.kinds[kind] : void 0;
|
|
12134
|
+
if (!policy) throw new KernelError("ENOTSUP", `this embedder does not answer ${kind}`);
|
|
12135
|
+
if (this.users !== "off" && !await this.allowed(policy, caller))
|
|
12136
|
+
throw new KernelError("EACCES", `${kind} is not allowed for this user`);
|
|
12137
|
+
const tags = caller.tags;
|
|
12138
|
+
return {
|
|
12139
|
+
pid: caller.shownPid,
|
|
12140
|
+
uid: caller.euid,
|
|
12141
|
+
gid: caller.egid,
|
|
12142
|
+
groups: [...caller.groups],
|
|
12143
|
+
users: this.users,
|
|
12144
|
+
kind,
|
|
12145
|
+
...tags?.length ? { tag: tags[0], tags: [...tags] } : {}
|
|
12146
|
+
};
|
|
12147
|
+
}
|
|
12148
|
+
channel(access2, caller) {
|
|
12149
|
+
const mine = this.open.get(caller.pid) ?? /* @__PURE__ */ new Set();
|
|
12150
|
+
if (mine.size >= EMBEDDER_PER_PROCESS)
|
|
12151
|
+
throw new KernelError("EMFILE", `at most ${EMBEDDER_PER_PROCESS} requests per process`);
|
|
12152
|
+
if (!this.budget.take(2 * EMBEDDER_WINDOW))
|
|
12153
|
+
throw new KernelError("EAGAIN", "the embedder request budget is spent");
|
|
12154
|
+
const channel = new EmbedderChannel(this, access2, caller);
|
|
12155
|
+
mine.add(channel);
|
|
12156
|
+
this.open.set(caller.pid, mine);
|
|
12157
|
+
return new OpenFile({
|
|
12158
|
+
read: channel.read.bind(channel),
|
|
12159
|
+
write: (bytes, signal) => channel.write(bytes, signal),
|
|
12160
|
+
poll: () => channel.poll(),
|
|
12161
|
+
close: () => channel.close(),
|
|
12162
|
+
embedder: channel
|
|
12163
|
+
});
|
|
12164
|
+
}
|
|
12165
|
+
release(channel) {
|
|
12166
|
+
const mine = this.open.get(channel.owner);
|
|
12167
|
+
if (!mine?.delete(channel)) return;
|
|
12168
|
+
if (mine.size === 0) this.open.delete(channel.owner);
|
|
12169
|
+
this.budget.give(2 * EMBEDDER_WINDOW);
|
|
12170
|
+
}
|
|
12171
|
+
};
|
|
12172
|
+
|
|
11653
12173
|
// src/kernel/exec-ticket.ts
|
|
11654
12174
|
var TICKET_ENV = "SLICC_SEALED_TICKET";
|
|
11655
12175
|
var TICKET_TTL_MS = 3e4;
|
|
@@ -12201,9 +12721,9 @@ var HeldPaths = class extends Map {
|
|
|
12201
12721
|
return false;
|
|
12202
12722
|
}
|
|
12203
12723
|
keep(path, file) {
|
|
12204
|
-
const
|
|
12205
|
-
for (const id2 of
|
|
12206
|
-
this.uses.set(file, Math.max(
|
|
12724
|
+
const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
12725
|
+
for (const id2 of fresh3) this.keptHandles.set(id2, file);
|
|
12726
|
+
this.uses.set(file, Math.max(fresh3.length, 1));
|
|
12207
12727
|
this.unlinked.set(path, file);
|
|
12208
12728
|
}
|
|
12209
12729
|
unkeep(path, file) {
|
|
@@ -13399,8 +13919,8 @@ var MountFs = class _MountFs {
|
|
|
13399
13919
|
}
|
|
13400
13920
|
this.writable(mount, real);
|
|
13401
13921
|
const { uid, gid, mode } = change;
|
|
13402
|
-
const
|
|
13403
|
-
const set3 =
|
|
13922
|
+
const fresh3 = change.kind === "new" && { uid, gid, mode };
|
|
13923
|
+
const set3 = fresh3 ? await this.driverOwner(found, real, fresh3, false) : change;
|
|
13404
13924
|
this.table.forget(mount, real);
|
|
13405
13925
|
const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
|
|
13406
13926
|
await mount.conn.call({
|
|
@@ -13672,10 +14192,10 @@ async function runMkdir(entry, path, req) {
|
|
|
13672
14192
|
const plain3 = plainDirMode(entry, req.mode);
|
|
13673
14193
|
const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
|
|
13674
14194
|
const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
|
|
13675
|
-
const
|
|
14195
|
+
const fresh3 = plain3 !== void 0 && !await fs.exists(path);
|
|
13676
14196
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
13677
14197
|
await fixParents?.();
|
|
13678
|
-
if (
|
|
14198
|
+
if (fresh3) await fs.chmod(path, plain3);
|
|
13679
14199
|
return done2;
|
|
13680
14200
|
}
|
|
13681
14201
|
async function shownAs(entry, path, st) {
|
|
@@ -14679,9 +15199,9 @@ var Pages = class {
|
|
|
14679
15199
|
const page = (start2 + at) / PAGE;
|
|
14680
15200
|
if (this.pages.has(page)) continue;
|
|
14681
15201
|
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
14682
|
-
const
|
|
14683
|
-
|
|
14684
|
-
this.pages.set(page,
|
|
15202
|
+
const fresh3 = this.take(part.length);
|
|
15203
|
+
fresh3.set(part);
|
|
15204
|
+
this.pages.set(page, fresh3);
|
|
14685
15205
|
}
|
|
14686
15206
|
}
|
|
14687
15207
|
read(at, length2) {
|
|
@@ -16844,6 +17364,7 @@ var TLS_SERVER_REQUEST_CERT = 1;
|
|
|
16844
17364
|
var TLS_SERVER_REQUIRE_CERT = 2;
|
|
16845
17365
|
var TLS_SERVER_SELECT = 4;
|
|
16846
17366
|
var TLS_HANDSHAKE_SELECT = 2;
|
|
17367
|
+
var TLS_KEYLOG = 8;
|
|
16847
17368
|
var SCRATCH = 64 * 1024;
|
|
16848
17369
|
var EOF = -1;
|
|
16849
17370
|
var TlsError = class extends Error {
|
|
@@ -16911,6 +17432,9 @@ var TlsEngine = class {
|
|
|
16911
17432
|
if (!ptr) throw this.error(this.m._tls_last_error(), "server certificate");
|
|
16912
17433
|
return new TlsLeaf(this, this.m, ptr);
|
|
16913
17434
|
}
|
|
17435
|
+
get keylogs() {
|
|
17436
|
+
return typeof this.m._tls_keylog === "function";
|
|
17437
|
+
}
|
|
16914
17438
|
get serverSelect() {
|
|
16915
17439
|
return typeof this.m._tls_server_select === "function";
|
|
16916
17440
|
}
|
|
@@ -16941,9 +17465,11 @@ var TlsEngine = class {
|
|
|
16941
17465
|
this.m._free(ptr);
|
|
16942
17466
|
}
|
|
16943
17467
|
}
|
|
16944
|
-
intoScratch(fn) {
|
|
17468
|
+
intoScratch(fn, secret = false) {
|
|
16945
17469
|
const n = fn(this.scratch, SCRATCH);
|
|
16946
|
-
|
|
17470
|
+
const out = n > 0 ? this.taken(n) : n;
|
|
17471
|
+
if (secret && n > 0) this.m.HEAPU8.fill(0, this.scratch, this.scratch + n);
|
|
17472
|
+
return out;
|
|
16947
17473
|
}
|
|
16948
17474
|
get sessionControls() {
|
|
16949
17475
|
return typeof this.m._tls_client_cert === "function";
|
|
@@ -16987,18 +17513,18 @@ var TlsEngine = class {
|
|
|
16987
17513
|
chainFlags(store, chain2, hostname) {
|
|
16988
17514
|
const verify = this.m._tls_chain_verify;
|
|
16989
17515
|
if (!verify || chain2.length === 0) return void 0;
|
|
16990
|
-
const
|
|
17516
|
+
const joined5 = new Uint8Array(chain2.reduce((n, c) => n + c.length, 0));
|
|
16991
17517
|
let at = 0;
|
|
16992
17518
|
for (const der2 of chain2) {
|
|
16993
|
-
|
|
17519
|
+
joined5.set(der2, at);
|
|
16994
17520
|
at += der2.length;
|
|
16995
17521
|
}
|
|
16996
17522
|
const name2 = this.put(new TextEncoder().encode(hostname));
|
|
16997
17523
|
try {
|
|
16998
|
-
return this.withBytes(
|
|
17524
|
+
return this.withBytes(joined5, (p) => {
|
|
16999
17525
|
const out = this.scratch;
|
|
17000
17526
|
this.m.HEAPU8.fill(255, out, out + 4);
|
|
17001
|
-
const ret = verify(store.ptr, p,
|
|
17527
|
+
const ret = verify(store.ptr, p, joined5.length, name2, out);
|
|
17002
17528
|
const flags2 = new DataView(this.m.HEAPU8.buffer, out, 4).getUint32(0, true);
|
|
17003
17529
|
if (ret === 0) return flags2 === 0 ? 0 : void 0;
|
|
17004
17530
|
return ret === 1 && flags2 !== 0 ? flags2 : void 0;
|
|
@@ -17154,6 +17680,14 @@ var TlsSession = class {
|
|
|
17154
17680
|
get version() {
|
|
17155
17681
|
return this.c._tls_version(this.ptr);
|
|
17156
17682
|
}
|
|
17683
|
+
keylog() {
|
|
17684
|
+
const drain2 = this.m._tls_keylog;
|
|
17685
|
+
const out = this.engine.intoScratch((p, max) => drain2(this.ptr, p, max), true);
|
|
17686
|
+
if (!(out instanceof Uint8Array)) return [];
|
|
17687
|
+
const text = new TextDecoder().decode(out);
|
|
17688
|
+
out.fill(0);
|
|
17689
|
+
return text.split("\n").filter((line) => line.length > 0);
|
|
17690
|
+
}
|
|
17157
17691
|
get sni() {
|
|
17158
17692
|
const out = this.engine.intoScratch(
|
|
17159
17693
|
(p, max) => this.m._tls_sni(this.ptr, p, max)
|
|
@@ -18790,7 +19324,7 @@ function isIP(name2) {
|
|
|
18790
19324
|
return ipv4Bytes(name2) !== void 0 || name2.includes(":") && ipv6Groups(name2) !== void 0;
|
|
18791
19325
|
}
|
|
18792
19326
|
function clientFlags(o) {
|
|
18793
|
-
return (o.insecure ? TLS_CLIENT_INSECURE_SKIP_VERIFY : 0) | (o.sni === false ? TLS_CLIENT_NO_SNI : 0) | (o.checkHostname === false ? TLS_CLIENT_VERIFY_CHAIN_ONLY : 0);
|
|
19327
|
+
return (o.insecure ? TLS_CLIENT_INSECURE_SKIP_VERIFY : 0) | (o.sni === false ? TLS_CLIENT_NO_SNI : 0) | (o.checkHostname === false ? TLS_CLIENT_VERIFY_CHAIN_ONLY : 0) | (o.keylog ? TLS_KEYLOG : 0);
|
|
18794
19328
|
}
|
|
18795
19329
|
function openSession(o) {
|
|
18796
19330
|
return o.engine.clientSession(o.store, o.servername, o.alpn, clientFlags(o));
|
|
@@ -18905,7 +19439,8 @@ async function infoOf(o, session) {
|
|
|
18905
19439
|
peerCertificates: chain2,
|
|
18906
19440
|
...controlsOf(o, session, protocol, chain2, shape),
|
|
18907
19441
|
verified: verdictOf(o, session),
|
|
18908
|
-
handshakeAt: Date.now()
|
|
19442
|
+
handshakeAt: Date.now(),
|
|
19443
|
+
...o.keylog ? { keylog: session.keylog() } : {}
|
|
18909
19444
|
};
|
|
18910
19445
|
}
|
|
18911
19446
|
var FATAL_ALERT = -30592;
|
|
@@ -19025,7 +19560,7 @@ async function serverHandshake(conn, session, inbox, hooks) {
|
|
|
19025
19560
|
}
|
|
19026
19561
|
}
|
|
19027
19562
|
}
|
|
19028
|
-
async function tlsServer(conn, session, clientCerts, select) {
|
|
19563
|
+
async function tlsServer(conn, session, clientCerts, select, keylog = false) {
|
|
19029
19564
|
let first;
|
|
19030
19565
|
const watched = {
|
|
19031
19566
|
...conn,
|
|
@@ -19048,7 +19583,8 @@ async function tlsServer(conn, session, clientCerts, select) {
|
|
|
19048
19583
|
servername: session.sni,
|
|
19049
19584
|
authorized: clientCerts && chain2.length > 0 && code === void 0,
|
|
19050
19585
|
...clientCerts && chain2.length > 0 && code ? { authorizationError: code } : {},
|
|
19051
|
-
peerCertificates: chain2
|
|
19586
|
+
peerCertificates: chain2,
|
|
19587
|
+
...keylog ? { keylog: session.keylog() } : {}
|
|
19052
19588
|
};
|
|
19053
19589
|
return { conn: plaintext(conn, session, inbox), info };
|
|
19054
19590
|
} catch (err) {
|
|
@@ -19848,6 +20384,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19848
20384
|
...file.tty ? { tty: file.tty } : {},
|
|
19849
20385
|
identity: file.identity ?? file,
|
|
19850
20386
|
...file.clipboard ? { clipboard: file.clipboard } : {},
|
|
20387
|
+
...file.embedder ? { embedder: file.embedder } : {},
|
|
19851
20388
|
...file.write ? {
|
|
19852
20389
|
write: async (bytes, signal) => {
|
|
19853
20390
|
await turn(signal);
|
|
@@ -19904,7 +20441,8 @@ function scrubStdio(fds, scrubber) {
|
|
|
19904
20441
|
const stdio = stdioFiles(fds);
|
|
19905
20442
|
for (const fd of fds.numbers()) {
|
|
19906
20443
|
const original = fds.get(fd);
|
|
19907
|
-
|
|
20444
|
+
const named = original.file.tty || original.file.clipboard || original.file.embedder;
|
|
20445
|
+
if (!stdio.has(original) && !named) continue;
|
|
19908
20446
|
const known = wrapped.get(original);
|
|
19909
20447
|
const flags2 = fds.statusFlags(fd);
|
|
19910
20448
|
const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
|
|
@@ -19917,9 +20455,9 @@ function scrubStdio(fds, scrubber) {
|
|
|
19917
20455
|
// src/kernel/net/sealed-scrubs.ts
|
|
19918
20456
|
function addTo(tree2, host, secrets) {
|
|
19919
20457
|
const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
|
|
19920
|
-
const
|
|
19921
|
-
if (
|
|
19922
|
-
entry.secrets = [...entry.secrets, ...
|
|
20458
|
+
const fresh3 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
|
|
20459
|
+
if (fresh3.length === 0) return;
|
|
20460
|
+
entry.secrets = [...entry.secrets, ...fresh3];
|
|
19923
20461
|
entry.version += 1;
|
|
19924
20462
|
entry.scrubber = void 0;
|
|
19925
20463
|
tree2.set(host, entry);
|
|
@@ -20133,13 +20671,13 @@ function serialized(reply) {
|
|
|
20133
20671
|
const text = JSON.stringify(reply);
|
|
20134
20672
|
plain3 = text === void 0 ? void 0 : JSON.parse(text);
|
|
20135
20673
|
} catch (err) {
|
|
20136
|
-
return JSON.stringify(errorReply("EIO",
|
|
20674
|
+
return JSON.stringify(errorReply("EIO", lineOf3(err)));
|
|
20137
20675
|
}
|
|
20138
20676
|
if (!isRecord(plain3)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
|
|
20139
20677
|
const { v: _, ...rest2 } = plain3;
|
|
20140
20678
|
return JSON.stringify({ v: 1, ...rest2 });
|
|
20141
20679
|
}
|
|
20142
|
-
function
|
|
20680
|
+
function lineOf3(err) {
|
|
20143
20681
|
const text = err instanceof Error ? err.message : String(err);
|
|
20144
20682
|
return (text.split("\n")[0] ?? "").slice(0, 200);
|
|
20145
20683
|
}
|
|
@@ -20190,7 +20728,7 @@ var SealedChannel = class {
|
|
|
20190
20728
|
if (signal.aborted) return void 0;
|
|
20191
20729
|
const asked = Promise.resolve().then(() => hook(req, { signal })).then(
|
|
20192
20730
|
(reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
|
|
20193
|
-
(err) => errorReply("EIO",
|
|
20731
|
+
(err) => errorReply("EIO", lineOf3(err))
|
|
20194
20732
|
);
|
|
20195
20733
|
const reply = await Promise.race([asked, stopped]);
|
|
20196
20734
|
if (!signal.aborted) return reply;
|
|
@@ -21307,13 +21845,13 @@ var PgServer = class {
|
|
|
21307
21845
|
at += whole;
|
|
21308
21846
|
}
|
|
21309
21847
|
this.buffer = data.slice(at);
|
|
21310
|
-
const
|
|
21848
|
+
const joined5 = new Uint8Array(out.reduce((n, b) => n + b.length, 0));
|
|
21311
21849
|
let to = 0;
|
|
21312
21850
|
for (const b of out) {
|
|
21313
|
-
|
|
21851
|
+
joined5.set(b, to);
|
|
21314
21852
|
to += b.length;
|
|
21315
21853
|
}
|
|
21316
|
-
return
|
|
21854
|
+
return joined5;
|
|
21317
21855
|
}
|
|
21318
21856
|
};
|
|
21319
21857
|
function frame3(type, body) {
|
|
@@ -21655,12 +22193,12 @@ var SessionWriter = class {
|
|
|
21655
22193
|
if (this.failed !== void 0) throw this.failed;
|
|
21656
22194
|
if (this.mode === "pass") return this.conn.write(bytes);
|
|
21657
22195
|
clearTimeout(this.timer);
|
|
21658
|
-
const
|
|
21659
|
-
|
|
21660
|
-
|
|
22196
|
+
const joined5 = new Uint8Array(this.buffer.length + bytes.length);
|
|
22197
|
+
joined5.set(this.buffer);
|
|
22198
|
+
joined5.set(bytes, this.buffer.length);
|
|
21661
22199
|
this.buffer.fill(0);
|
|
21662
|
-
this.buffer =
|
|
21663
|
-
if (this.mode === "sniff") this.mode = modeOf(latin12(
|
|
22200
|
+
this.buffer = joined5;
|
|
22201
|
+
if (this.mode === "sniff") this.mode = modeOf(latin12(joined5.subarray(0, 8))) ?? "sniff";
|
|
21664
22202
|
if (this.mode === "pg" && !this.sawPg) {
|
|
21665
22203
|
this.sawPg = true;
|
|
21666
22204
|
if (!this.s.verified) void this.proof("pg");
|
|
@@ -21808,7 +22346,7 @@ var ScrubReader = class {
|
|
|
21808
22346
|
const before = this.current();
|
|
21809
22347
|
const out = this.through(got);
|
|
21810
22348
|
if (before.length === 0 && out !== void 0) return out;
|
|
21811
|
-
if (before.length > 0) return
|
|
22349
|
+
if (before.length > 0) return joined4(before, out);
|
|
21812
22350
|
}
|
|
21813
22351
|
}
|
|
21814
22352
|
through(got) {
|
|
@@ -21824,7 +22362,7 @@ var ScrubReader = class {
|
|
|
21824
22362
|
return out.length > 0 ? out : void 0;
|
|
21825
22363
|
}
|
|
21826
22364
|
};
|
|
21827
|
-
function
|
|
22365
|
+
function joined4(a, b) {
|
|
21828
22366
|
if (!b) return a;
|
|
21829
22367
|
const out = new Uint8Array(a.length + b.length);
|
|
21830
22368
|
out.set(a);
|
|
@@ -21939,7 +22477,8 @@ function sessionOf(info, servername) {
|
|
|
21939
22477
|
} : {},
|
|
21940
22478
|
servername,
|
|
21941
22479
|
peerCertificates: info.peerCertificates.map(encode),
|
|
21942
|
-
...controls(info)
|
|
22480
|
+
...controls(info),
|
|
22481
|
+
...info.keylog ? { keylog: info.keylog } : {}
|
|
21943
22482
|
};
|
|
21944
22483
|
}
|
|
21945
22484
|
function controls(info) {
|
|
@@ -22081,11 +22620,14 @@ async function prepareWith(support, req, servername, cert) {
|
|
|
22081
22620
|
insecure: req.insecure === true,
|
|
22082
22621
|
...req.sni === false ? { sni: false } : {},
|
|
22083
22622
|
...req.checkHostname === false ? { checkHostname: false } : {},
|
|
22623
|
+
...req.keylog === true ? { keylog: true } : {},
|
|
22084
22624
|
clientCertificate: cert !== void 0,
|
|
22085
22625
|
bundled: own === void 0
|
|
22086
22626
|
};
|
|
22087
22627
|
let session;
|
|
22088
22628
|
try {
|
|
22629
|
+
if (req.keylog === true && !engine.keylogs)
|
|
22630
|
+
throw new KernelError("ENOTSUP", "the installed tls engine has no key log");
|
|
22089
22631
|
const controls2 = req.sni === false || req.checkHostname === false || cert !== void 0;
|
|
22090
22632
|
if (controls2 && !engine.sessionControls) {
|
|
22091
22633
|
throw new KernelError("ENOTSUP", "the installed tls engine has no client certificates");
|
|
@@ -22133,7 +22675,7 @@ function secure(prepared, seal) {
|
|
|
22133
22675
|
try {
|
|
22134
22676
|
const made2 = await tlsClient(raw2, prepared.options);
|
|
22135
22677
|
const engine = prepared.options.engine;
|
|
22136
|
-
const session = sealing({ ...seal, engine }, raw2, made2.info, prepared.options.servername);
|
|
22678
|
+
const session = prepared.options.keylog ? void 0 : sealing({ ...seal, engine }, raw2, made2.info, prepared.options.servername);
|
|
22137
22679
|
if (!session) return made2;
|
|
22138
22680
|
made2.info.substitutes = substitutes(session);
|
|
22139
22681
|
if (made2.info.substitutes && session.verified)
|
|
@@ -22246,25 +22788,31 @@ function certFlags(req) {
|
|
|
22246
22788
|
if (!req.requestCert) return 0;
|
|
22247
22789
|
return req.rejectUnauthorized === false ? TLS_SERVER_REQUEST_CERT : TLS_SERVER_REQUIRE_CERT;
|
|
22248
22790
|
}
|
|
22249
|
-
|
|
22250
|
-
|
|
22791
|
+
var CERT_FLAGS = TLS_SERVER_REQUEST_CERT | TLS_SERVER_REQUIRE_CERT;
|
|
22792
|
+
function selectFlag(req) {
|
|
22793
|
+
if (!req.select) return 0;
|
|
22251
22794
|
if (req.alpn && req.alpn.length > 0)
|
|
22252
22795
|
throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
|
|
22253
|
-
return
|
|
22796
|
+
return TLS_SERVER_SELECT;
|
|
22797
|
+
}
|
|
22798
|
+
function flagsOf(req) {
|
|
22799
|
+
return certFlags(req) | selectFlag(req) | (req.keylog ? TLS_KEYLOG : 0);
|
|
22254
22800
|
}
|
|
22255
|
-
async function engineOf(proc, select) {
|
|
22801
|
+
async function engineOf(proc, { select, keylog }) {
|
|
22256
22802
|
const engine = await supportOf(proc.net).engine();
|
|
22257
22803
|
if (!engine.serverSessions)
|
|
22258
22804
|
throw new KernelError("ENOTSUP", "the installed tls engine has no server sessions");
|
|
22805
|
+
if (keylog && !engine.keylogs)
|
|
22806
|
+
throw new KernelError("ENOTSUP", "the installed tls engine has no key log");
|
|
22259
22807
|
if (select && !engine.serverSelect)
|
|
22260
22808
|
throw new KernelError("ENOTSUP", "the installed tls engine has no server selection");
|
|
22261
22809
|
return engine;
|
|
22262
22810
|
}
|
|
22263
|
-
async function importedLeaf(proc, own,
|
|
22811
|
+
async function importedLeaf(proc, own, needs = {}) {
|
|
22264
22812
|
const cert = certBytes(own);
|
|
22265
22813
|
let engine;
|
|
22266
22814
|
try {
|
|
22267
|
-
engine = await engineOf(proc,
|
|
22815
|
+
engine = await engineOf(proc, needs);
|
|
22268
22816
|
} catch (err) {
|
|
22269
22817
|
wipe(cert);
|
|
22270
22818
|
throw err;
|
|
@@ -22294,12 +22842,12 @@ async function serverOf(proc, req) {
|
|
|
22294
22842
|
delete req.cert;
|
|
22295
22843
|
if (!own && !req.select)
|
|
22296
22844
|
throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: a server needs cert and key");
|
|
22297
|
-
const { engine, leaf } = own ? await importedLeaf(proc, own, req
|
|
22845
|
+
const { engine, leaf } = own ? await importedLeaf(proc, own, req) : { engine: await engineOf(proc, { ...req, select: true }), leaf: void 0 };
|
|
22298
22846
|
let mine;
|
|
22299
22847
|
try {
|
|
22300
22848
|
mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
|
|
22301
22849
|
const flags2 = flagsOf(req);
|
|
22302
|
-
const store = mine ?? (flags2 &
|
|
22850
|
+
const store = mine ?? (flags2 & CERT_FLAGS ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
|
|
22303
22851
|
return {
|
|
22304
22852
|
engine,
|
|
22305
22853
|
leaf,
|
|
@@ -22333,7 +22881,8 @@ function servedOf(info) {
|
|
|
22333
22881
|
authorized: info.authorized,
|
|
22334
22882
|
...info.authorizationError ? { authorizationError: info.authorizationError } : {},
|
|
22335
22883
|
peerCertificates: info.peerCertificates.map(encode),
|
|
22336
|
-
cipher: info.cipher
|
|
22884
|
+
cipher: info.cipher,
|
|
22885
|
+
...info.keylog ? { keylog: info.keylog } : {}
|
|
22337
22886
|
};
|
|
22338
22887
|
}
|
|
22339
22888
|
var selections = /* @__PURE__ */ new WeakMap();
|
|
@@ -22356,13 +22905,14 @@ async function serveSecure(proc, socket, req) {
|
|
|
22356
22905
|
const { engine, leaf, alpn, store, flags: flags2 } = server;
|
|
22357
22906
|
const limit = handshakeSignal(proc, server.timeoutMs);
|
|
22358
22907
|
const { pausedAt, select } = pausing(limit.signal);
|
|
22359
|
-
const certs = (flags2 &
|
|
22908
|
+
const certs = (flags2 & CERT_FLAGS) !== 0;
|
|
22360
22909
|
const run2 = socket.secureLink(
|
|
22361
22910
|
(raw2) => tlsServer(
|
|
22362
22911
|
raw2,
|
|
22363
22912
|
engine.serverSession(leaf, alpn, store, flags2),
|
|
22364
22913
|
certs,
|
|
22365
|
-
flags2 & TLS_SERVER_SELECT ? select : void 0
|
|
22914
|
+
flags2 & TLS_SERVER_SELECT ? select : void 0,
|
|
22915
|
+
(flags2 & TLS_KEYLOG) !== 0
|
|
22366
22916
|
),
|
|
22367
22917
|
limit.signal,
|
|
22368
22918
|
(reason) => !shared && reason === timedOut
|
|
@@ -22732,7 +23282,7 @@ async function connect(req, proc) {
|
|
|
22732
23282
|
dialing?.catch(() => void 0);
|
|
22733
23283
|
throw new KernelError("EINPROGRESS");
|
|
22734
23284
|
}
|
|
22735
|
-
if (dialing !== void 0) await
|
|
23285
|
+
if (dialing !== void 0) await interruptible3(dialing, proc.blocking());
|
|
22736
23286
|
return ok3();
|
|
22737
23287
|
}
|
|
22738
23288
|
async function bindUnixDgram(proc, socket, addr) {
|
|
@@ -22797,7 +23347,7 @@ async function unixDatagram(req, dgram, proc) {
|
|
|
22797
23347
|
function domainOf(socket) {
|
|
22798
23348
|
return socket instanceof UnixDgram ? "unix" : "inet";
|
|
22799
23349
|
}
|
|
22800
|
-
function
|
|
23350
|
+
function interruptible3(dialing, signal) {
|
|
22801
23351
|
return new Promise((resolve2, reject) => {
|
|
22802
23352
|
const abort = () => reject(new KernelError("EINTR"));
|
|
22803
23353
|
signal.addEventListener("abort", abort, { once: true });
|
|
@@ -22832,7 +23382,7 @@ var NOT_HTTP_PORTS = /* @__PURE__ */ new Set([
|
|
|
22832
23382
|
]);
|
|
22833
23383
|
var HOST_NAME = /^[A-Za-z0-9.-]+$|^\[?[0-9A-Fa-f:.]+\]?$/;
|
|
22834
23384
|
function sessionControls(req) {
|
|
22835
|
-
return req.sni === false || req.checkHostname === false || req.clientCert !== void 0;
|
|
23385
|
+
return req.sni === false || req.checkHostname === false || req.clientCert !== void 0 || req.keylog === true;
|
|
22836
23386
|
}
|
|
22837
23387
|
function wantsKernel(req) {
|
|
22838
23388
|
if (req.path === "kernel") return true;
|
|
@@ -23801,7 +24351,7 @@ var ProcessWatches = class {
|
|
|
23801
24351
|
};
|
|
23802
24352
|
|
|
23803
24353
|
// src/kernel/process.ts
|
|
23804
|
-
function
|
|
24354
|
+
function aborted2(signal) {
|
|
23805
24355
|
return new Promise((_, reject) => {
|
|
23806
24356
|
signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
|
|
23807
24357
|
});
|
|
@@ -23891,6 +24441,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
23891
24441
|
"fd-open-sealed",
|
|
23892
24442
|
"fd-open-clipboard",
|
|
23893
24443
|
"fd-clipboard-abort",
|
|
24444
|
+
"fd-open-embedder",
|
|
23894
24445
|
"tty-get",
|
|
23895
24446
|
"tty-set",
|
|
23896
24447
|
"tty-winsz",
|
|
@@ -24444,6 +24995,7 @@ var WasmProcess = class {
|
|
|
24444
24995
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
24445
24996
|
if (file.tty) this.checkForeground(file.tty);
|
|
24446
24997
|
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
24998
|
+
if (file.embedder) return this.readEmbedder(file, req, cancelled);
|
|
24447
24999
|
if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
|
|
24448
25000
|
if (req.max <= 0) return new Uint8Array(0);
|
|
24449
25001
|
if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
|
|
@@ -24464,6 +25016,81 @@ var WasmProcess = class {
|
|
|
24464
25016
|
throw err;
|
|
24465
25017
|
}
|
|
24466
25018
|
}
|
|
25019
|
+
async readEmbedder(file, req, cancelled) {
|
|
25020
|
+
const channel = file.embedder;
|
|
25021
|
+
if (req.max <= 0) return new Uint8Array(0);
|
|
25022
|
+
channel.caller = await this.embedderCaller();
|
|
25023
|
+
try {
|
|
25024
|
+
return await channel.read(Math.min(req.max, MAX_READ), this.blockingSignal(cancelled));
|
|
25025
|
+
} catch (err) {
|
|
25026
|
+
await this.deviceComplaint(EMBEDDER_PATH, err);
|
|
25027
|
+
throw err;
|
|
25028
|
+
}
|
|
25029
|
+
}
|
|
25030
|
+
async writeEmbedder(file, body, nonblock, cancelled) {
|
|
25031
|
+
const channel = file.embedder;
|
|
25032
|
+
channel.caller = await this.embedderCaller();
|
|
25033
|
+
const write = file.write;
|
|
25034
|
+
if (!pollFile(file).writable && nonblock) throw new KernelError("EAGAIN");
|
|
25035
|
+
try {
|
|
25036
|
+
return await write.call(file, body, this.blockingSignal(cancelled));
|
|
25037
|
+
} catch (err) {
|
|
25038
|
+
await this.deviceComplaint(EMBEDDER_PATH, err);
|
|
25039
|
+
throw err;
|
|
25040
|
+
}
|
|
25041
|
+
}
|
|
25042
|
+
async deviceComplaint(path, err) {
|
|
25043
|
+
const name2 = this.options.clipboardName;
|
|
25044
|
+
if (name2 === void 0 || !(err instanceof KernelError) || err.detail === void 0) return;
|
|
25045
|
+
if (err.code === "EINTR") return;
|
|
25046
|
+
await this.complainLine(`${name2}: ${path}: ${clipboardLine(err.detail)}`);
|
|
25047
|
+
}
|
|
25048
|
+
async complainLine(line) {
|
|
25049
|
+
const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
|
|
25050
|
+
if (!stderr?.write || stderr.clipboard || stderr.embedder || stderr.sealed) return;
|
|
25051
|
+
const timeout = new Promise((r) => setTimeout(() => r(0), 1e3));
|
|
25052
|
+
await Promise.race([
|
|
25053
|
+
stderr.write(new TextEncoder().encode(`${line}
|
|
25054
|
+
`), AbortSignal.timeout(1e3)).catch(() => 0),
|
|
25055
|
+
timeout
|
|
25056
|
+
]);
|
|
25057
|
+
}
|
|
25058
|
+
async embedderCaller() {
|
|
25059
|
+
const shown = await this.options.identity?.();
|
|
25060
|
+
const session = this.options.embedderSession?.() ?? { client: 0, uid: 0 };
|
|
25061
|
+
const name2 = this.options.clipboardName ?? this.options.embedderName ?? "program";
|
|
25062
|
+
return {
|
|
25063
|
+
pid: this.pid,
|
|
25064
|
+
shownPid: shown?.pid ?? this.pid,
|
|
25065
|
+
euid: this.cred.euid,
|
|
25066
|
+
egid: this.cred.egid,
|
|
25067
|
+
groups: [...this.cred.groups],
|
|
25068
|
+
sessionUid: session.uid,
|
|
25069
|
+
client: session.client,
|
|
25070
|
+
tags: this.options.embedder?.tagsOf(this.pid),
|
|
25071
|
+
progress: (line) => void this.complainLine(`${name2}: ${line}`),
|
|
25072
|
+
closing: () => AbortSignal.any([this.interrupt.signal, this.gone.signal])
|
|
25073
|
+
};
|
|
25074
|
+
}
|
|
25075
|
+
async closeFd(fd) {
|
|
25076
|
+
this.options.locks?.closed(this.pid, fd);
|
|
25077
|
+
await this.dropRecordLocks(fd);
|
|
25078
|
+
const closing2 = this.fds.get(fd).file;
|
|
25079
|
+
const channel = closing2.clipboard;
|
|
25080
|
+
if (channel) channel.stamp = await this.clipboardStamp();
|
|
25081
|
+
if (closing2.embedder) closing2.embedder.caller = await this.embedderCaller();
|
|
25082
|
+
await Promise.resolve(this.fds.close(fd)).catch(async (err) => {
|
|
25083
|
+
if (channel) await this.clipboardComplaint(err);
|
|
25084
|
+
if (closing2.embedder) await this.deviceComplaint(EMBEDDER_PATH, err);
|
|
25085
|
+
throw err;
|
|
25086
|
+
});
|
|
25087
|
+
}
|
|
25088
|
+
async openEmbedder(flags2) {
|
|
25089
|
+
const broker = this.options.embedder;
|
|
25090
|
+
if (!broker) throw new KernelError("ENOENT");
|
|
25091
|
+
const file = broker.channel(openAccess(flags2), await this.embedderCaller());
|
|
25092
|
+
return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
|
|
25093
|
+
}
|
|
24467
25094
|
async complain(line) {
|
|
24468
25095
|
const stderr = this.fds.has(2) ? this.fds.get(2).file : void 0;
|
|
24469
25096
|
if (!stderr?.write || stderr.sealed || stderr.clipboard) return false;
|
|
@@ -24494,6 +25121,7 @@ var WasmProcess = class {
|
|
|
24494
25121
|
const file = this.fds.get(fd).file;
|
|
24495
25122
|
if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
|
|
24496
25123
|
if (!file.write) throw new KernelError("EBADF");
|
|
25124
|
+
if (file.embedder) return this.writeEmbedder(file, body, nonblock, cancelled);
|
|
24497
25125
|
if (!pollFile(file).writable) {
|
|
24498
25126
|
if (nonblock) throw new KernelError("EAGAIN");
|
|
24499
25127
|
return file.write(body, this.blockingSignal(cancelled));
|
|
@@ -24541,11 +25169,11 @@ var WasmProcess = class {
|
|
|
24541
25169
|
} else if (req.create) {
|
|
24542
25170
|
await nodes.onEntry(path, async () => {
|
|
24543
25171
|
const marks = this.options.owners || plain3 !== void 0;
|
|
24544
|
-
const
|
|
25172
|
+
const fresh3 = marks && !await present3(fs, path, true);
|
|
24545
25173
|
this.fds.installAt(fd, this.openVfsFile(open2, await nodes.identify(path), fs));
|
|
24546
25174
|
await this.fds.get(fd).file.stat?.();
|
|
24547
|
-
if (
|
|
24548
|
-
else if (
|
|
25175
|
+
if (fresh3 && plain3 !== void 0) await fs.chmod?.(path, plain3);
|
|
25176
|
+
else if (fresh3) {
|
|
24549
25177
|
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
24550
25178
|
}
|
|
24551
25179
|
});
|
|
@@ -24763,17 +25391,9 @@ var WasmProcess = class {
|
|
|
24763
25391
|
kind: "json",
|
|
24764
25392
|
json: await this.write(req.fd, req.body, req.nonblock, cancelled)
|
|
24765
25393
|
};
|
|
24766
|
-
case "fd-close":
|
|
24767
|
-
this.
|
|
24768
|
-
await this.dropRecordLocks(req.fd);
|
|
24769
|
-
const channel = this.fds.get(req.fd).file.clipboard;
|
|
24770
|
-
if (channel) channel.stamp = await this.clipboardStamp();
|
|
24771
|
-
await Promise.resolve(this.fds.close(req.fd)).catch(async (err) => {
|
|
24772
|
-
if (channel) await this.clipboardComplaint(err);
|
|
24773
|
-
throw err;
|
|
24774
|
-
});
|
|
25394
|
+
case "fd-close":
|
|
25395
|
+
await this.closeFd(req.fd);
|
|
24775
25396
|
return { ok: true, kind: "void" };
|
|
24776
|
-
}
|
|
24777
25397
|
case "fd-pipe": {
|
|
24778
25398
|
const pipe = openPipe();
|
|
24779
25399
|
const read = this.fds.install(pipe.read, 3);
|
|
@@ -24833,6 +25453,8 @@ var WasmProcess = class {
|
|
|
24833
25453
|
return { ok: true, kind: "json", json: this.openSealed(req.flags) };
|
|
24834
25454
|
case "fd-open-clipboard":
|
|
24835
25455
|
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
25456
|
+
case "fd-open-embedder":
|
|
25457
|
+
return { ok: true, kind: "json", json: await this.openEmbedder(req.flags) };
|
|
24836
25458
|
case "fd-clipboard-abort": {
|
|
24837
25459
|
const file = this.fds.get(req.fd);
|
|
24838
25460
|
file.file.clipboard?.abort();
|
|
@@ -25327,7 +25949,7 @@ var WasmProcess = class {
|
|
|
25327
25949
|
else {
|
|
25328
25950
|
const { source, target, type, flags: flags2, data } = req;
|
|
25329
25951
|
const signal = this.blockingSignal(cancelled);
|
|
25330
|
-
await Promise.race([mount({ source, target, type, flags: flags2, data }, signal),
|
|
25952
|
+
await Promise.race([mount({ source, target, type, flags: flags2, data }, signal), aborted2(signal)]);
|
|
25331
25953
|
}
|
|
25332
25954
|
} catch (err) {
|
|
25333
25955
|
const code = err?.code;
|
|
@@ -25337,7 +25959,7 @@ var WasmProcess = class {
|
|
|
25337
25959
|
return { ok: true, kind: "void" };
|
|
25338
25960
|
}
|
|
25339
25961
|
pause(cancelled) {
|
|
25340
|
-
return
|
|
25962
|
+
return aborted2(this.blockingSignal(cancelled));
|
|
25341
25963
|
}
|
|
25342
25964
|
alarm;
|
|
25343
25965
|
dlLog = [];
|
|
@@ -25887,6 +26509,15 @@ function linksOf(opts) {
|
|
|
25887
26509
|
function clipboardName(opts) {
|
|
25888
26510
|
return opts.program.abi === "js" ? {} : { clipboardName: opts.argv0 };
|
|
25889
26511
|
}
|
|
26512
|
+
function embedderOptions(opts) {
|
|
26513
|
+
if (!opts.embedder) return {};
|
|
26514
|
+
return {
|
|
26515
|
+
embedder: opts.embedder.broker,
|
|
26516
|
+
embedderSession: opts.embedder.session,
|
|
26517
|
+
embedderName: opts.argv0,
|
|
26518
|
+
...clipboardName(opts)
|
|
26519
|
+
};
|
|
26520
|
+
}
|
|
25890
26521
|
function processOptions(opts, header4, hooks, created4) {
|
|
25891
26522
|
return {
|
|
25892
26523
|
...inherited(opts),
|
|
@@ -25894,6 +26525,7 @@ function processOptions(opts, header4, hooks, created4) {
|
|
|
25894
26525
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
25895
26526
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
25896
26527
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
26528
|
+
...embedderOptions(opts),
|
|
25897
26529
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
25898
26530
|
...opts.source ? { source: opts.source } : {},
|
|
25899
26531
|
noLinks: opts.noLinks,
|
|
@@ -28044,14 +28676,14 @@ var RESTORE_AGAIN_MS = 3e4;
|
|
|
28044
28676
|
function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
28045
28677
|
const handles = /* @__PURE__ */ new Map();
|
|
28046
28678
|
let nextFh = 0;
|
|
28047
|
-
const
|
|
28679
|
+
const fresh3 = (remote) => {
|
|
28048
28680
|
if (remote.client.token === remote.token && !remote.client.gone) return remote;
|
|
28049
28681
|
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
28050
28682
|
};
|
|
28051
28683
|
const live = (fh) => {
|
|
28052
28684
|
const remote = handles.get(fh);
|
|
28053
28685
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
28054
|
-
return
|
|
28686
|
+
return fresh3(remote);
|
|
28055
28687
|
};
|
|
28056
28688
|
const call = (op, body = {}) => current().call(op, body);
|
|
28057
28689
|
const same3 = (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);
|
|
@@ -28133,7 +28765,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
28133
28765
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
28134
28766
|
handles.delete(fh);
|
|
28135
28767
|
try {
|
|
28136
|
-
await
|
|
28768
|
+
await fresh3(remote).client.call("release", { fh: remote.fh }, true);
|
|
28137
28769
|
} finally {
|
|
28138
28770
|
if (remote.restore) await settle3(current(), remote.restore);
|
|
28139
28771
|
}
|
|
@@ -29365,12 +29997,12 @@ ${lines2}
|
|
|
29365
29997
|
const timer = setTimeout(() => control.abort("expired"), timeout);
|
|
29366
29998
|
const withdraw = () => control.abort("withdrawn");
|
|
29367
29999
|
requester.addEventListener("abort", withdraw, { once: true });
|
|
29368
|
-
const
|
|
30000
|
+
const aborted3 = new Promise(
|
|
29369
30001
|
(resolve2) => control.signal.addEventListener("abort", () => resolve2("aborted"), { once: true })
|
|
29370
30002
|
);
|
|
29371
30003
|
try {
|
|
29372
30004
|
const answered2 = Promise.resolve().then(() => approve(req, { signal: control.signal })).catch(() => "deny");
|
|
29373
|
-
const answer2 = await Promise.race([answered2,
|
|
30005
|
+
const answer2 = await Promise.race([answered2, aborted3]);
|
|
29374
30006
|
if (answer2 === "aborted" || control.signal.aborted) {
|
|
29375
30007
|
return control.signal.reason === "expired" ? failed2("ETIMEDOUT", "sudo: the request expired") : failed2("EINTR", "sudo: the request was withdrawn");
|
|
29376
30008
|
}
|
|
@@ -29496,6 +30128,7 @@ var SUDO_SOURCE = [
|
|
|
29496
30128
|
].join("\n");
|
|
29497
30129
|
|
|
29498
30130
|
// src/launcher.ts
|
|
30131
|
+
var NO_SESSION = { client: -1, uid: -1 };
|
|
29499
30132
|
var MODULE_URLS = 16;
|
|
29500
30133
|
function dataModuleUrl(source) {
|
|
29501
30134
|
const bytes = new TextEncoder().encode(source);
|
|
@@ -29758,6 +30391,7 @@ var Launcher = class {
|
|
|
29758
30391
|
described = /* @__PURE__ */ new Map();
|
|
29759
30392
|
creds = /* @__PURE__ */ new Map();
|
|
29760
30393
|
tags = /* @__PURE__ */ new Map();
|
|
30394
|
+
sessions = /* @__PURE__ */ new Map();
|
|
29761
30395
|
tagBases = /* @__PURE__ */ new Map();
|
|
29762
30396
|
links = /* @__PURE__ */ new Map();
|
|
29763
30397
|
freshModules = new FreshModules();
|
|
@@ -29818,6 +30452,7 @@ var Launcher = class {
|
|
|
29818
30452
|
sealed;
|
|
29819
30453
|
scrubs = /* @__PURE__ */ new Map();
|
|
29820
30454
|
clipboard;
|
|
30455
|
+
embedder;
|
|
29821
30456
|
rootFs;
|
|
29822
30457
|
fileWatches;
|
|
29823
30458
|
nextPid = 1e3;
|
|
@@ -29839,14 +30474,14 @@ var Launcher = class {
|
|
|
29839
30474
|
void 0,
|
|
29840
30475
|
(pid) => this.tagsOf(pid)
|
|
29841
30476
|
);
|
|
30477
|
+
this.embedder = this.embedderBroker(options.embedder);
|
|
29842
30478
|
this.sealed = new SealedBroker(
|
|
29843
30479
|
options.sealed,
|
|
29844
30480
|
this.usersMode,
|
|
29845
30481
|
void 0,
|
|
29846
30482
|
(pid) => this.tagsOf(pid)
|
|
29847
30483
|
);
|
|
29848
|
-
this.
|
|
29849
|
-
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
30484
|
+
this.wireSealed();
|
|
29850
30485
|
if (this.owners) options.fs.trackOwners?.();
|
|
29851
30486
|
this.watchers.tap((paths) => {
|
|
29852
30487
|
this.changes.publish(...paths);
|
|
@@ -30907,6 +31542,8 @@ var Launcher = class {
|
|
|
30907
31542
|
}
|
|
30908
31543
|
marks(pid, req) {
|
|
30909
31544
|
if (req.tags?.length) this.tags.set(pid, req.tags);
|
|
31545
|
+
const session = req.session ?? this.sessions.get(req.ppid ?? -1);
|
|
31546
|
+
if (session) this.sessions.set(pid, session);
|
|
30910
31547
|
if (req.tags?.length) this.tagBases.set(pid, req.tagBase ?? req.tags.length);
|
|
30911
31548
|
if (req.links !== void 0) this.links.set(pid, req.links);
|
|
30912
31549
|
}
|
|
@@ -30986,6 +31623,7 @@ var Launcher = class {
|
|
|
30986
31623
|
sealed: this.sealed,
|
|
30987
31624
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
30988
31625
|
clipboard: this.clipboard,
|
|
31626
|
+
embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
|
|
30989
31627
|
...this.dac ? { dac: this.dac } : {},
|
|
30990
31628
|
program: req.program,
|
|
30991
31629
|
changes: this.changes.sab,
|
|
@@ -31114,6 +31752,7 @@ var Launcher = class {
|
|
|
31114
31752
|
this.childTimes.delete(pid);
|
|
31115
31753
|
this.creds.delete(pid);
|
|
31116
31754
|
this.tags.delete(pid);
|
|
31755
|
+
this.sessions.delete(pid);
|
|
31117
31756
|
this.tagBases.delete(pid);
|
|
31118
31757
|
this.links.delete(pid);
|
|
31119
31758
|
this.granted.delete(pid);
|
|
@@ -31159,8 +31798,8 @@ var Launcher = class {
|
|
|
31159
31798
|
this.sampledAt = Math.max(this.sampledAt, now);
|
|
31160
31799
|
this.load = this.load.map((avg, i) => {
|
|
31161
31800
|
const window = LOAD_WINDOWS_MS[i];
|
|
31162
|
-
const
|
|
31163
|
-
return avg * Math.exp(-elapsed / window) + running *
|
|
31801
|
+
const fresh3 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
|
|
31802
|
+
return avg * Math.exp(-elapsed / window) + running * fresh3;
|
|
31164
31803
|
});
|
|
31165
31804
|
}
|
|
31166
31805
|
idleSince;
|
|
@@ -31390,7 +32029,7 @@ var Launcher = class {
|
|
|
31390
32029
|
forker(ppid, parent) {
|
|
31391
32030
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
31392
32031
|
return async (state, given) => {
|
|
31393
|
-
const
|
|
32032
|
+
const fresh3 = (fds, quietOom) => this.start({
|
|
31394
32033
|
...image,
|
|
31395
32034
|
quietOom,
|
|
31396
32035
|
cwd: state.cwd ?? parent.cwd,
|
|
@@ -31406,7 +32045,7 @@ var Launcher = class {
|
|
|
31406
32045
|
tagBase: this.tagBases.get(ppid),
|
|
31407
32046
|
links: this.links.get(ppid)
|
|
31408
32047
|
});
|
|
31409
|
-
return childHandle(await this.admitted(parent.program.abi !== "js", given,
|
|
32048
|
+
return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh3));
|
|
31410
32049
|
};
|
|
31411
32050
|
}
|
|
31412
32051
|
keySignal(tty, fallback, sig, send2) {
|
|
@@ -31442,6 +32081,25 @@ var Launcher = class {
|
|
|
31442
32081
|
credOf(pid) {
|
|
31443
32082
|
return this.creds.get(pid)?.() ?? copyCred(ROOT);
|
|
31444
32083
|
}
|
|
32084
|
+
wireSealed() {
|
|
32085
|
+
this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
|
|
32086
|
+
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
32087
|
+
}
|
|
32088
|
+
embedderBroker(hook) {
|
|
32089
|
+
const broker = new EmbedderBroker(
|
|
32090
|
+
this.usersMode,
|
|
32091
|
+
{
|
|
32092
|
+
groupId: async (name2) => (await this.users.groups()).find((g) => g.name === name2)?.gid,
|
|
32093
|
+
userId: async (name2) => (await this.users.lookup(name2))?.uid
|
|
32094
|
+
},
|
|
32095
|
+
(pid) => this.tagsOf(pid)
|
|
32096
|
+
);
|
|
32097
|
+
broker.register(0, hook);
|
|
32098
|
+
return broker;
|
|
32099
|
+
}
|
|
32100
|
+
sessionOf(pid) {
|
|
32101
|
+
return this.sessions.get(pid) ?? NO_SESSION;
|
|
32102
|
+
}
|
|
31445
32103
|
tagOf(pid) {
|
|
31446
32104
|
return this.tags.get(pid)?.[0];
|
|
31447
32105
|
}
|
|
@@ -31713,8 +32371,8 @@ var Launcher = class {
|
|
|
31713
32371
|
for (const [path, mode] of layout) {
|
|
31714
32372
|
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
31715
32373
|
const real = await this.realLayoutPath(path);
|
|
31716
|
-
const
|
|
31717
|
-
if (
|
|
32374
|
+
const fresh3 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
32375
|
+
if (fresh3) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
31718
32376
|
}
|
|
31719
32377
|
}
|
|
31720
32378
|
async realLayoutPath(given) {
|
|
@@ -31887,9 +32545,10 @@ var Launcher = class {
|
|
|
31887
32545
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
31888
32546
|
return found;
|
|
31889
32547
|
}
|
|
31890
|
-
identity(account, given) {
|
|
32548
|
+
identity(account, given, session = 0) {
|
|
31891
32549
|
return {
|
|
31892
32550
|
...given,
|
|
32551
|
+
session: { client: session, uid: account.uid },
|
|
31893
32552
|
cred: credOf(account),
|
|
31894
32553
|
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
31895
32554
|
};
|
|
@@ -32076,7 +32735,7 @@ var Launcher = class {
|
|
|
32076
32735
|
cwd,
|
|
32077
32736
|
fds,
|
|
32078
32737
|
report,
|
|
32079
|
-
...this.identity(account, tagged)
|
|
32738
|
+
...this.identity(account, tagged, options.session)
|
|
32080
32739
|
});
|
|
32081
32740
|
leader = handle.pid;
|
|
32082
32741
|
void handle.exited.then(() => tty.hangup());
|
|
@@ -32133,7 +32792,7 @@ var Launcher = class {
|
|
|
32133
32792
|
fds,
|
|
32134
32793
|
report,
|
|
32135
32794
|
...group,
|
|
32136
|
-
...this.identity(account, tagged)
|
|
32795
|
+
...this.identity(account, tagged, options.session)
|
|
32137
32796
|
});
|
|
32138
32797
|
options.onStarted?.(handle.pid);
|
|
32139
32798
|
const status = await handle.exited;
|
|
@@ -32599,6 +33258,8 @@ function isUsersRequest(req) {
|
|
|
32599
33258
|
}
|
|
32600
33259
|
function transferOf2(req, result) {
|
|
32601
33260
|
if (result instanceof MessagePort) return [result];
|
|
33261
|
+
const bound3 = result?.port;
|
|
33262
|
+
if (req.op === "connect" && bound3 instanceof MessagePort) return [bound3];
|
|
32602
33263
|
if (req.op === "attest") return [result.bytes.buffer];
|
|
32603
33264
|
return void 0;
|
|
32604
33265
|
}
|
|
@@ -32632,7 +33293,35 @@ function usersOp(l, req) {
|
|
|
32632
33293
|
if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
|
|
32633
33294
|
return l.users.list();
|
|
32634
33295
|
}
|
|
32635
|
-
|
|
33296
|
+
var clientIds = 0;
|
|
33297
|
+
async function connectClient(deps, { user, tag, bind: bind2 }) {
|
|
33298
|
+
const l = await deps.ready();
|
|
33299
|
+
tagOption(tag);
|
|
33300
|
+
if (user !== void 0 && !await l.users.lookup(user)) {
|
|
33301
|
+
throw fsError("ENOENT", `no user ${String(user)}`);
|
|
33302
|
+
}
|
|
33303
|
+
const { port1, port2 } = new MessageChannel();
|
|
33304
|
+
const held = deps.held ? await deps.held() : void 0;
|
|
33305
|
+
const client = ++clientIds;
|
|
33306
|
+
const served2 = serveClient(port1, {
|
|
33307
|
+
launcher: deps.ready,
|
|
33308
|
+
signal: signalNumber,
|
|
33309
|
+
session: client,
|
|
33310
|
+
...bind2 === "attach" ? { attachRequests: true } : {},
|
|
33311
|
+
...user !== void 0 ? { user } : {},
|
|
33312
|
+
...tag !== void 0 ? { tag } : {},
|
|
33313
|
+
...deps.locks ? { locks: deps.locks } : {},
|
|
33314
|
+
...held ? { lock: held } : {}
|
|
33315
|
+
});
|
|
33316
|
+
deps.clients.add(served2);
|
|
33317
|
+
const unbind = bind2 && bind2 !== "attach" ? l.embedder.register(client, deps.requests.hookFor(client, bind2)) : void 0;
|
|
33318
|
+
void served2.closed.then(() => {
|
|
33319
|
+
deps.clients.delete(served2);
|
|
33320
|
+
unbind?.();
|
|
33321
|
+
});
|
|
33322
|
+
return unbind ? { port: port2, client } : port2;
|
|
33323
|
+
}
|
|
33324
|
+
function hostOptions(req, port, sealed, clipboard, lookup2, requests) {
|
|
32636
33325
|
return {
|
|
32637
33326
|
cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
|
|
32638
33327
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
@@ -32641,7 +33330,8 @@ function hostOptions(req, port, sealed, clipboard, lookup2) {
|
|
|
32641
33330
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|
|
32642
33331
|
...req.protect ? { protect: hostProtect(req.protect, lookup2) } : {},
|
|
32643
33332
|
...req.sealed ? { sealed } : {},
|
|
32644
|
-
...req.clipboard ? { clipboard } : {}
|
|
33333
|
+
...req.clipboard ? { clipboard } : {},
|
|
33334
|
+
...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {}
|
|
32645
33335
|
};
|
|
32646
33336
|
}
|
|
32647
33337
|
function hostProtect(init, lookup2) {
|
|
@@ -32676,6 +33366,7 @@ function serveKernel(port, deps) {
|
|
|
32676
33366
|
const clients = /* @__PURE__ */ new Set();
|
|
32677
33367
|
const sealed = sealedAsker(port);
|
|
32678
33368
|
const clipboard = clipboardAsker(port);
|
|
33369
|
+
const requests = embedderAsker(port);
|
|
32679
33370
|
const integrity = integrityAsker(port);
|
|
32680
33371
|
const approvals = sudoApprovals(port);
|
|
32681
33372
|
const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
|
|
@@ -32691,26 +33382,10 @@ function serveKernel(port, deps) {
|
|
|
32691
33382
|
});
|
|
32692
33383
|
return holding;
|
|
32693
33384
|
}
|
|
32694
|
-
|
|
32695
|
-
|
|
32696
|
-
|
|
32697
|
-
|
|
32698
|
-
throw fsError("ENOENT", `no user ${String(user)}`);
|
|
32699
|
-
}
|
|
32700
|
-
const { port1, port2 } = new MessageChannel();
|
|
32701
|
-
const held = locks ? await holdLock(locks) : void 0;
|
|
32702
|
-
const served2 = serveClient(port1, {
|
|
32703
|
-
launcher: ready2,
|
|
32704
|
-
signal: signalNumber,
|
|
32705
|
-
...user !== void 0 ? { user } : {},
|
|
32706
|
-
...tag !== void 0 ? { tag } : {},
|
|
32707
|
-
...locks ? { locks } : {},
|
|
32708
|
-
...held ? { lock: held } : {}
|
|
32709
|
-
});
|
|
32710
|
-
clients.add(served2);
|
|
32711
|
-
void served2.closed.then(() => clients.delete(served2));
|
|
32712
|
-
return port2;
|
|
32713
|
-
}
|
|
33385
|
+
const connect2 = (user, tag, bind2) => connectClient(
|
|
33386
|
+
{ ready: ready2, clients, requests, held: locks ? () => holdLock(locks) : void 0, locks },
|
|
33387
|
+
{ user, tag, bind: bind2 }
|
|
33388
|
+
);
|
|
32714
33389
|
async function ready2() {
|
|
32715
33390
|
if (!launcher) throw new Error("the kernel is not initialized");
|
|
32716
33391
|
return launcher;
|
|
@@ -32744,7 +33419,7 @@ function serveKernel(port, deps) {
|
|
|
32744
33419
|
...req.modules ? { modules: req.modules } : {},
|
|
32745
33420
|
...req.commands ? { commands: req.commands } : {},
|
|
32746
33421
|
...req.env ? { env: req.env } : {},
|
|
32747
|
-
...hostOptions(req, port, sealed.hook, clipboard.hook, integrity.lookup),
|
|
33422
|
+
...hostOptions(req, port, sealed.hook, clipboard.hook, integrity.lookup, requests),
|
|
32748
33423
|
...req.approve ? { approve: approvals.ask } : {},
|
|
32749
33424
|
...net,
|
|
32750
33425
|
...media ? { media } : {},
|
|
@@ -32772,7 +33447,7 @@ function serveKernel(port, deps) {
|
|
|
32772
33447
|
if (req.op === "init") return init(req);
|
|
32773
33448
|
if (isAttestRequest(req)) return attestOp(await ready2(), req);
|
|
32774
33449
|
if (req.op === "open-terminal") return terminal(req);
|
|
32775
|
-
if (req.op === "connect") return connect2(req.user, req.tag);
|
|
33450
|
+
if (req.op === "connect") return connect2(req.user, req.tag, req.requests);
|
|
32776
33451
|
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
32777
33452
|
if (req.op === "routes" || req.op === "transport-traits") {
|
|
32778
33453
|
return networkOp(await ready2(), req, remote);
|
|
@@ -32814,6 +33489,8 @@ function serveKernel(port, deps) {
|
|
|
32814
33489
|
if (sealedReply) return sealed.answer(sealedReply);
|
|
32815
33490
|
const clipboardReply = event.data.clipboardReply;
|
|
32816
33491
|
if (clipboardReply) return clipboard.answer(clipboardReply);
|
|
33492
|
+
const embedderReply = embedderReplyOf(event.data);
|
|
33493
|
+
if (embedderReply) return requests.answer(embedderReply);
|
|
32817
33494
|
const integrityReply = event.data.integrityReply;
|
|
32818
33495
|
if (integrityReply) return integrity.answer(integrityReply);
|
|
32819
33496
|
const grant = event.data.hostfsGrant;
|