@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/kernel-worker.js
CHANGED
|
@@ -652,8 +652,8 @@ function dirsChannel(metadata) {
|
|
|
652
652
|
function noticeOf(data) {
|
|
653
653
|
if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
|
|
654
654
|
const notice = data ?? {};
|
|
655
|
-
const
|
|
656
|
-
return { paths:
|
|
655
|
+
const strings3 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
656
|
+
return { paths: strings3(notice.paths), trees: strings3(notice.trees), outside: true };
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
// src/fs/types.ts
|
|
@@ -1427,15 +1427,15 @@ var RemoteWatch = class {
|
|
|
1427
1427
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
1428
1428
|
}
|
|
1429
1429
|
hold(add, drop, wait2) {
|
|
1430
|
-
const
|
|
1430
|
+
const fresh4 = add.filter(
|
|
1431
1431
|
([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
|
|
1432
1432
|
);
|
|
1433
1433
|
const gone = drop.filter((path) => this.myHolds.has(path));
|
|
1434
1434
|
for (const path of gone) this.myHolds.delete(path);
|
|
1435
|
-
for (const [path, fid] of
|
|
1436
|
-
if (
|
|
1435
|
+
for (const [path, fid] of fresh4) this.myHolds.set(path, fid);
|
|
1436
|
+
if (fresh4.length === 0 && gone.length === 0 || this.peers.size === 0)
|
|
1437
1437
|
return Promise.resolve();
|
|
1438
|
-
const seq2 = this.sendHolds(
|
|
1438
|
+
const seq2 = this.sendHolds(fresh4, gone);
|
|
1439
1439
|
if (!wait2) return Promise.resolve();
|
|
1440
1440
|
const heard = new Set(
|
|
1441
1441
|
[...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
|
|
@@ -2121,7 +2121,7 @@ var OpfsFs = class {
|
|
|
2121
2121
|
const parent = entries.at(-1);
|
|
2122
2122
|
const setgid = ((parent?.mode ?? 0) & S_ISGID2) !== 0;
|
|
2123
2123
|
const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
|
|
2124
|
-
const
|
|
2124
|
+
const want2 = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
|
|
2125
2125
|
let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
|
|
2126
2126
|
if (directory && setgid) mode = (mode ?? 493) | S_ISGID2;
|
|
2127
2127
|
const fields = {};
|
|
@@ -2129,7 +2129,7 @@ var OpfsFs = class {
|
|
|
2129
2129
|
fields.mode = mode;
|
|
2130
2130
|
}
|
|
2131
2131
|
if (self) fields.heir = void 0;
|
|
2132
|
-
if (ownOf(self) || !sameOwner(
|
|
2132
|
+
if (ownOf(self) || !sameOwner(want2, inherit(entries))) Object.assign(fields, want2);
|
|
2133
2133
|
return Object.keys(fields).length > 0 ? fields : void 0;
|
|
2134
2134
|
}
|
|
2135
2135
|
async stampNew(path, owner, directory) {
|
|
@@ -2366,9 +2366,9 @@ var OpfsFs = class {
|
|
|
2366
2366
|
this.lookups++;
|
|
2367
2367
|
if (split(path).length === 0) return this.root;
|
|
2368
2368
|
const cached = this.files.get(path);
|
|
2369
|
-
const
|
|
2370
|
-
if (cached && !
|
|
2371
|
-
if (cached &&
|
|
2369
|
+
const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
|
|
2370
|
+
if (cached && !fresh4) this.files.delete(path);
|
|
2371
|
+
if (cached && fresh4) {
|
|
2372
2372
|
if (this.glanced(cached)) return this.remember(path, cached);
|
|
2373
2373
|
try {
|
|
2374
2374
|
await this.glance(cached);
|
|
@@ -2586,8 +2586,8 @@ var OpfsFs = class {
|
|
|
2586
2586
|
glanced(handle) {
|
|
2587
2587
|
if (this.exact > 0) return void 0;
|
|
2588
2588
|
const seen = this.glances.get(handle);
|
|
2589
|
-
const
|
|
2590
|
-
return
|
|
2589
|
+
const fresh4 = seen && seen.changes === this.changes && this.recent(seen, GLANCE_LOOKUPS);
|
|
2590
|
+
return fresh4 ? seen.file : void 0;
|
|
2591
2591
|
}
|
|
2592
2592
|
async glance(handle) {
|
|
2593
2593
|
const at = performance.now();
|
|
@@ -2789,13 +2789,13 @@ var OpfsFs = class {
|
|
|
2789
2789
|
async keptFor(path) {
|
|
2790
2790
|
const at = normalizePath(path);
|
|
2791
2791
|
const cached = this.files.get(at);
|
|
2792
|
-
const
|
|
2793
|
-
const [target, existed] =
|
|
2792
|
+
const fresh4 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
|
|
2793
|
+
const [target, existed] = fresh4 ? [at, true] : await this.writeTarget(path);
|
|
2794
2794
|
if (!existed) return [void 0, target, false];
|
|
2795
2795
|
this.refuse(target);
|
|
2796
2796
|
const [entry] = await this.meta.get([target]);
|
|
2797
2797
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
2798
|
-
const kept =
|
|
2798
|
+
const kept = fresh4 ? cached : this.files.get(target);
|
|
2799
2799
|
const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
2800
2800
|
return [usable3 ? kept : void 0, target, true];
|
|
2801
2801
|
}
|
|
@@ -4342,21 +4342,21 @@ function latin1Bytes(text) {
|
|
|
4342
4342
|
return out;
|
|
4343
4343
|
}
|
|
4344
4344
|
function parseRequestHead(bytes) {
|
|
4345
|
-
const
|
|
4346
|
-
while (
|
|
4347
|
-
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(
|
|
4345
|
+
const lines3 = latin1(bytes).split(/\r?\n/);
|
|
4346
|
+
while (lines3.length > 0 && lines3[0] === "") lines3.shift();
|
|
4347
|
+
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines3.shift() ?? "");
|
|
4348
4348
|
if (!match) throw new HttpError(400, "malformed request line");
|
|
4349
4349
|
if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
|
|
4350
4350
|
return {
|
|
4351
4351
|
method: match[1],
|
|
4352
4352
|
target: match[2],
|
|
4353
4353
|
minor: Number(match[4]),
|
|
4354
|
-
headers: parseFields(
|
|
4354
|
+
headers: parseFields(lines3)
|
|
4355
4355
|
};
|
|
4356
4356
|
}
|
|
4357
|
-
function parseFields(
|
|
4357
|
+
function parseFields(lines3) {
|
|
4358
4358
|
const headers = [];
|
|
4359
|
-
for (const line of
|
|
4359
|
+
for (const line of lines3) {
|
|
4360
4360
|
if (line === "") continue;
|
|
4361
4361
|
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
4362
4362
|
throw new HttpError(400, "obsolete line folding");
|
|
@@ -4825,10 +4825,10 @@ var CdpFacade = class {
|
|
|
4825
4825
|
);
|
|
4826
4826
|
return;
|
|
4827
4827
|
}
|
|
4828
|
-
await this.write(conn, json(await this.list(runtime)));
|
|
4828
|
+
await this.write(conn, json(await this.list(runtime, conn.peerStamp)));
|
|
4829
4829
|
}
|
|
4830
|
-
async list(runtime) {
|
|
4831
|
-
const conn = await this.host(runtime);
|
|
4830
|
+
async list(runtime, stamp) {
|
|
4831
|
+
const conn = await this.host(runtime, stamp);
|
|
4832
4832
|
try {
|
|
4833
4833
|
const reply = await new Promise(
|
|
4834
4834
|
(resolve2, reject) => {
|
|
@@ -4841,10 +4841,10 @@ var CdpFacade = class {
|
|
|
4841
4841
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
4842
4842
|
};
|
|
4843
4843
|
conn.onmessage = (message3) => {
|
|
4844
|
-
const
|
|
4845
|
-
if (
|
|
4844
|
+
const parsed3 = JSON.parse(message3);
|
|
4845
|
+
if (parsed3.id !== 1) return;
|
|
4846
4846
|
clearTimeout(timer);
|
|
4847
|
-
resolve2(
|
|
4847
|
+
resolve2(parsed3);
|
|
4848
4848
|
};
|
|
4849
4849
|
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
4850
4850
|
}
|
|
@@ -4863,12 +4863,15 @@ var CdpFacade = class {
|
|
|
4863
4863
|
conn.close();
|
|
4864
4864
|
}
|
|
4865
4865
|
}
|
|
4866
|
-
host(runtime) {
|
|
4867
|
-
return this.options.hosts.connect(
|
|
4866
|
+
host(runtime, stamp) {
|
|
4867
|
+
return this.options.hosts.connect({
|
|
4868
|
+
...runtime ? { runtime } : {},
|
|
4869
|
+
...stamp ? { stamp } : {}
|
|
4870
|
+
});
|
|
4868
4871
|
}
|
|
4869
4872
|
async upgrade(conn, incoming, req, runtime) {
|
|
4870
4873
|
const accept2 = await acceptKey(req.headers);
|
|
4871
|
-
const host = await this.host(runtime);
|
|
4874
|
+
const host = await this.host(runtime, conn.peerStamp);
|
|
4872
4875
|
const limits = {
|
|
4873
4876
|
max: this.options.maxMessage ?? MAX_MESSAGE,
|
|
4874
4877
|
closeWaitMs: this.options.closeWaitMs ?? CLOSE_WAIT_MS
|
|
@@ -4948,6 +4951,7 @@ function enableCdp(net, hosts) {
|
|
|
4948
4951
|
let facade;
|
|
4949
4952
|
net.kernelOnly(CDP_PORT, true);
|
|
4950
4953
|
net.reserve(CDP_PORT);
|
|
4954
|
+
net.stampPeers(CDP_PORT);
|
|
4951
4955
|
net.activate({ family: "inet", host: "127.0.0.1", port: CDP_PORT }, () => {
|
|
4952
4956
|
const started = new CdpFacade({ net, hosts });
|
|
4953
4957
|
facade = started;
|
|
@@ -4990,7 +4994,7 @@ var CdpHosts = class {
|
|
|
4990
4994
|
if (!entry) throw new HttpError(503, NO_CDP_HOST);
|
|
4991
4995
|
let conn;
|
|
4992
4996
|
try {
|
|
4993
|
-
conn = await entry.hook(request3
|
|
4997
|
+
conn = await entry.hook(hookRequest(request3));
|
|
4994
4998
|
} catch (e) {
|
|
4995
4999
|
throw new HttpError(502, `CDP host: ${e instanceof Error ? e.message : String(e)}`);
|
|
4996
5000
|
}
|
|
@@ -5010,6 +5014,12 @@ var CdpHosts = class {
|
|
|
5010
5014
|
for (const entry of [this.embedder, ...this.clients]) if (entry) closeAll(entry);
|
|
5011
5015
|
}
|
|
5012
5016
|
};
|
|
5017
|
+
function hookRequest({ runtime, stamp }) {
|
|
5018
|
+
return {
|
|
5019
|
+
...runtime ? { runtime } : {},
|
|
5020
|
+
...stamp ? { stamp: structuredClone(stamp) } : {}
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5013
5023
|
function closeAll(entry) {
|
|
5014
5024
|
for (const conn of [...entry.open]) {
|
|
5015
5025
|
conn.close();
|
|
@@ -5065,10 +5075,129 @@ function portCdpHook(ask, gone) {
|
|
|
5065
5075
|
});
|
|
5066
5076
|
}
|
|
5067
5077
|
|
|
5068
|
-
// src/
|
|
5078
|
+
// src/devices-relay.ts
|
|
5079
|
+
function quiet() {
|
|
5080
|
+
return void 0;
|
|
5081
|
+
}
|
|
5082
|
+
function raised(failure2) {
|
|
5083
|
+
return Object.assign(new Error(failure2.message), { name: failure2.name });
|
|
5084
|
+
}
|
|
5069
5085
|
function fresh2(bytes) {
|
|
5070
5086
|
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
|
|
5071
5087
|
}
|
|
5088
|
+
function devicesAsker(port) {
|
|
5089
|
+
const remotes2 = /* @__PURE__ */ new Map();
|
|
5090
|
+
let next3 = 0;
|
|
5091
|
+
const post = (message3, transfer = []) => port.postMessage({ devices: message3 }, transfer);
|
|
5092
|
+
const push = (remote, item) => {
|
|
5093
|
+
remote.chunks.push(item);
|
|
5094
|
+
remote.wake?.();
|
|
5095
|
+
};
|
|
5096
|
+
const streams = (id2, remote) => ({
|
|
5097
|
+
readable: new ReadableStream(
|
|
5098
|
+
{
|
|
5099
|
+
pull: async (controller) => {
|
|
5100
|
+
if (remote.chunks.length === 0) {
|
|
5101
|
+
post({ id: id2, pull: true });
|
|
5102
|
+
await new Promise((resolve2) => {
|
|
5103
|
+
remote.wake = resolve2;
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
const item = remote.chunks.shift();
|
|
5107
|
+
if (item instanceof Uint8Array) controller.enqueue(item);
|
|
5108
|
+
else if (item === null || item === void 0) controller.close();
|
|
5109
|
+
else controller.error(raised(item));
|
|
5110
|
+
},
|
|
5111
|
+
cancel: (reason) => {
|
|
5112
|
+
post({ id: id2, cancel: String(reason ?? "closed") });
|
|
5113
|
+
}
|
|
5114
|
+
},
|
|
5115
|
+
{ highWaterMark: 0 }
|
|
5116
|
+
),
|
|
5117
|
+
writable: new WritableStream({
|
|
5118
|
+
write: (chunk2) => {
|
|
5119
|
+
const seq2 = ++remote.seq;
|
|
5120
|
+
const done5 = Promise.withResolvers();
|
|
5121
|
+
remote.writes.set(seq2, done5);
|
|
5122
|
+
const bytes = fresh2(chunk2);
|
|
5123
|
+
post({ id: id2, write: bytes, seq: seq2 }, [bytes.buffer]);
|
|
5124
|
+
return done5.promise;
|
|
5125
|
+
},
|
|
5126
|
+
abort: (reason) => {
|
|
5127
|
+
post({ id: id2, cancel: String(reason ?? "closed") });
|
|
5128
|
+
}
|
|
5129
|
+
}),
|
|
5130
|
+
control: (op, args) => {
|
|
5131
|
+
const seq2 = ++remote.seq;
|
|
5132
|
+
const done5 = Promise.withResolvers();
|
|
5133
|
+
remote.controls.set(seq2, done5);
|
|
5134
|
+
post({ id: id2, control: { seq: seq2, op, ...args ? { args } : {} } });
|
|
5135
|
+
if (op === "close") void done5.promise.finally(() => remotes2.delete(id2)).catch(quiet);
|
|
5136
|
+
return done5.promise;
|
|
5137
|
+
}
|
|
5138
|
+
});
|
|
5139
|
+
const hookFor = (client) => ({
|
|
5140
|
+
open: async (request3) => {
|
|
5141
|
+
const id2 = ++next3;
|
|
5142
|
+
const remote = {
|
|
5143
|
+
opened: Promise.withResolvers(),
|
|
5144
|
+
chunks: [],
|
|
5145
|
+
wake: void 0,
|
|
5146
|
+
writes: /* @__PURE__ */ new Map(),
|
|
5147
|
+
controls: /* @__PURE__ */ new Map(),
|
|
5148
|
+
seq: 0
|
|
5149
|
+
};
|
|
5150
|
+
remotes2.set(id2, remote);
|
|
5151
|
+
const abort = () => {
|
|
5152
|
+
remotes2.delete(id2);
|
|
5153
|
+
post({ id: id2, cancel: "interrupted" });
|
|
5154
|
+
remote.opened.reject(request3.signal.reason);
|
|
5155
|
+
};
|
|
5156
|
+
request3.signal.addEventListener("abort", abort, { once: true });
|
|
5157
|
+
const { path, flags: flags2, stamp } = request3;
|
|
5158
|
+
post({ id: id2, open: { client, path, flags: flags2, stamp } });
|
|
5159
|
+
try {
|
|
5160
|
+
await remote.opened.promise;
|
|
5161
|
+
} finally {
|
|
5162
|
+
request3.signal.removeEventListener("abort", abort);
|
|
5163
|
+
}
|
|
5164
|
+
return streams(id2, remote);
|
|
5165
|
+
}
|
|
5166
|
+
});
|
|
5167
|
+
const answer2 = (message3) => {
|
|
5168
|
+
const remote = remotes2.get(message3.id);
|
|
5169
|
+
if (!remote) return;
|
|
5170
|
+
if ("opened" in message3) remote.opened.resolve();
|
|
5171
|
+
else if ("failed" in message3) {
|
|
5172
|
+
remotes2.delete(message3.id);
|
|
5173
|
+
remote.opened.reject(raised(message3.failed));
|
|
5174
|
+
} else if ("chunk" in message3) push(remote, message3.chunk);
|
|
5175
|
+
else if ("done" in message3) push(remote, null);
|
|
5176
|
+
else if ("readError" in message3) push(remote, message3.readError);
|
|
5177
|
+
else if ("wrote" in message3) {
|
|
5178
|
+
remote.writes.get(message3.wrote)?.resolve();
|
|
5179
|
+
remote.writes.delete(message3.wrote);
|
|
5180
|
+
} else if ("writeError" in message3) {
|
|
5181
|
+
remote.writes.get(message3.seq)?.reject(raised(message3.writeError));
|
|
5182
|
+
remote.writes.delete(message3.seq);
|
|
5183
|
+
} else if ("answered" in message3) {
|
|
5184
|
+
remote.controls.get(message3.answered)?.resolve(message3.value);
|
|
5185
|
+
remote.controls.delete(message3.answered);
|
|
5186
|
+
} else {
|
|
5187
|
+
remote.controls.get(message3.refused)?.reject(raised(message3.error));
|
|
5188
|
+
remote.controls.delete(message3.refused);
|
|
5189
|
+
}
|
|
5190
|
+
};
|
|
5191
|
+
return { hookFor, answer: answer2 };
|
|
5192
|
+
}
|
|
5193
|
+
function devicesReplyOf(data) {
|
|
5194
|
+
return data?.devicesReply;
|
|
5195
|
+
}
|
|
5196
|
+
|
|
5197
|
+
// src/embedder-relay.ts
|
|
5198
|
+
function fresh3(bytes) {
|
|
5199
|
+
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
|
|
5200
|
+
}
|
|
5072
5201
|
function embedderAsker(port) {
|
|
5073
5202
|
const asks = /* @__PURE__ */ new Map();
|
|
5074
5203
|
let next3 = 0;
|
|
@@ -5107,7 +5236,7 @@ function embedderAsker(port) {
|
|
|
5107
5236
|
const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
|
|
5108
5237
|
if (got.done) post({ id: id2, bodyEnd: true });
|
|
5109
5238
|
else {
|
|
5110
|
-
const bytes =
|
|
5239
|
+
const bytes = fresh3(got.value);
|
|
5111
5240
|
post({ id: id2, body: bytes }, [bytes.buffer]);
|
|
5112
5241
|
}
|
|
5113
5242
|
};
|
|
@@ -5278,11 +5407,11 @@ function permOf(st, link2) {
|
|
|
5278
5407
|
...link2 !== void 0 ? { link: link2 } : {}
|
|
5279
5408
|
};
|
|
5280
5409
|
}
|
|
5281
|
-
function permits(cred, perm,
|
|
5410
|
+
function permits(cred, perm, want2, directory = true) {
|
|
5282
5411
|
const mode = perm.mode ?? (directory ? 493 : 420);
|
|
5283
|
-
if (cred.euid === 0) return (
|
|
5412
|
+
if (cred.euid === 0) return (want2 & X_OK) === 0 || directory || (mode & 73) !== 0;
|
|
5284
5413
|
const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
|
|
5285
|
-
return (mode >> shift &
|
|
5414
|
+
return (mode >> shift & want2) === want2;
|
|
5286
5415
|
}
|
|
5287
5416
|
function sticky(cred, dir, entry) {
|
|
5288
5417
|
if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
|
|
@@ -5357,27 +5486,27 @@ async function present(fs, path, directory) {
|
|
|
5357
5486
|
const st = await fs.lstat(path).catch(() => void 0);
|
|
5358
5487
|
return st !== void 0 && (!directory || st.isDirectory);
|
|
5359
5488
|
}
|
|
5360
|
-
async function finalOf(fs, walked,
|
|
5489
|
+
async function finalOf(fs, walked, want2, options) {
|
|
5361
5490
|
const perm = walked.perm;
|
|
5362
5491
|
if (options.follow === false && perm.link !== void 0) {
|
|
5363
5492
|
return [{ ...perm, mode: 511 }, false];
|
|
5364
5493
|
}
|
|
5365
|
-
if ((
|
|
5494
|
+
if ((want2 & X_OK) === 0 || options.directory !== void 0) {
|
|
5366
5495
|
return [perm, options.directory !== false];
|
|
5367
5496
|
}
|
|
5368
5497
|
const look = options.follow === false ? fs.lstat : fs.stat;
|
|
5369
5498
|
const st = await look.call(fs, walked.path).catch(() => void 0);
|
|
5370
5499
|
return st ? [permOf(st), st.isDirectory] : [perm, false];
|
|
5371
5500
|
}
|
|
5372
|
-
async function access(fs, cred, path,
|
|
5501
|
+
async function access(fs, cred, path, want2, options = {}) {
|
|
5373
5502
|
const walked = await walk(fs, cred, path, options.follow !== false, options.links);
|
|
5374
5503
|
if (walked.denied !== void 0) {
|
|
5375
5504
|
if (walked.link || await present(fs, walked.denied, true)) return walked;
|
|
5376
5505
|
return { path, perm: void 0, missing: true };
|
|
5377
5506
|
}
|
|
5378
5507
|
if (!walked.perm) return { ...walked, missing: true };
|
|
5379
|
-
const [perm, directory] = await finalOf(fs, walked,
|
|
5380
|
-
if (
|
|
5508
|
+
const [perm, directory] = await finalOf(fs, walked, want2, options);
|
|
5509
|
+
if (want2 === 0 || permits(cred, perm, want2, directory)) return walked;
|
|
5381
5510
|
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
5382
5511
|
return { ...walked, missing: true };
|
|
5383
5512
|
}
|
|
@@ -5483,11 +5612,11 @@ function ttyOpenable(g, tty) {
|
|
|
5483
5612
|
if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
|
|
5484
5613
|
deny(g, "open", tty.name ?? "/dev/tty");
|
|
5485
5614
|
}
|
|
5486
|
-
async function need(g, op, path,
|
|
5487
|
-
if (
|
|
5488
|
-
const walked = await access(g.fs, g.cred, path,
|
|
5615
|
+
async function need(g, op, path, want2, options = {}, shown2 = path) {
|
|
5616
|
+
if (want2 & W_OK && shown2 === path) await fence(g, op, path);
|
|
5617
|
+
const walked = await access(g.fs, g.cred, path, want2, linked(g, options));
|
|
5489
5618
|
if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown2);
|
|
5490
|
-
else if (
|
|
5619
|
+
else if (want2 & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
|
|
5491
5620
|
return walked;
|
|
5492
5621
|
}
|
|
5493
5622
|
async function readsAs(g, op, path, shown2) {
|
|
@@ -5636,10 +5765,10 @@ async function creatable(g, op, path) {
|
|
|
5636
5765
|
const target = await need(g, op, path, 0);
|
|
5637
5766
|
if (target.denied === void 0) await need(g, op, parentOf5(target.path), W_OK | X_OK, DIR, path);
|
|
5638
5767
|
}
|
|
5639
|
-
async function openable(g, op, path,
|
|
5768
|
+
async function openable(g, op, path, want2, create) {
|
|
5640
5769
|
const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
|
|
5641
5770
|
if (create && !st) return creatable(g, op, path);
|
|
5642
|
-
await need(g, op, path,
|
|
5771
|
+
await need(g, op, path, want2, { directory: st?.isDirectory ?? false });
|
|
5643
5772
|
}
|
|
5644
5773
|
|
|
5645
5774
|
// src/kernel/cred.ts
|
|
@@ -6080,8 +6209,8 @@ var FdTable = class _FdTable {
|
|
|
6080
6209
|
}
|
|
6081
6210
|
stdioTerminal() {
|
|
6082
6211
|
for (const fd of [0, 1, 2]) {
|
|
6083
|
-
const
|
|
6084
|
-
if (tty) return tty;
|
|
6212
|
+
const file = this.fds.get(fd)?.file;
|
|
6213
|
+
if (file?.tty && !file.device) return file.tty;
|
|
6085
6214
|
}
|
|
6086
6215
|
return void 0;
|
|
6087
6216
|
}
|
|
@@ -6382,13 +6511,13 @@ function addressBits(host) {
|
|
|
6382
6511
|
function parsePrefix(text) {
|
|
6383
6512
|
const parts2 = text.trim().split("/");
|
|
6384
6513
|
if (parts2.length > 2) return void 0;
|
|
6385
|
-
const [
|
|
6386
|
-
const
|
|
6387
|
-
if (!
|
|
6388
|
-
const max =
|
|
6514
|
+
const [address2 = "", size] = parts2;
|
|
6515
|
+
const parsed3 = addressBits(address2);
|
|
6516
|
+
if (!parsed3) return void 0;
|
|
6517
|
+
const max = parsed3.v6 ? 128 : 32;
|
|
6389
6518
|
const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
6390
6519
|
if (length2 < 0 || length2 > max) return void 0;
|
|
6391
|
-
return { ...
|
|
6520
|
+
return { ...parsed3, length: length2 };
|
|
6392
6521
|
}
|
|
6393
6522
|
function within4(prefix, addr) {
|
|
6394
6523
|
if (prefix.v6 !== addr.v6) return false;
|
|
@@ -6414,10 +6543,10 @@ var Routes = class {
|
|
|
6414
6543
|
this.ipv6 = ipv62;
|
|
6415
6544
|
}
|
|
6416
6545
|
set(table) {
|
|
6417
|
-
const
|
|
6418
|
-
const bad = (table.prefixes ?? []).find((_, i) => !
|
|
6546
|
+
const parsed3 = (table.prefixes ?? []).map(parsePrefix);
|
|
6547
|
+
const bad = (table.prefixes ?? []).find((_, i) => !parsed3[i]);
|
|
6419
6548
|
if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
|
|
6420
|
-
this.table =
|
|
6549
|
+
this.table = parsed3;
|
|
6421
6550
|
this.exit = table.exit === true;
|
|
6422
6551
|
}
|
|
6423
6552
|
classify(host) {
|
|
@@ -6953,10 +7082,10 @@ var KernelSocket = class _KernelSocket {
|
|
|
6953
7082
|
}
|
|
6954
7083
|
};
|
|
6955
7084
|
let made2;
|
|
6956
|
-
const
|
|
7085
|
+
const abandon2 = () => {
|
|
6957
7086
|
if (!keeps(signal.reason)) view2.close();
|
|
6958
7087
|
};
|
|
6959
|
-
signal.addEventListener("abort",
|
|
7088
|
+
signal.addEventListener("abort", abandon2, { once: true });
|
|
6960
7089
|
try {
|
|
6961
7090
|
made2 = await make(view2);
|
|
6962
7091
|
} catch (err) {
|
|
@@ -6969,7 +7098,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
6969
7098
|
throw signal.aborted ? signal.reason : err;
|
|
6970
7099
|
} finally {
|
|
6971
7100
|
handshaking = false;
|
|
6972
|
-
signal.removeEventListener("abort",
|
|
7101
|
+
signal.removeEventListener("abort", abandon2);
|
|
6973
7102
|
}
|
|
6974
7103
|
const plain3 = uplinkLink(made2.conn, (err) => {
|
|
6975
7104
|
this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
|
|
@@ -7158,6 +7287,7 @@ var LoopbackNet = class {
|
|
|
7158
7287
|
reserved = /* @__PURE__ */ new Set();
|
|
7159
7288
|
facades = /* @__PURE__ */ new Map();
|
|
7160
7289
|
proxyStamped;
|
|
7290
|
+
stamped = /* @__PURE__ */ new Set();
|
|
7161
7291
|
nextEphemeral = EPHEMERAL_FIRST;
|
|
7162
7292
|
route;
|
|
7163
7293
|
origins;
|
|
@@ -7232,6 +7362,9 @@ var LoopbackNet = class {
|
|
|
7232
7362
|
this.kernelOnly(port, true);
|
|
7233
7363
|
this.facades.set(port, name2);
|
|
7234
7364
|
}
|
|
7365
|
+
stampPeers(port) {
|
|
7366
|
+
this.stamped.add(port);
|
|
7367
|
+
}
|
|
7235
7368
|
stampProxy(port) {
|
|
7236
7369
|
this.proxyStamped = port;
|
|
7237
7370
|
}
|
|
@@ -7240,7 +7373,7 @@ var LoopbackNet = class {
|
|
|
7240
7373
|
}
|
|
7241
7374
|
stampsPeers(addr) {
|
|
7242
7375
|
if (addr.family !== "inet" || !isLoopback(canonicalHost(addr.host))) return false;
|
|
7243
|
-
return this.facades.has(addr.port) || this.proxyStamped === addr.port;
|
|
7376
|
+
return this.facades.has(addr.port) || this.stamped.has(addr.port) || this.proxyStamped === addr.port;
|
|
7244
7377
|
}
|
|
7245
7378
|
activate(addr, start2) {
|
|
7246
7379
|
this.activators.set(keyOf(addr), start2);
|
|
@@ -8114,7 +8247,7 @@ var UserDb = class {
|
|
|
8114
8247
|
joined(name2, extra);
|
|
8115
8248
|
group.push({ name: name2, gid: uid, members: [] });
|
|
8116
8249
|
passwd.push(row);
|
|
8117
|
-
const
|
|
8250
|
+
const fresh4 = !await this.fs.exists(home);
|
|
8118
8251
|
await this.fs.mkdir(home, { recursive: true });
|
|
8119
8252
|
try {
|
|
8120
8253
|
await this.provision(row, this.umaskFor(options), shared2);
|
|
@@ -8122,7 +8255,7 @@ var UserDb = class {
|
|
|
8122
8255
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
8123
8256
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
8124
8257
|
} catch (err) {
|
|
8125
|
-
if (
|
|
8258
|
+
if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
|
|
8126
8259
|
throw err;
|
|
8127
8260
|
}
|
|
8128
8261
|
remember();
|
|
@@ -8206,7 +8339,7 @@ var UserDb = class {
|
|
|
8206
8339
|
};
|
|
8207
8340
|
|
|
8208
8341
|
// src/client/protocol.ts
|
|
8209
|
-
var PROTOCOL = [1,
|
|
8342
|
+
var PROTOCOL = [1, 18];
|
|
8210
8343
|
var FS_METHODS = [
|
|
8211
8344
|
"readFile",
|
|
8212
8345
|
"writeFile",
|
|
@@ -8466,8 +8599,10 @@ function hostRegistries(send2, gone, port) {
|
|
|
8466
8599
|
const cdps = cdpRegistry(send2, gone);
|
|
8467
8600
|
const facades = facadeRegistry(send2, gone);
|
|
8468
8601
|
const requests = requestBinding(send2, port);
|
|
8602
|
+
const devices = deviceBinding(send2, port);
|
|
8469
8603
|
return {
|
|
8470
8604
|
requests,
|
|
8605
|
+
devices,
|
|
8471
8606
|
answer(l, req) {
|
|
8472
8607
|
if (req.op === "serve-cdp") return cdps.serve(l, req);
|
|
8473
8608
|
if (req.op === "unserve-cdp") return cdps.drop(req.registration);
|
|
@@ -8476,6 +8611,7 @@ function hostRegistries(send2, gone, port) {
|
|
|
8476
8611
|
},
|
|
8477
8612
|
close() {
|
|
8478
8613
|
requests.close();
|
|
8614
|
+
devices.close();
|
|
8479
8615
|
cdps.close();
|
|
8480
8616
|
facades.close();
|
|
8481
8617
|
}
|
|
@@ -8589,6 +8725,28 @@ function answered(pending, req, reply, detach) {
|
|
|
8589
8725
|
}
|
|
8590
8726
|
);
|
|
8591
8727
|
}
|
|
8728
|
+
async function helloRefused(host, hosts, message3) {
|
|
8729
|
+
return versionError(message3.protocol) ?? await hosts.requests.accept(host, message3) ?? await hosts.devices.accept(host, message3);
|
|
8730
|
+
}
|
|
8731
|
+
function deviceBinding(send2, port) {
|
|
8732
|
+
const asker = devicesAsker({ postMessage: (m, t) => send2(m, t) });
|
|
8733
|
+
let unbind;
|
|
8734
|
+
port.addEventListener("message", (event) => {
|
|
8735
|
+
const reply = devicesReplyOf(event.data);
|
|
8736
|
+
if (reply) asker.answer(reply);
|
|
8737
|
+
});
|
|
8738
|
+
return {
|
|
8739
|
+
async accept(host, hello) {
|
|
8740
|
+
if (!hello.devices) return void 0;
|
|
8741
|
+
if (!host.attachDevices || host.session === void 0)
|
|
8742
|
+
return "EPERM: this port may not answer device opens";
|
|
8743
|
+
const launcher = await host.launcher();
|
|
8744
|
+
unbind = launcher.devices.register(host.session, asker.hookFor(host.session));
|
|
8745
|
+
return void 0;
|
|
8746
|
+
},
|
|
8747
|
+
close: () => unbind?.()
|
|
8748
|
+
};
|
|
8749
|
+
}
|
|
8592
8750
|
function serveClient(port, host) {
|
|
8593
8751
|
let state = "new";
|
|
8594
8752
|
let self;
|
|
@@ -8622,9 +8780,9 @@ function serveClient(port, host) {
|
|
|
8622
8780
|
ended.resolve();
|
|
8623
8781
|
}
|
|
8624
8782
|
async function hello(message3) {
|
|
8625
|
-
const
|
|
8626
|
-
if (
|
|
8627
|
-
send2({ hello: { protocol: PROTOCOL, error:
|
|
8783
|
+
const refused5 = await helloRefused(host, hosts, message3);
|
|
8784
|
+
if (refused5) {
|
|
8785
|
+
send2({ hello: { protocol: PROTOCOL, error: refused5 } });
|
|
8628
8786
|
detach();
|
|
8629
8787
|
return;
|
|
8630
8788
|
}
|
|
@@ -8908,7 +9066,7 @@ function satisfies(version, range2) {
|
|
|
8908
9066
|
}
|
|
8909
9067
|
|
|
8910
9068
|
// src/version.ts
|
|
8911
|
-
var KERNEL_VERSION = true ? "1.
|
|
9069
|
+
var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
|
|
8912
9070
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8913
9071
|
function procVersion() {
|
|
8914
9072
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9200,10 +9358,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
|
|
|
9200
9358
|
const own = commandsOf(pkg, manifest);
|
|
9201
9359
|
const named = new Set(own.map((command) => command.name));
|
|
9202
9360
|
const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
|
|
9203
|
-
const
|
|
9361
|
+
const refused5 = engineRefusal(manifest, kernel);
|
|
9204
9362
|
const name2 = pkg.slice(root.length + 1);
|
|
9205
9363
|
const dependency = direct?.has(name2) === false;
|
|
9206
|
-
const marks = { ...
|
|
9364
|
+
const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
|
|
9207
9365
|
out.push({
|
|
9208
9366
|
name: name2,
|
|
9209
9367
|
pkg,
|
|
@@ -9235,7 +9393,7 @@ function winners(tier) {
|
|
|
9235
9393
|
}
|
|
9236
9394
|
return out;
|
|
9237
9395
|
}
|
|
9238
|
-
async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION,
|
|
9396
|
+
async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused5, global = /* @__PURE__ */ new Set()) {
|
|
9239
9397
|
const found = /* @__PURE__ */ new Map();
|
|
9240
9398
|
const physical = physicalResolver(fs);
|
|
9241
9399
|
const tiers = (typeof modules === "string" ? [modules] : modules).map(
|
|
@@ -9249,7 +9407,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
|
|
|
9249
9407
|
const dependencies = [];
|
|
9250
9408
|
for (const [index, roots] of tiers.entries()) {
|
|
9251
9409
|
const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
|
|
9252
|
-
if (
|
|
9410
|
+
if (refused5) refused5.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
|
|
9253
9411
|
await take(tier.filter((entry) => !entry.dependency));
|
|
9254
9412
|
dependencies.push(tier.filter((entry) => entry.dependency));
|
|
9255
9413
|
}
|
|
@@ -9853,9 +10011,9 @@ var TAGS_VERSION = 1;
|
|
|
9853
10011
|
async function readTags(fs) {
|
|
9854
10012
|
const text = await fs.readFile(TAGS_PATH).catch(() => "");
|
|
9855
10013
|
try {
|
|
9856
|
-
const
|
|
9857
|
-
if (!
|
|
9858
|
-
const { v: _, ...tables } =
|
|
10014
|
+
const parsed3 = JSON.parse(text);
|
|
10015
|
+
if (!parsed3 || typeof parsed3 !== "object") return {};
|
|
10016
|
+
const { v: _, ...tables } = parsed3;
|
|
9859
10017
|
return tables;
|
|
9860
10018
|
} catch {
|
|
9861
10019
|
return {};
|
|
@@ -9925,27 +10083,27 @@ var Crontabs = class {
|
|
|
9925
10083
|
const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
|
|
9926
10084
|
if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
|
|
9927
10085
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
9928
|
-
const
|
|
9929
|
-
await fs.writeFile(
|
|
9930
|
-
await fs.rename(
|
|
10086
|
+
const fresh4 = `${TAGS_PATH}.${crypto.randomUUID()}.new`;
|
|
10087
|
+
await fs.writeFile(fresh4, json5, owner);
|
|
10088
|
+
await fs.rename(fresh4, TAGS_PATH);
|
|
9931
10089
|
}
|
|
9932
10090
|
async install(path, target, text, caller) {
|
|
9933
10091
|
if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
|
|
9934
10092
|
const { errors } = parseCrontab(text, path);
|
|
9935
10093
|
if (errors.length > 0) {
|
|
9936
|
-
const
|
|
9937
|
-
return { error: ["crontab: errors in crontab file, cannot install", ...
|
|
10094
|
+
const lines3 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
|
|
10095
|
+
return { error: ["crontab: errors in crontab file, cannot install", ...lines3].join("\n") };
|
|
9938
10096
|
}
|
|
9939
10097
|
const { fs, owners } = this.o;
|
|
9940
10098
|
if (!await fs.exists(SPOOL_DIR)) {
|
|
9941
10099
|
await fs.mkdir(SPOOL_DIR, { recursive: true });
|
|
9942
10100
|
if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
|
|
9943
10101
|
}
|
|
9944
|
-
const
|
|
10102
|
+
const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
|
|
9945
10103
|
const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
|
|
9946
10104
|
await this.retag(path, text, caller);
|
|
9947
|
-
await fs.writeFile(
|
|
9948
|
-
await fs.rename(
|
|
10105
|
+
await fs.writeFile(fresh4, text, owner);
|
|
10106
|
+
await fs.rename(fresh4, path);
|
|
9949
10107
|
return { text: "" };
|
|
9950
10108
|
}
|
|
9951
10109
|
};
|
|
@@ -10266,15 +10424,15 @@ var UnlinkedKeeper = class {
|
|
|
10266
10424
|
const releases = [];
|
|
10267
10425
|
const take = async () => {
|
|
10268
10426
|
for (; ; ) {
|
|
10269
|
-
const
|
|
10270
|
-
if (
|
|
10271
|
-
|
|
10272
|
-
if (paths.some((path) => path >
|
|
10427
|
+
const fresh4 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
|
|
10428
|
+
if (fresh4.length === 0) return;
|
|
10429
|
+
fresh4.sort();
|
|
10430
|
+
if (paths.some((path) => path > fresh4[0])) {
|
|
10273
10431
|
for (const release of releases.splice(0)) release();
|
|
10274
|
-
|
|
10275
|
-
|
|
10432
|
+
fresh4.push(...paths.splice(0));
|
|
10433
|
+
fresh4.sort();
|
|
10276
10434
|
}
|
|
10277
|
-
for (const path of
|
|
10435
|
+
for (const path of fresh4) {
|
|
10278
10436
|
paths.push(path);
|
|
10279
10437
|
releases.push(await this.gate.acquire(path));
|
|
10280
10438
|
}
|
|
@@ -10670,10 +10828,10 @@ var Output = class {
|
|
|
10670
10828
|
};
|
|
10671
10829
|
function readStamp(text) {
|
|
10672
10830
|
try {
|
|
10673
|
-
const
|
|
10674
|
-
const jobs =
|
|
10675
|
-
const reboot =
|
|
10676
|
-
const when =
|
|
10831
|
+
const parsed3 = JSON.parse(text ?? "");
|
|
10832
|
+
const jobs = parsed3.jobs && typeof parsed3.jobs === "object" ? parsed3.jobs : {};
|
|
10833
|
+
const reboot = parsed3.reboot && typeof parsed3.reboot === "object" ? parsed3.reboot : {};
|
|
10834
|
+
const when = parsed3.when && typeof parsed3.when === "object" ? parsed3.when : {};
|
|
10677
10835
|
return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
|
|
10678
10836
|
} catch {
|
|
10679
10837
|
return { jobs: {}, reboot: {}, when: {} };
|
|
@@ -10793,10 +10951,10 @@ var CronService = class {
|
|
|
10793
10951
|
const text = JSON.stringify(stamp);
|
|
10794
10952
|
const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
|
|
10795
10953
|
if (old === text || old === void 0 && text === EMPTY_STAMP) return;
|
|
10796
|
-
const
|
|
10954
|
+
const fresh4 = `${STAMP_PATH}.new`;
|
|
10797
10955
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
10798
|
-
await fs.writeFile(
|
|
10799
|
-
await fs.rename(
|
|
10956
|
+
await fs.writeFile(fresh4, text, owner);
|
|
10957
|
+
await fs.rename(fresh4, STAMP_PATH);
|
|
10800
10958
|
}
|
|
10801
10959
|
async load() {
|
|
10802
10960
|
const { fs } = this.host;
|
|
@@ -10849,10 +11007,10 @@ var CronService = class {
|
|
|
10849
11007
|
await this.once(path, text, errors);
|
|
10850
11008
|
return table;
|
|
10851
11009
|
}
|
|
10852
|
-
async once(path, version,
|
|
11010
|
+
async once(path, version, lines3) {
|
|
10853
11011
|
if (this.reported.get(path) === version) return;
|
|
10854
11012
|
this.reported.set(path, version);
|
|
10855
|
-
for (const line of
|
|
11013
|
+
for (const line of lines3) await this.error(line);
|
|
10856
11014
|
}
|
|
10857
11015
|
async error(line) {
|
|
10858
11016
|
if (this.stopping.signal.aborted) return;
|
|
@@ -11086,8 +11244,8 @@ function canonicalHost2(host) {
|
|
|
11086
11244
|
function localV4(a, b) {
|
|
11087
11245
|
return a === 127 || a === 0 || a === 169 && b === 254;
|
|
11088
11246
|
}
|
|
11089
|
-
function hextets(
|
|
11090
|
-
const [head, tail2, extra] =
|
|
11247
|
+
function hextets(address2) {
|
|
11248
|
+
const [head, tail2, extra] = address2.split("::");
|
|
11091
11249
|
if (extra !== void 0) return void 0;
|
|
11092
11250
|
const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
11093
11251
|
const front = part(head);
|
|
@@ -11095,8 +11253,8 @@ function hextets(address) {
|
|
|
11095
11253
|
const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
11096
11254
|
return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
|
|
11097
11255
|
}
|
|
11098
|
-
function localV6(
|
|
11099
|
-
const g = hextets(
|
|
11256
|
+
function localV6(address2) {
|
|
11257
|
+
const g = hextets(address2);
|
|
11100
11258
|
if (!g) return true;
|
|
11101
11259
|
if ((g[0] & 65472) === 65152) return true;
|
|
11102
11260
|
const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
|
|
@@ -11469,8 +11627,8 @@ var RealmProxy = class {
|
|
|
11469
11627
|
}
|
|
11470
11628
|
async connect(conn, incoming, req, idle2) {
|
|
11471
11629
|
const target = tunnelTarget(req);
|
|
11472
|
-
const
|
|
11473
|
-
if (
|
|
11630
|
+
const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
11631
|
+
if (refused5) throw refused5;
|
|
11474
11632
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
11475
11633
|
try {
|
|
11476
11634
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -11914,15 +12072,15 @@ async function followLinks(fs, path) {
|
|
|
11914
12072
|
}
|
|
11915
12073
|
var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
|
|
11916
12074
|
var EMPTY2 = new Uint8Array(0);
|
|
11917
|
-
function
|
|
12075
|
+
function quiet2(promise) {
|
|
11918
12076
|
return promise.catch(() => void 0);
|
|
11919
12077
|
}
|
|
11920
12078
|
async function readsAs2(fs, cred, target) {
|
|
11921
|
-
const walked = await
|
|
12079
|
+
const walked = await quiet2(access(fs, cred, target, R_OK, { directory: false }));
|
|
11922
12080
|
return walked !== void 0 && walked.denied === void 0 && !walked.missing;
|
|
11923
12081
|
}
|
|
11924
12082
|
async function shownTarget(fs, target) {
|
|
11925
|
-
const st = await
|
|
12083
|
+
const st = await quiet2(fs.stat(target));
|
|
11926
12084
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
11927
12085
|
}
|
|
11928
12086
|
async function madeFor(owner, virtual) {
|
|
@@ -12245,10 +12403,10 @@ var FreshModules = class {
|
|
|
12245
12403
|
this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
|
|
12246
12404
|
}
|
|
12247
12405
|
forget(pid) {
|
|
12248
|
-
for (const [path,
|
|
12406
|
+
for (const [path, fresh4] of this.fresh) if (fresh4.pid === pid) this.fresh.delete(path);
|
|
12249
12407
|
}
|
|
12250
|
-
drop(
|
|
12251
|
-
for (const [key, note] of [...this.fresh]) if (note ===
|
|
12408
|
+
drop(fresh4) {
|
|
12409
|
+
for (const [key, note] of [...this.fresh]) if (note === fresh4) this.fresh.delete(key);
|
|
12252
12410
|
}
|
|
12253
12411
|
moved(from, to) {
|
|
12254
12412
|
const source = normalizePath(from);
|
|
@@ -12257,10 +12415,10 @@ var FreshModules = class {
|
|
|
12257
12415
|
for (const key of [...this.fresh.keys()]) {
|
|
12258
12416
|
if (key === target || within6(key, target)) this.fresh.delete(key);
|
|
12259
12417
|
}
|
|
12260
|
-
for (const [key,
|
|
12418
|
+
for (const [key, fresh4] of [...this.fresh]) {
|
|
12261
12419
|
if (key !== source && !within6(key, source)) continue;
|
|
12262
12420
|
this.fresh.delete(key);
|
|
12263
|
-
this.fresh.set(target + key.slice(source.length),
|
|
12421
|
+
this.fresh.set(target + key.slice(source.length), fresh4);
|
|
12264
12422
|
}
|
|
12265
12423
|
}
|
|
12266
12424
|
wrap(fs) {
|
|
@@ -12270,15 +12428,15 @@ var FreshModules = class {
|
|
|
12270
12428
|
};
|
|
12271
12429
|
const decide = async (given, known) => {
|
|
12272
12430
|
const path = normalizePath(given);
|
|
12273
|
-
const
|
|
12274
|
-
if (!
|
|
12431
|
+
const fresh4 = this.fresh.get(path);
|
|
12432
|
+
if (!fresh4) return;
|
|
12275
12433
|
const bytes = known && known.length >= MAGIC.length ? known : await head(path);
|
|
12276
|
-
const
|
|
12277
|
-
|
|
12278
|
-
if (
|
|
12434
|
+
const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
|
|
12435
|
+
fresh4.setup = false;
|
|
12436
|
+
if (want2 === fresh4.added) return;
|
|
12279
12437
|
const mode = (await fs.stat(path)).mode & 4095;
|
|
12280
|
-
await fs.chmod(path, mode & ~
|
|
12281
|
-
|
|
12438
|
+
await fs.chmod(path, mode & ~fresh4.added | want2);
|
|
12439
|
+
fresh4.added = want2;
|
|
12282
12440
|
};
|
|
12283
12441
|
const overrides = {
|
|
12284
12442
|
writeFile: async (path, content, owner) => {
|
|
@@ -12291,9 +12449,9 @@ var FreshModules = class {
|
|
|
12291
12449
|
}
|
|
12292
12450
|
},
|
|
12293
12451
|
chmod: async (path, mode) => {
|
|
12294
|
-
const
|
|
12295
|
-
if (
|
|
12296
|
-
else if (
|
|
12452
|
+
const fresh4 = this.fresh.get(normalizePath(path));
|
|
12453
|
+
if (fresh4?.setup && (mode & 4095) === (438 & ~fresh4.umask)) fresh4.setup = false;
|
|
12454
|
+
else if (fresh4) this.drop(fresh4);
|
|
12297
12455
|
await fs.chmod(path, mode);
|
|
12298
12456
|
},
|
|
12299
12457
|
rm: async (path, options) => {
|
|
@@ -12310,16 +12468,16 @@ var FreshModules = class {
|
|
|
12310
12468
|
link: async (from, to, follow2) => {
|
|
12311
12469
|
await fs.link(from, to, follow2);
|
|
12312
12470
|
const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
|
|
12313
|
-
const
|
|
12314
|
-
if (
|
|
12471
|
+
const fresh4 = this.fresh.get(normalizePath(source));
|
|
12472
|
+
if (fresh4) this.fresh.set(normalizePath(to), fresh4);
|
|
12315
12473
|
}
|
|
12316
12474
|
};
|
|
12317
12475
|
const { append } = fs;
|
|
12318
12476
|
if (append) {
|
|
12319
12477
|
overrides.append = async (path, bytes, options) => {
|
|
12320
|
-
const
|
|
12478
|
+
const fresh4 = this.fresh.has(normalizePath(path));
|
|
12321
12479
|
const end = await append.call(fs, path, bytes, options);
|
|
12322
|
-
if (
|
|
12480
|
+
if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
|
|
12323
12481
|
return end;
|
|
12324
12482
|
};
|
|
12325
12483
|
}
|
|
@@ -12646,7 +12804,7 @@ var FsWatchers = class {
|
|
|
12646
12804
|
};
|
|
12647
12805
|
const content = (run2, types) => async (...args) => {
|
|
12648
12806
|
const at = normalizePath(args[0]);
|
|
12649
|
-
const
|
|
12807
|
+
const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
|
|
12650
12808
|
let done5 = false;
|
|
12651
12809
|
try {
|
|
12652
12810
|
await run2(...args);
|
|
@@ -12655,11 +12813,11 @@ var FsWatchers = class {
|
|
|
12655
12813
|
const real = await realOf(at);
|
|
12656
12814
|
const paths = real === at ? [at] : [at, real];
|
|
12657
12815
|
this.report(paths);
|
|
12658
|
-
if (done5) for (const type of types(
|
|
12816
|
+
if (done5) for (const type of types(fresh4, args)) this.emit(type, paths);
|
|
12659
12817
|
}
|
|
12660
12818
|
};
|
|
12661
|
-
const written2 = (
|
|
12662
|
-
const overwritten = (
|
|
12819
|
+
const written2 = (fresh4) => fresh4 ? ["rename", "change"] : ["change"];
|
|
12820
|
+
const overwritten = (fresh4, args) => fresh4 || args[1].length === 0 ? written2(fresh4) : ["change", "change"];
|
|
12663
12821
|
const reshaping = (run2) => async (...args) => {
|
|
12664
12822
|
this.reshaped();
|
|
12665
12823
|
try {
|
|
@@ -12692,14 +12850,14 @@ var FsWatchers = class {
|
|
|
12692
12850
|
if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
|
|
12693
12851
|
}
|
|
12694
12852
|
}
|
|
12695
|
-
const
|
|
12853
|
+
const fresh4 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
|
|
12696
12854
|
let done5 = false;
|
|
12697
12855
|
try {
|
|
12698
12856
|
await fs.mkdir(path, options);
|
|
12699
12857
|
done5 = true;
|
|
12700
12858
|
} finally {
|
|
12701
12859
|
this.report([...created4, path]);
|
|
12702
|
-
if (done5) this.emit("rename",
|
|
12860
|
+
if (done5) this.emit("rename", fresh4 ? [...created4, path] : created4);
|
|
12703
12861
|
}
|
|
12704
12862
|
},
|
|
12705
12863
|
rm: reshaping(async (path, options) => {
|
|
@@ -13209,15 +13367,15 @@ function embedderFailure(err) {
|
|
|
13209
13367
|
return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
|
|
13210
13368
|
}
|
|
13211
13369
|
function headerOf(bytes) {
|
|
13212
|
-
let
|
|
13370
|
+
let parsed3;
|
|
13213
13371
|
try {
|
|
13214
|
-
|
|
13372
|
+
parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
13215
13373
|
} catch {
|
|
13216
13374
|
throw new KernelError("EINVAL", "the request header is not JSON");
|
|
13217
13375
|
}
|
|
13218
|
-
if (typeof
|
|
13376
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
13219
13377
|
throw new KernelError("EINVAL", "the request header is not an object");
|
|
13220
|
-
const { kind, ...options } =
|
|
13378
|
+
const { kind, ...options } = parsed3;
|
|
13221
13379
|
if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
|
|
13222
13380
|
throw new KernelError("EINVAL", "the request has no valid kind");
|
|
13223
13381
|
return { kind, options };
|
|
@@ -13644,6 +13802,966 @@ var EmbedderBroker = class {
|
|
|
13644
13802
|
}
|
|
13645
13803
|
};
|
|
13646
13804
|
|
|
13805
|
+
// src/kernel/serial-ioctl.ts
|
|
13806
|
+
var IOCTL = {
|
|
13807
|
+
TCGETS: 21505,
|
|
13808
|
+
TCSETS: 21506,
|
|
13809
|
+
TCSETSW: 21507,
|
|
13810
|
+
TCSETSF: 21508,
|
|
13811
|
+
TCSBRK: 21513,
|
|
13812
|
+
TCFLSH: 21515,
|
|
13813
|
+
TIOCEXCL: 21516,
|
|
13814
|
+
TIOCNXCL: 21517,
|
|
13815
|
+
TIOCSCTTY: 21518,
|
|
13816
|
+
TIOCGPGRP: 21519,
|
|
13817
|
+
TIOCSPGRP: 21520,
|
|
13818
|
+
TIOCOUTQ: 21521,
|
|
13819
|
+
TIOCGWINSZ: 21523,
|
|
13820
|
+
TIOCSWINSZ: 21524,
|
|
13821
|
+
TIOCMGET: 21525,
|
|
13822
|
+
TIOCMBIS: 21526,
|
|
13823
|
+
TIOCMBIC: 21527,
|
|
13824
|
+
TIOCMSET: 21528,
|
|
13825
|
+
FIONREAD: 21531,
|
|
13826
|
+
TIOCNOTTY: 21538,
|
|
13827
|
+
TCSBRKP: 21541,
|
|
13828
|
+
TIOCSBRK: 21543,
|
|
13829
|
+
TIOCCBRK: 21544,
|
|
13830
|
+
TCGETS2: 2150388778,
|
|
13831
|
+
TCSETS2: 1076646955,
|
|
13832
|
+
TCSETSW2: 1076646956,
|
|
13833
|
+
TCSETSF2: 1076646957
|
|
13834
|
+
};
|
|
13835
|
+
var TERMIOS_GETS = /* @__PURE__ */ new Set([IOCTL.TCGETS, IOCTL.TCGETS2]);
|
|
13836
|
+
var TERMIOS_SETS = /* @__PURE__ */ new Set([
|
|
13837
|
+
IOCTL.TCSETS,
|
|
13838
|
+
IOCTL.TCSETSW,
|
|
13839
|
+
IOCTL.TCSETSF,
|
|
13840
|
+
IOCTL.TCSETS2,
|
|
13841
|
+
IOCTL.TCSETSW2,
|
|
13842
|
+
IOCTL.TCSETSF2
|
|
13843
|
+
]);
|
|
13844
|
+
var INT_OUT = /* @__PURE__ */ new Set([
|
|
13845
|
+
IOCTL.TIOCMGET,
|
|
13846
|
+
IOCTL.FIONREAD,
|
|
13847
|
+
IOCTL.TIOCOUTQ
|
|
13848
|
+
]);
|
|
13849
|
+
var INT_IN = /* @__PURE__ */ new Set([
|
|
13850
|
+
IOCTL.TIOCMBIS,
|
|
13851
|
+
IOCTL.TIOCMBIC,
|
|
13852
|
+
IOCTL.TIOCMSET
|
|
13853
|
+
]);
|
|
13854
|
+
var INT_ARG = /* @__PURE__ */ new Set([IOCTL.TCSBRK, IOCTL.TCSBRKP, IOCTL.TCFLSH]);
|
|
13855
|
+
var TIOCM = {
|
|
13856
|
+
DTR: 2,
|
|
13857
|
+
RTS: 4,
|
|
13858
|
+
CTS: 32,
|
|
13859
|
+
CD: 64,
|
|
13860
|
+
RI: 128,
|
|
13861
|
+
DSR: 256
|
|
13862
|
+
};
|
|
13863
|
+
var CBAUD = 4111;
|
|
13864
|
+
var BOTHER = 4096;
|
|
13865
|
+
var IBSHIFT = 16;
|
|
13866
|
+
var CSIZE = 48;
|
|
13867
|
+
var CSTOPB = 64;
|
|
13868
|
+
var PARENB = 256;
|
|
13869
|
+
var PARODD = 512;
|
|
13870
|
+
var CRTSCTS = 2147483648;
|
|
13871
|
+
var SPEEDS = [
|
|
13872
|
+
0,
|
|
13873
|
+
50,
|
|
13874
|
+
75,
|
|
13875
|
+
110,
|
|
13876
|
+
134,
|
|
13877
|
+
150,
|
|
13878
|
+
200,
|
|
13879
|
+
300,
|
|
13880
|
+
600,
|
|
13881
|
+
1200,
|
|
13882
|
+
1800,
|
|
13883
|
+
2400,
|
|
13884
|
+
4800,
|
|
13885
|
+
9600,
|
|
13886
|
+
19200,
|
|
13887
|
+
38400
|
|
13888
|
+
];
|
|
13889
|
+
var FAST = [
|
|
13890
|
+
0,
|
|
13891
|
+
57600,
|
|
13892
|
+
115200,
|
|
13893
|
+
230400,
|
|
13894
|
+
460800,
|
|
13895
|
+
5e5,
|
|
13896
|
+
576e3,
|
|
13897
|
+
921600,
|
|
13898
|
+
1e6,
|
|
13899
|
+
1152e3,
|
|
13900
|
+
15e5,
|
|
13901
|
+
2e6,
|
|
13902
|
+
25e5,
|
|
13903
|
+
3e6,
|
|
13904
|
+
35e5,
|
|
13905
|
+
4e6
|
|
13906
|
+
];
|
|
13907
|
+
function speedOf(code) {
|
|
13908
|
+
if (code === BOTHER) return void 0;
|
|
13909
|
+
return code & BOTHER ? FAST[code & 15] : SPEEDS[code & 15];
|
|
13910
|
+
}
|
|
13911
|
+
function speedsOf(termios, current, extended) {
|
|
13912
|
+
const out = termios.c_cflag & CBAUD;
|
|
13913
|
+
const ospeed = speedOf(out) ?? (extended ? (termios.c_ospeed ?? current.ospeed) >>> 0 : current.ospeed);
|
|
13914
|
+
const inCode = termios.c_cflag >>> IBSHIFT & CBAUD;
|
|
13915
|
+
if (inCode === 0) return { ispeed: ospeed, ospeed };
|
|
13916
|
+
const ispeed = speedOf(inCode) ?? (extended ? (termios.c_ispeed ?? current.ispeed) >>> 0 : current.ispeed);
|
|
13917
|
+
return { ispeed, ospeed };
|
|
13918
|
+
}
|
|
13919
|
+
function settingsOf(cflag, baudRate) {
|
|
13920
|
+
return {
|
|
13921
|
+
baudRate,
|
|
13922
|
+
dataBits: 5 + ((cflag & CSIZE) >> 4),
|
|
13923
|
+
parity: cflag & PARENB ? cflag & PARODD ? "odd" : "even" : "none",
|
|
13924
|
+
stopBits: cflag & CSTOPB ? 2 : 1,
|
|
13925
|
+
flowControl: cflag & CRTSCTS ? "hardware" : "none"
|
|
13926
|
+
};
|
|
13927
|
+
}
|
|
13928
|
+
function sameSettings(a, b) {
|
|
13929
|
+
return a.baudRate === b.baudRate && a.dataBits === b.dataBits && a.parity === b.parity && a.stopBits === b.stopBits && a.flowControl === b.flowControl;
|
|
13930
|
+
}
|
|
13931
|
+
var SERIAL_CFLAG = 13 | 48 | 128 | 1024 | 2048;
|
|
13932
|
+
var SERIAL_LFLAG = 16 | 32 | 512 | 2048;
|
|
13933
|
+
var SERIAL_OFLAG = 4;
|
|
13934
|
+
|
|
13935
|
+
// src/kernel/tty.ts
|
|
13936
|
+
var ICRNL = 256;
|
|
13937
|
+
var IUTF8 = 16384;
|
|
13938
|
+
var OPOST = 1;
|
|
13939
|
+
var ONLCR = 4;
|
|
13940
|
+
var ISIG = 1;
|
|
13941
|
+
var ICANON = 2;
|
|
13942
|
+
var ECHO = 8;
|
|
13943
|
+
var ECHOE = 16;
|
|
13944
|
+
var ECHOCTL = 512;
|
|
13945
|
+
var NOFLSH = 128;
|
|
13946
|
+
var IEXTEN = 32768;
|
|
13947
|
+
var VINTR = 0;
|
|
13948
|
+
var VQUIT = 1;
|
|
13949
|
+
var VERASE = 2;
|
|
13950
|
+
var VKILL = 3;
|
|
13951
|
+
var VEOF = 4;
|
|
13952
|
+
var VSUSP = 10;
|
|
13953
|
+
var VWERASE = 14;
|
|
13954
|
+
function defaultTermios() {
|
|
13955
|
+
const cCc = new Array(32).fill(0);
|
|
13956
|
+
Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
|
|
13957
|
+
Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
|
|
13958
|
+
Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
|
|
13959
|
+
return {
|
|
13960
|
+
c_iflag: ICRNL | 1024 | 8192 | IUTF8,
|
|
13961
|
+
c_oflag: OPOST | ONLCR,
|
|
13962
|
+
c_cflag: 15 | 48 | 128,
|
|
13963
|
+
c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
|
|
13964
|
+
c_cc: cCc
|
|
13965
|
+
};
|
|
13966
|
+
}
|
|
13967
|
+
var encoder3 = new TextEncoder();
|
|
13968
|
+
function isContinuation(byte) {
|
|
13969
|
+
return (byte & 192) === 128;
|
|
13970
|
+
}
|
|
13971
|
+
var KernelTty = class {
|
|
13972
|
+
termios = defaultTermios();
|
|
13973
|
+
cols = 80;
|
|
13974
|
+
rows = 24;
|
|
13975
|
+
readable = [];
|
|
13976
|
+
line = [];
|
|
13977
|
+
waiters = [];
|
|
13978
|
+
hungUp = false;
|
|
13979
|
+
name;
|
|
13980
|
+
owner;
|
|
13981
|
+
onRead;
|
|
13982
|
+
readers = 0;
|
|
13983
|
+
screen;
|
|
13984
|
+
signal;
|
|
13985
|
+
constructor(screen, signal) {
|
|
13986
|
+
this.screen = screen;
|
|
13987
|
+
this.signal = signal;
|
|
13988
|
+
}
|
|
13989
|
+
receive(bytes) {
|
|
13990
|
+
const { c_iflag, c_lflag } = this.termios;
|
|
13991
|
+
if (!(c_iflag & ICRNL) && !(c_lflag & (ISIG | ICANON | ECHO))) {
|
|
13992
|
+
if (bytes.length > 0) this.readable.push(bytes.slice());
|
|
13993
|
+
} else for (const byte of bytes) this.key(byte);
|
|
13994
|
+
this.wake();
|
|
13995
|
+
}
|
|
13996
|
+
resize(cols, rows) {
|
|
13997
|
+
if (cols === this.cols && rows === this.rows) return;
|
|
13998
|
+
this.setSize(cols, rows);
|
|
13999
|
+
this.signal(SIG.WINCH);
|
|
14000
|
+
}
|
|
14001
|
+
hangup() {
|
|
14002
|
+
this.hungUp = true;
|
|
14003
|
+
this.wake();
|
|
14004
|
+
}
|
|
14005
|
+
signalHangup() {
|
|
14006
|
+
this.signal(SIG.HUP);
|
|
14007
|
+
}
|
|
14008
|
+
setSize(cols, rows) {
|
|
14009
|
+
this.cols = cols;
|
|
14010
|
+
this.rows = rows;
|
|
14011
|
+
}
|
|
14012
|
+
tcgets() {
|
|
14013
|
+
return { ...this.termios, c_cc: [...this.termios.c_cc] };
|
|
14014
|
+
}
|
|
14015
|
+
tcsets(termios, flush = false) {
|
|
14016
|
+
const wasCanonical = this.canonical;
|
|
14017
|
+
this.termios = { ...termios, c_cc: [...termios.c_cc] };
|
|
14018
|
+
if (flush) {
|
|
14019
|
+
this.line = [];
|
|
14020
|
+
this.readable = [];
|
|
14021
|
+
}
|
|
14022
|
+
if (wasCanonical && !this.canonical && this.line.length > 0) {
|
|
14023
|
+
this.readable.push(Uint8Array.from(this.line));
|
|
14024
|
+
this.line = [];
|
|
14025
|
+
this.wake();
|
|
14026
|
+
}
|
|
14027
|
+
}
|
|
14028
|
+
winsize() {
|
|
14029
|
+
return [this.rows, this.cols];
|
|
14030
|
+
}
|
|
14031
|
+
file() {
|
|
14032
|
+
return new OpenFile({
|
|
14033
|
+
read: (max, signal) => this.read(max, signal),
|
|
14034
|
+
write: async (bytes) => {
|
|
14035
|
+
if (this.hungUp) throw new KernelError("EIO");
|
|
14036
|
+
this.output(bytes);
|
|
14037
|
+
return bytes.length;
|
|
14038
|
+
},
|
|
14039
|
+
poll: () => ({
|
|
14040
|
+
readable: this.readable.length > 0 || this.hungUp,
|
|
14041
|
+
writable: true,
|
|
14042
|
+
hangup: this.hungUp,
|
|
14043
|
+
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
14044
|
+
}),
|
|
14045
|
+
changed: (signal) => this.changed(signal),
|
|
14046
|
+
close: () => {
|
|
14047
|
+
},
|
|
14048
|
+
tty: this
|
|
14049
|
+
});
|
|
14050
|
+
}
|
|
14051
|
+
get canonical() {
|
|
14052
|
+
return (this.termios.c_lflag & ICANON) !== 0;
|
|
14053
|
+
}
|
|
14054
|
+
cc(slot2) {
|
|
14055
|
+
return this.termios.c_cc[slot2] ?? 0;
|
|
14056
|
+
}
|
|
14057
|
+
key(input) {
|
|
14058
|
+
const { c_iflag, c_lflag } = this.termios;
|
|
14059
|
+
const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
|
|
14060
|
+
if (c_lflag & ISIG && this.signalKey(byte)) return;
|
|
14061
|
+
if (!this.canonical) {
|
|
14062
|
+
this.readable.push(Uint8Array.of(byte));
|
|
14063
|
+
if (c_lflag & ECHO) this.echo(byte);
|
|
14064
|
+
return;
|
|
14065
|
+
}
|
|
14066
|
+
this.edit(byte);
|
|
14067
|
+
}
|
|
14068
|
+
signalKey(byte) {
|
|
14069
|
+
const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
|
|
14070
|
+
if (!sig || byte === 0) return false;
|
|
14071
|
+
if (this.termios.c_lflag & ECHO) this.echo(byte);
|
|
14072
|
+
if (!(this.termios.c_lflag & NOFLSH)) {
|
|
14073
|
+
this.line = [];
|
|
14074
|
+
this.readable = [];
|
|
14075
|
+
}
|
|
14076
|
+
this.signal(sig);
|
|
14077
|
+
return true;
|
|
14078
|
+
}
|
|
14079
|
+
edit(byte) {
|
|
14080
|
+
const echo = (this.termios.c_lflag & ECHO) !== 0;
|
|
14081
|
+
if (byte === this.cc(VERASE) || byte === 8) {
|
|
14082
|
+
this.rubOut(this.lastCharLength());
|
|
14083
|
+
} else if (byte === this.cc(VKILL)) {
|
|
14084
|
+
this.rubOut(this.line.length);
|
|
14085
|
+
} else if (byte === this.cc(VWERASE)) {
|
|
14086
|
+
this.rubOut(this.lastWordLength());
|
|
14087
|
+
} else if (byte === this.cc(VEOF)) {
|
|
14088
|
+
this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
|
|
14089
|
+
this.line = [];
|
|
14090
|
+
} else if (byte === 10) {
|
|
14091
|
+
this.line.push(byte);
|
|
14092
|
+
this.readable.push(Uint8Array.from(this.line));
|
|
14093
|
+
this.line = [];
|
|
14094
|
+
if (echo) this.output(encoder3.encode("\n"));
|
|
14095
|
+
} else {
|
|
14096
|
+
this.line.push(byte);
|
|
14097
|
+
if (echo) this.echo(byte);
|
|
14098
|
+
}
|
|
14099
|
+
}
|
|
14100
|
+
lastWordLength() {
|
|
14101
|
+
let end = this.line.length;
|
|
14102
|
+
while (end > 0 && this.line[end - 1] === 32) end--;
|
|
14103
|
+
while (end > 0 && this.line[end - 1] !== 32) end--;
|
|
14104
|
+
return this.line.length - end;
|
|
14105
|
+
}
|
|
14106
|
+
lastCharLength() {
|
|
14107
|
+
const { line } = this;
|
|
14108
|
+
let n = Math.min(1, line.length);
|
|
14109
|
+
if (this.termios.c_iflag & IUTF8) {
|
|
14110
|
+
while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
|
|
14111
|
+
}
|
|
14112
|
+
return n;
|
|
14113
|
+
}
|
|
14114
|
+
rubOut(n) {
|
|
14115
|
+
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
14116
|
+
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
14117
|
+
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
14118
|
+
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
14119
|
+
this.output(encoder3.encode("\b \b".repeat(cells)), false);
|
|
14120
|
+
}
|
|
14121
|
+
}
|
|
14122
|
+
echo(byte) {
|
|
14123
|
+
if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
|
|
14124
|
+
this.output(Uint8Array.of(94, byte + 64), false);
|
|
14125
|
+
return;
|
|
14126
|
+
}
|
|
14127
|
+
this.output(Uint8Array.of(byte));
|
|
14128
|
+
}
|
|
14129
|
+
output(bytes, translate2 = true) {
|
|
14130
|
+
const { c_oflag } = this.termios;
|
|
14131
|
+
if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
|
|
14132
|
+
this.screen.write(bytes);
|
|
14133
|
+
return;
|
|
14134
|
+
}
|
|
14135
|
+
const out = [];
|
|
14136
|
+
for (const byte of bytes) {
|
|
14137
|
+
if (byte === 10) out.push(13);
|
|
14138
|
+
out.push(byte);
|
|
14139
|
+
}
|
|
14140
|
+
this.screen.write(Uint8Array.from(out));
|
|
14141
|
+
}
|
|
14142
|
+
get reading() {
|
|
14143
|
+
return this.readers > 0;
|
|
14144
|
+
}
|
|
14145
|
+
async read(max, signal) {
|
|
14146
|
+
this.onRead?.();
|
|
14147
|
+
this.readers++;
|
|
14148
|
+
try {
|
|
14149
|
+
return await this.take(max, signal);
|
|
14150
|
+
} finally {
|
|
14151
|
+
this.readers--;
|
|
14152
|
+
}
|
|
14153
|
+
}
|
|
14154
|
+
async take(max, signal) {
|
|
14155
|
+
while (this.readable.length === 0) {
|
|
14156
|
+
if (this.hungUp) return new Uint8Array(0);
|
|
14157
|
+
await this.changed(signal);
|
|
14158
|
+
}
|
|
14159
|
+
const head = this.readable[0];
|
|
14160
|
+
if (head === null) {
|
|
14161
|
+
this.readable.shift();
|
|
14162
|
+
return new Uint8Array(0);
|
|
14163
|
+
}
|
|
14164
|
+
if (head === void 0) return new Uint8Array(0);
|
|
14165
|
+
const chunks = [];
|
|
14166
|
+
let n = 0;
|
|
14167
|
+
while (this.readable.length > 0 && n < max) {
|
|
14168
|
+
const next3 = this.readable[0];
|
|
14169
|
+
if (next3 === null || next3 === void 0) break;
|
|
14170
|
+
const take = next3.subarray(0, max - n);
|
|
14171
|
+
chunks.push(take);
|
|
14172
|
+
n += take.length;
|
|
14173
|
+
if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
|
|
14174
|
+
else this.readable.shift();
|
|
14175
|
+
if (this.canonical) break;
|
|
14176
|
+
}
|
|
14177
|
+
const out = new Uint8Array(n);
|
|
14178
|
+
let at = 0;
|
|
14179
|
+
for (const chunk2 of chunks) {
|
|
14180
|
+
out.set(chunk2, at);
|
|
14181
|
+
at += chunk2.length;
|
|
14182
|
+
}
|
|
14183
|
+
return out;
|
|
14184
|
+
}
|
|
14185
|
+
changed(signal) {
|
|
14186
|
+
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
14187
|
+
return new Promise((resolve2, reject) => {
|
|
14188
|
+
const onAbort = () => {
|
|
14189
|
+
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
14190
|
+
reject(new KernelError("EINTR"));
|
|
14191
|
+
};
|
|
14192
|
+
const waiter = () => {
|
|
14193
|
+
signal?.removeEventListener("abort", onAbort);
|
|
14194
|
+
resolve2();
|
|
14195
|
+
};
|
|
14196
|
+
this.waiters.push(waiter);
|
|
14197
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
14198
|
+
});
|
|
14199
|
+
}
|
|
14200
|
+
wake() {
|
|
14201
|
+
const waiters = this.waiters;
|
|
14202
|
+
this.waiters = [];
|
|
14203
|
+
for (const resolve2 of waiters) resolve2();
|
|
14204
|
+
}
|
|
14205
|
+
};
|
|
14206
|
+
|
|
14207
|
+
// src/kernel/devices.ts
|
|
14208
|
+
var DEVICE_NAME = /^tty(USB|ACM)(\d{1,3})$/;
|
|
14209
|
+
var BY_ID = "/dev/serial/by-id";
|
|
14210
|
+
var LINK_NAME = /^[A-Za-z0-9._:+@=,-]{1,255}$/;
|
|
14211
|
+
var MAJORS = { USB: 188, ACM: 166 };
|
|
14212
|
+
var HIGH_WATER = 64 * 1024;
|
|
14213
|
+
var CLOSING_WAIT_MS = 3e4;
|
|
14214
|
+
var BREAK_MS = 250;
|
|
14215
|
+
var MAX_ID2 = 4294967294;
|
|
14216
|
+
var DEFAULT_SETTINGS = settingsOf(SERIAL_CFLAG, 9600);
|
|
14217
|
+
function refused2(code, text) {
|
|
14218
|
+
return Object.assign(new Error(`${code}: ${text}`), { code });
|
|
14219
|
+
}
|
|
14220
|
+
function invalid(what) {
|
|
14221
|
+
return refused2("EINVAL", what);
|
|
14222
|
+
}
|
|
14223
|
+
function nodePath(path) {
|
|
14224
|
+
if (typeof path !== "string") throw invalid("path must be a string");
|
|
14225
|
+
const name2 = path.startsWith("/dev/") ? path.slice(5) : path;
|
|
14226
|
+
const m = DEVICE_NAME.exec(name2);
|
|
14227
|
+
const minor = Number(m?.[2]);
|
|
14228
|
+
if (!m || minor > 255) throw invalid(`not a serial device name: ${path}`);
|
|
14229
|
+
return { path: `/dev/${name2}`, major: MAJORS[m[1]], minor };
|
|
14230
|
+
}
|
|
14231
|
+
function linkPath(link2) {
|
|
14232
|
+
if (typeof link2 !== "string") throw invalid("a link must be a string");
|
|
14233
|
+
const name2 = link2.startsWith(`${BY_ID}/`) ? link2.slice(BY_ID.length + 1) : link2;
|
|
14234
|
+
if (!LINK_NAME.test(name2) || name2 === "." || name2 === "..")
|
|
14235
|
+
throw invalid(`not a link name: ${link2}`);
|
|
14236
|
+
return `${BY_ID}/${name2}`;
|
|
14237
|
+
}
|
|
14238
|
+
function modeOf(mode) {
|
|
14239
|
+
if (mode === void 0) return 384;
|
|
14240
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 4095)
|
|
14241
|
+
throw invalid("mode must be permission bits");
|
|
14242
|
+
return mode;
|
|
14243
|
+
}
|
|
14244
|
+
async function idOf(value2, find) {
|
|
14245
|
+
if (value2 === void 0) return 0;
|
|
14246
|
+
if (typeof value2 === "number") {
|
|
14247
|
+
if (!Number.isInteger(value2) || value2 < 0 || value2 > MAX_ID2) throw invalid(`bad id ${value2}`);
|
|
14248
|
+
return value2;
|
|
14249
|
+
}
|
|
14250
|
+
const id2 = typeof value2 === "string" ? await find(value2) : void 0;
|
|
14251
|
+
if (id2 === void 0) throw invalid(`unknown owner or group ${String(value2)}`);
|
|
14252
|
+
return id2;
|
|
14253
|
+
}
|
|
14254
|
+
function quiet3() {
|
|
14255
|
+
return void 0;
|
|
14256
|
+
}
|
|
14257
|
+
function want(access2) {
|
|
14258
|
+
return access2 === "read" ? R_OK : access2 === "write" ? W_OK : R_OK | W_OK;
|
|
14259
|
+
}
|
|
14260
|
+
function checkMeta(node, change, uid, gid, guard2) {
|
|
14261
|
+
const { cred } = guard2;
|
|
14262
|
+
const owner = cred.euid === 0 || cred.euid === node.uid;
|
|
14263
|
+
const member = cred.egid === gid || cred.groups.includes(gid);
|
|
14264
|
+
const chown2 = uid !== node.uid || gid !== node.gid;
|
|
14265
|
+
const times = (change.atimeMs ?? change.mtimeMs ?? null) !== null;
|
|
14266
|
+
if (change.mode !== void 0 && !owner) deny(guard2, "chmod", node.path, "EPERM");
|
|
14267
|
+
if (chown2 && cred.euid !== 0 && (uid !== node.uid || !owner || !member))
|
|
14268
|
+
deny(guard2, "chown", node.path, "EPERM");
|
|
14269
|
+
if (times && !owner && !(change.now && permits(cred, node, W_OK, false)))
|
|
14270
|
+
deny(guard2, "utimes", node.path, change.now ? "EACCES" : "EPERM");
|
|
14271
|
+
}
|
|
14272
|
+
var DeviceNodes = class {
|
|
14273
|
+
hooks = /* @__PURE__ */ new Map();
|
|
14274
|
+
nodes = /* @__PURE__ */ new Map();
|
|
14275
|
+
links = /* @__PURE__ */ new Map();
|
|
14276
|
+
lookup;
|
|
14277
|
+
users;
|
|
14278
|
+
next = 0;
|
|
14279
|
+
constructor(users, lookup2) {
|
|
14280
|
+
this.users = users;
|
|
14281
|
+
this.lookup = lookup2;
|
|
14282
|
+
}
|
|
14283
|
+
register(client, hook) {
|
|
14284
|
+
this.hooks.set(client, hook);
|
|
14285
|
+
return () => {
|
|
14286
|
+
if (this.hooks.get(client) !== hook) return;
|
|
14287
|
+
this.hooks.delete(client);
|
|
14288
|
+
for (const node of [...this.nodes.values()]) if (node.client === client) this.drop(node);
|
|
14289
|
+
};
|
|
14290
|
+
}
|
|
14291
|
+
async add(spec, client) {
|
|
14292
|
+
if (typeof spec !== "object" || spec === null) throw invalid("add needs a spec");
|
|
14293
|
+
const { path, major, minor } = nodePath(spec.path);
|
|
14294
|
+
const mode = modeOf(spec.mode);
|
|
14295
|
+
if (spec.links !== void 0 && !Array.isArray(spec.links))
|
|
14296
|
+
throw invalid("links must be a list");
|
|
14297
|
+
const links = (spec.links ?? []).map(linkPath);
|
|
14298
|
+
const uid = await idOf(spec.owner, (name2) => this.lookup.userId(name2));
|
|
14299
|
+
const gid = await idOf(spec.group, (name2) => this.lookup.groupId(name2));
|
|
14300
|
+
if (!this.hooks.has(client)) throw refused2("ENOTSUP", "this client has no devices hook");
|
|
14301
|
+
if (this.nodes.has(path)) throw refused2("EEXIST", `${path} exists`);
|
|
14302
|
+
const taken = links.find((l, i) => this.links.has(l) || links.indexOf(l) !== i);
|
|
14303
|
+
if (taken !== void 0) throw refused2("EEXIST", `${taken} exists`);
|
|
14304
|
+
const node = {
|
|
14305
|
+
id: ++this.next,
|
|
14306
|
+
client,
|
|
14307
|
+
path,
|
|
14308
|
+
uid,
|
|
14309
|
+
gid,
|
|
14310
|
+
mode,
|
|
14311
|
+
major,
|
|
14312
|
+
minor,
|
|
14313
|
+
links,
|
|
14314
|
+
opening: false,
|
|
14315
|
+
atimeMs: Date.now(),
|
|
14316
|
+
mtimeMs: Date.now(),
|
|
14317
|
+
ctimeMs: Date.now()
|
|
14318
|
+
};
|
|
14319
|
+
this.nodes.set(path, node);
|
|
14320
|
+
for (const link2 of links) this.links.set(link2, node);
|
|
14321
|
+
return node.id;
|
|
14322
|
+
}
|
|
14323
|
+
remove(id2) {
|
|
14324
|
+
const node = [...this.nodes.values()].find((n) => n.id === id2);
|
|
14325
|
+
if (!node) return false;
|
|
14326
|
+
this.drop(node);
|
|
14327
|
+
return true;
|
|
14328
|
+
}
|
|
14329
|
+
drop(node) {
|
|
14330
|
+
this.nodes.delete(node.path);
|
|
14331
|
+
for (const link2 of node.links) this.links.delete(link2);
|
|
14332
|
+
node.channel?.lost(new KernelError("EIO", `${node.path} was removed`));
|
|
14333
|
+
}
|
|
14334
|
+
list() {
|
|
14335
|
+
return [...this.nodes.values()].sort((a, b) => a.path < b.path ? -1 : 1).map(({ path, uid, gid, mode, major, minor, links, atimeMs, mtimeMs, ctimeMs }) => ({
|
|
14336
|
+
path,
|
|
14337
|
+
uid,
|
|
14338
|
+
gid,
|
|
14339
|
+
mode,
|
|
14340
|
+
major,
|
|
14341
|
+
minor,
|
|
14342
|
+
links: [...links],
|
|
14343
|
+
atimeMs,
|
|
14344
|
+
mtimeMs,
|
|
14345
|
+
ctimeMs
|
|
14346
|
+
}));
|
|
14347
|
+
}
|
|
14348
|
+
meta(path, change, guard2) {
|
|
14349
|
+
const node = this.find(path);
|
|
14350
|
+
if (!node) throw new KernelError("ENOENT");
|
|
14351
|
+
const uid = change.uid === void 0 || change.uid === -1 ? node.uid : change.uid >>> 0;
|
|
14352
|
+
const gid = change.gid === void 0 || change.gid === -1 ? node.gid : change.gid >>> 0;
|
|
14353
|
+
if (guard2) checkMeta(node, change, uid, gid, guard2);
|
|
14354
|
+
const now = Date.now();
|
|
14355
|
+
if (change.mode !== void 0) node.mode = change.mode & 4095;
|
|
14356
|
+
node.uid = uid;
|
|
14357
|
+
node.gid = gid;
|
|
14358
|
+
const at = (ms, kept) => ms === void 0 || ms === null ? kept : change.now ? now : ms;
|
|
14359
|
+
node.atimeMs = at(change.atimeMs, node.atimeMs);
|
|
14360
|
+
node.mtimeMs = at(change.mtimeMs, node.mtimeMs);
|
|
14361
|
+
node.ctimeMs = now;
|
|
14362
|
+
if (node.channel) node.channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
|
|
14363
|
+
}
|
|
14364
|
+
find(path) {
|
|
14365
|
+
return this.nodes.get(path) ?? this.links.get(path);
|
|
14366
|
+
}
|
|
14367
|
+
openTty(path) {
|
|
14368
|
+
return this.nodes.get(path)?.channel?.tty;
|
|
14369
|
+
}
|
|
14370
|
+
async open(path, flags2, opener) {
|
|
14371
|
+
const node = this.find(path);
|
|
14372
|
+
if (!node) throw new KernelError("ENOENT");
|
|
14373
|
+
const access2 = (flags2 & 3) === 0 ? "read" : (flags2 & 3) === 1 ? "write" : "both";
|
|
14374
|
+
if (opener.guard && !permits(opener.guard.cred, node, want(access2), false))
|
|
14375
|
+
deny(opener.guard, "open", node.path);
|
|
14376
|
+
if (node.channel || node.opening) throw new KernelError("EBUSY");
|
|
14377
|
+
const hook = this.hooks.get(node.client);
|
|
14378
|
+
node.opening = true;
|
|
14379
|
+
const asked = Promise.resolve().then(
|
|
14380
|
+
() => hook.open({ path: node.path, flags: flags2 & 3, stamp: opener.stamp, signal: opener.signal })
|
|
14381
|
+
);
|
|
14382
|
+
let streams;
|
|
14383
|
+
try {
|
|
14384
|
+
streams = await interruptible2(asked, opener.signal);
|
|
14385
|
+
} catch (err) {
|
|
14386
|
+
if (err instanceof KernelError && err.code === "EINTR") {
|
|
14387
|
+
void asked.then(abandon, quiet3).finally(() => node.opening = false);
|
|
14388
|
+
} else node.opening = false;
|
|
14389
|
+
throw embedderFailure(err);
|
|
14390
|
+
}
|
|
14391
|
+
node.opening = false;
|
|
14392
|
+
if (!isStreams(streams)) throw new KernelError("EIO", "the devices hook gave no streams");
|
|
14393
|
+
if (this.nodes.get(node.path) !== node) {
|
|
14394
|
+
void abandon(streams);
|
|
14395
|
+
throw new KernelError("ENOENT");
|
|
14396
|
+
}
|
|
14397
|
+
const channel = new DeviceChannel(node.path, streams, node.saved, (saved) => {
|
|
14398
|
+
node.saved = saved;
|
|
14399
|
+
if (node.channel === channel) delete node.channel;
|
|
14400
|
+
});
|
|
14401
|
+
channel.tty.owner = { uid: node.uid, gid: node.gid, mode: node.mode };
|
|
14402
|
+
node.channel = channel;
|
|
14403
|
+
try {
|
|
14404
|
+
await channel.restore();
|
|
14405
|
+
} catch (err) {
|
|
14406
|
+
await channel.close();
|
|
14407
|
+
throw err;
|
|
14408
|
+
}
|
|
14409
|
+
return channel.file(access2);
|
|
14410
|
+
}
|
|
14411
|
+
};
|
|
14412
|
+
function isStreams(value2) {
|
|
14413
|
+
const s = value2;
|
|
14414
|
+
return !!s && s.readable instanceof ReadableStream && s.writable instanceof WritableStream && typeof s.control === "function";
|
|
14415
|
+
}
|
|
14416
|
+
async function abandon(streams) {
|
|
14417
|
+
if (!isStreams(streams)) return;
|
|
14418
|
+
await streams.readable.cancel("closed").catch(quiet3);
|
|
14419
|
+
await streams.writable.abort("closed").catch(quiet3);
|
|
14420
|
+
await Promise.resolve().then(() => streams.control("close")).catch(quiet3);
|
|
14421
|
+
}
|
|
14422
|
+
function serialTermios() {
|
|
14423
|
+
const base = defaultTermios();
|
|
14424
|
+
return {
|
|
14425
|
+
c_iflag: 0,
|
|
14426
|
+
c_oflag: SERIAL_OFLAG,
|
|
14427
|
+
c_cflag: SERIAL_CFLAG,
|
|
14428
|
+
c_lflag: SERIAL_LFLAG,
|
|
14429
|
+
c_cc: base.c_cc
|
|
14430
|
+
};
|
|
14431
|
+
}
|
|
14432
|
+
function sleep(ms, signal) {
|
|
14433
|
+
return interruptible2(new Promise((resolve2) => setTimeout(resolve2, ms)), signal);
|
|
14434
|
+
}
|
|
14435
|
+
var DeviceChannel = class {
|
|
14436
|
+
tty;
|
|
14437
|
+
path;
|
|
14438
|
+
inner;
|
|
14439
|
+
reader;
|
|
14440
|
+
writer;
|
|
14441
|
+
streams;
|
|
14442
|
+
onClosed;
|
|
14443
|
+
gone = Promise.withResolvers();
|
|
14444
|
+
failure;
|
|
14445
|
+
pending = /* @__PURE__ */ new Set();
|
|
14446
|
+
queued = 0;
|
|
14447
|
+
resume;
|
|
14448
|
+
waiters = [];
|
|
14449
|
+
closed = false;
|
|
14450
|
+
ispeed = 9600;
|
|
14451
|
+
ospeed = 9600;
|
|
14452
|
+
dtr = true;
|
|
14453
|
+
rts = true;
|
|
14454
|
+
constructor(path, streams, saved, onClosed) {
|
|
14455
|
+
this.path = path;
|
|
14456
|
+
this.streams = streams;
|
|
14457
|
+
this.onClosed = onClosed;
|
|
14458
|
+
this.gone.promise.catch(quiet3);
|
|
14459
|
+
this.tty = new KernelTty({ write: (bytes) => this.send(bytes) }, quiet3);
|
|
14460
|
+
this.tty.name = path;
|
|
14461
|
+
this.tty.setSize(0, 0);
|
|
14462
|
+
this.tty.tcsets(saved?.termios ?? serialTermios());
|
|
14463
|
+
if (saved) {
|
|
14464
|
+
this.ispeed = saved.ispeed;
|
|
14465
|
+
this.ospeed = saved.ospeed;
|
|
14466
|
+
this.dtr = this.rts = saved.ospeed !== 0;
|
|
14467
|
+
}
|
|
14468
|
+
this.inner = this.tty.file();
|
|
14469
|
+
this.reader = streams.readable.getReader();
|
|
14470
|
+
this.writer = streams.writable.getWriter();
|
|
14471
|
+
void this.pump();
|
|
14472
|
+
}
|
|
14473
|
+
file(access2) {
|
|
14474
|
+
return new OpenFile({
|
|
14475
|
+
...access2 === "write" ? {} : { read: (max, signal) => this.read(max, signal) },
|
|
14476
|
+
...access2 === "read" ? {} : { write: (bytes, signal) => this.write(bytes, signal) },
|
|
14477
|
+
poll: () => this.poll(),
|
|
14478
|
+
changed: (signal) => this.changed(signal),
|
|
14479
|
+
close: () => this.close(),
|
|
14480
|
+
tty: this.tty,
|
|
14481
|
+
device: this
|
|
14482
|
+
});
|
|
14483
|
+
}
|
|
14484
|
+
lost(err) {
|
|
14485
|
+
if (this.failure) return;
|
|
14486
|
+
this.failure = err;
|
|
14487
|
+
this.gone.reject(err);
|
|
14488
|
+
this.tty.hangup();
|
|
14489
|
+
this.wake();
|
|
14490
|
+
}
|
|
14491
|
+
control(op, args) {
|
|
14492
|
+
return Promise.resolve().then(() => this.streams.control(op, args));
|
|
14493
|
+
}
|
|
14494
|
+
buffered() {
|
|
14495
|
+
return this.inner.file.poll?.().nread ?? 0;
|
|
14496
|
+
}
|
|
14497
|
+
async pump() {
|
|
14498
|
+
for (; ; ) {
|
|
14499
|
+
while (this.buffered() >= HIGH_WATER && !this.closed && !this.failure) {
|
|
14500
|
+
await new Promise((resolve2) => {
|
|
14501
|
+
this.resume = resolve2;
|
|
14502
|
+
});
|
|
14503
|
+
}
|
|
14504
|
+
if (this.closed || this.failure) return;
|
|
14505
|
+
let got;
|
|
14506
|
+
try {
|
|
14507
|
+
got = await this.reader.read();
|
|
14508
|
+
} catch (err) {
|
|
14509
|
+
if (!this.closed) this.lost(new KernelError("EIO", lineOf2(err)));
|
|
14510
|
+
return;
|
|
14511
|
+
}
|
|
14512
|
+
if (got.done) {
|
|
14513
|
+
this.tty.hangup();
|
|
14514
|
+
this.wake();
|
|
14515
|
+
return;
|
|
14516
|
+
}
|
|
14517
|
+
this.tty.receive(got.value);
|
|
14518
|
+
this.wake();
|
|
14519
|
+
}
|
|
14520
|
+
}
|
|
14521
|
+
send(bytes) {
|
|
14522
|
+
if (this.closed || this.failure) return;
|
|
14523
|
+
const n = bytes.length;
|
|
14524
|
+
this.queued += n;
|
|
14525
|
+
const sent = this.writer.write(bytes.slice()).catch((err) => {
|
|
14526
|
+
this.lost(new KernelError("EIO", lineOf2(err)));
|
|
14527
|
+
});
|
|
14528
|
+
const tracked = sent.finally(() => {
|
|
14529
|
+
this.queued -= n;
|
|
14530
|
+
this.pending.delete(tracked);
|
|
14531
|
+
this.wake();
|
|
14532
|
+
});
|
|
14533
|
+
this.pending.add(tracked);
|
|
14534
|
+
}
|
|
14535
|
+
check() {
|
|
14536
|
+
if (this.failure) throw this.failure;
|
|
14537
|
+
}
|
|
14538
|
+
async read(max, signal) {
|
|
14539
|
+
this.check();
|
|
14540
|
+
const read = this.inner.file.read;
|
|
14541
|
+
const got = await read(max, signal);
|
|
14542
|
+
this.resume?.();
|
|
14543
|
+
this.resume = void 0;
|
|
14544
|
+
if (got.length === 0) this.check();
|
|
14545
|
+
return got;
|
|
14546
|
+
}
|
|
14547
|
+
async ready(signal) {
|
|
14548
|
+
this.check();
|
|
14549
|
+
try {
|
|
14550
|
+
await interruptible2(Promise.race([this.writer.ready, this.gone.promise]), signal);
|
|
14551
|
+
} catch (err) {
|
|
14552
|
+
if (err instanceof KernelError) throw err;
|
|
14553
|
+
throw new KernelError("EIO", lineOf2(err));
|
|
14554
|
+
}
|
|
14555
|
+
}
|
|
14556
|
+
async write(bytes, signal) {
|
|
14557
|
+
this.check();
|
|
14558
|
+
if (!((this.writer.desiredSize ?? 0) > 0)) await this.ready(signal);
|
|
14559
|
+
const write = this.inner.file.write;
|
|
14560
|
+
return write(bytes, signal);
|
|
14561
|
+
}
|
|
14562
|
+
poll() {
|
|
14563
|
+
if (this.failure)
|
|
14564
|
+
return { readable: true, writable: true, hangup: true, error: true, nread: 0 };
|
|
14565
|
+
const inner = this.inner.file.poll?.();
|
|
14566
|
+
const writable3 = (this.writer.desiredSize ?? 0) > 0;
|
|
14567
|
+
return { ...inner, writable: writable3 };
|
|
14568
|
+
}
|
|
14569
|
+
changed(signal) {
|
|
14570
|
+
const mine = new Promise((resolve2) => this.waiters.push(resolve2));
|
|
14571
|
+
const theirs = this.inner.file.changed?.(signal);
|
|
14572
|
+
return interruptible2(Promise.race([mine, theirs]), signal);
|
|
14573
|
+
}
|
|
14574
|
+
wake() {
|
|
14575
|
+
const waiters = this.waiters;
|
|
14576
|
+
this.waiters = [];
|
|
14577
|
+
for (const resolve2 of waiters) resolve2();
|
|
14578
|
+
}
|
|
14579
|
+
async drain(signal) {
|
|
14580
|
+
while (this.pending.size > 0) {
|
|
14581
|
+
this.check();
|
|
14582
|
+
await interruptible2(
|
|
14583
|
+
Promise.race([Promise.all([...this.pending]), this.gone.promise]),
|
|
14584
|
+
signal
|
|
14585
|
+
);
|
|
14586
|
+
}
|
|
14587
|
+
this.check();
|
|
14588
|
+
}
|
|
14589
|
+
async restore() {
|
|
14590
|
+
const settings = this.settings();
|
|
14591
|
+
if (settings.baudRate === 0)
|
|
14592
|
+
return this.signals({ dataTerminalReady: false, requestToSend: false });
|
|
14593
|
+
if (sameSettings(settings, DEFAULT_SETTINGS)) return;
|
|
14594
|
+
try {
|
|
14595
|
+
await this.control("configure", settings);
|
|
14596
|
+
} catch (err) {
|
|
14597
|
+
throw embedderFailure(err);
|
|
14598
|
+
}
|
|
14599
|
+
}
|
|
14600
|
+
settings() {
|
|
14601
|
+
return settingsOf(this.tty.tcgets().c_cflag, this.ospeed);
|
|
14602
|
+
}
|
|
14603
|
+
termios() {
|
|
14604
|
+
return { ...this.tty.tcgets(), c_ispeed: this.ispeed, c_ospeed: this.ospeed };
|
|
14605
|
+
}
|
|
14606
|
+
async setTermios(termios, how, signal) {
|
|
14607
|
+
this.check();
|
|
14608
|
+
if (how.drain) await this.drain(signal);
|
|
14609
|
+
const before = this.settings();
|
|
14610
|
+
const old = { termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed };
|
|
14611
|
+
const speeds = speedsOf(termios, this, how.extended === true);
|
|
14612
|
+
this.tty.tcsets(termios, how.flush === true);
|
|
14613
|
+
this.ispeed = speeds.ispeed;
|
|
14614
|
+
this.ospeed = speeds.ospeed;
|
|
14615
|
+
try {
|
|
14616
|
+
if (speeds.ospeed === 0) await this.setModem(this.bits() & ~(TIOCM.DTR | TIOCM.RTS));
|
|
14617
|
+
else {
|
|
14618
|
+
if (!sameSettings(before, this.settings()))
|
|
14619
|
+
await this.control("configure", this.settings());
|
|
14620
|
+
if (old.ospeed === 0) await this.setModem(this.bits() | TIOCM.DTR | TIOCM.RTS);
|
|
14621
|
+
}
|
|
14622
|
+
if (how.flush) await this.control("flush", { input: true, output: false });
|
|
14623
|
+
} catch (err) {
|
|
14624
|
+
this.tty.tcsets(old.termios);
|
|
14625
|
+
this.ispeed = old.ispeed;
|
|
14626
|
+
this.ospeed = old.ospeed;
|
|
14627
|
+
throw embedderFailure(err);
|
|
14628
|
+
}
|
|
14629
|
+
}
|
|
14630
|
+
bits() {
|
|
14631
|
+
return (this.dtr ? TIOCM.DTR : 0) | (this.rts ? TIOCM.RTS : 0);
|
|
14632
|
+
}
|
|
14633
|
+
async modem() {
|
|
14634
|
+
this.check();
|
|
14635
|
+
const s = await this.control("signals").catch((err) => {
|
|
14636
|
+
throw embedderFailure(err);
|
|
14637
|
+
});
|
|
14638
|
+
return this.bits() | (s?.clearToSend ? TIOCM.CTS : 0) | (s?.dataSetReady ? TIOCM.DSR : 0) | (s?.dataCarrierDetect ? TIOCM.CD : 0) | (s?.ringIndicator ? TIOCM.RI : 0);
|
|
14639
|
+
}
|
|
14640
|
+
async setModem(bits2) {
|
|
14641
|
+
this.check();
|
|
14642
|
+
const dataTerminalReady = (bits2 & TIOCM.DTR) !== 0;
|
|
14643
|
+
const requestToSend = (bits2 & TIOCM.RTS) !== 0;
|
|
14644
|
+
await this.signals({ dataTerminalReady, requestToSend });
|
|
14645
|
+
this.dtr = dataTerminalReady;
|
|
14646
|
+
this.rts = requestToSend;
|
|
14647
|
+
}
|
|
14648
|
+
async signals(args) {
|
|
14649
|
+
try {
|
|
14650
|
+
await this.control("setSignals", args);
|
|
14651
|
+
} catch (err) {
|
|
14652
|
+
throw embedderFailure(err);
|
|
14653
|
+
}
|
|
14654
|
+
}
|
|
14655
|
+
async sendBreak(ms, signal) {
|
|
14656
|
+
await this.drain(signal);
|
|
14657
|
+
await this.signals({ break: true });
|
|
14658
|
+
try {
|
|
14659
|
+
await sleep(ms, signal);
|
|
14660
|
+
} finally {
|
|
14661
|
+
await this.signals({ break: false });
|
|
14662
|
+
}
|
|
14663
|
+
}
|
|
14664
|
+
async flush(input, output) {
|
|
14665
|
+
this.check();
|
|
14666
|
+
if (input) this.tty.tcsets(this.tty.tcgets(), true);
|
|
14667
|
+
try {
|
|
14668
|
+
await this.control("flush", { input, output });
|
|
14669
|
+
} catch (err) {
|
|
14670
|
+
throw embedderFailure(err);
|
|
14671
|
+
}
|
|
14672
|
+
}
|
|
14673
|
+
async ioctl(request3, arg, signal) {
|
|
14674
|
+
this.check();
|
|
14675
|
+
switch (request3) {
|
|
14676
|
+
case IOCTL.TCGETS:
|
|
14677
|
+
case IOCTL.TCGETS2:
|
|
14678
|
+
return this.termios();
|
|
14679
|
+
case IOCTL.TCSETS:
|
|
14680
|
+
case IOCTL.TCSETSW:
|
|
14681
|
+
case IOCTL.TCSETSF:
|
|
14682
|
+
case IOCTL.TCSETS2:
|
|
14683
|
+
case IOCTL.TCSETSW2:
|
|
14684
|
+
case IOCTL.TCSETSF2: {
|
|
14685
|
+
const extended = request3 > 65535;
|
|
14686
|
+
const kind = (request3 & 255) - (extended ? 43 : 2);
|
|
14687
|
+
await this.setTermios(
|
|
14688
|
+
arg,
|
|
14689
|
+
{ drain: kind > 0, flush: kind === 2, extended },
|
|
14690
|
+
signal
|
|
14691
|
+
);
|
|
14692
|
+
return null;
|
|
14693
|
+
}
|
|
14694
|
+
case IOCTL.TIOCMGET:
|
|
14695
|
+
return this.modem();
|
|
14696
|
+
case IOCTL.TIOCMSET:
|
|
14697
|
+
await this.setModem(Number(arg));
|
|
14698
|
+
return null;
|
|
14699
|
+
case IOCTL.TIOCMBIS:
|
|
14700
|
+
await this.setModem(this.bits() | Number(arg));
|
|
14701
|
+
return null;
|
|
14702
|
+
case IOCTL.TIOCMBIC:
|
|
14703
|
+
await this.setModem(this.bits() & ~Number(arg));
|
|
14704
|
+
return null;
|
|
14705
|
+
case IOCTL.TIOCSBRK:
|
|
14706
|
+
case IOCTL.TIOCCBRK:
|
|
14707
|
+
await this.signals({ break: request3 === IOCTL.TIOCSBRK });
|
|
14708
|
+
return null;
|
|
14709
|
+
case IOCTL.TCSBRK:
|
|
14710
|
+
if (Number(arg) === 0) await this.sendBreak(BREAK_MS, signal);
|
|
14711
|
+
else await this.drain(signal);
|
|
14712
|
+
return null;
|
|
14713
|
+
case IOCTL.TCSBRKP:
|
|
14714
|
+
await this.sendBreak(Number(arg) > 0 ? Number(arg) * 100 : BREAK_MS, signal);
|
|
14715
|
+
return null;
|
|
14716
|
+
case IOCTL.TCFLSH: {
|
|
14717
|
+
const queue = Number(arg);
|
|
14718
|
+
if (queue < 0 || queue > 2) throw new KernelError("EINVAL");
|
|
14719
|
+
await this.flush(queue !== 1, queue !== 0);
|
|
14720
|
+
return null;
|
|
14721
|
+
}
|
|
14722
|
+
case IOCTL.FIONREAD:
|
|
14723
|
+
return this.buffered();
|
|
14724
|
+
case IOCTL.TIOCOUTQ:
|
|
14725
|
+
return this.queued;
|
|
14726
|
+
case IOCTL.TIOCEXCL:
|
|
14727
|
+
case IOCTL.TIOCNXCL:
|
|
14728
|
+
return null;
|
|
14729
|
+
case IOCTL.TIOCGWINSZ: {
|
|
14730
|
+
const [rows, cols] = this.tty.winsize();
|
|
14731
|
+
return { rows, cols };
|
|
14732
|
+
}
|
|
14733
|
+
case IOCTL.TIOCSWINSZ: {
|
|
14734
|
+
const size = arg;
|
|
14735
|
+
this.tty.setSize(Number(size?.cols) & 65535, Number(size?.rows) & 65535);
|
|
14736
|
+
return null;
|
|
14737
|
+
}
|
|
14738
|
+
case IOCTL.TIOCSCTTY:
|
|
14739
|
+
throw new KernelError("EPERM");
|
|
14740
|
+
default:
|
|
14741
|
+
throw new KernelError("ENOTTY");
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
async close() {
|
|
14745
|
+
if (this.closed) return;
|
|
14746
|
+
try {
|
|
14747
|
+
if (!this.failure) {
|
|
14748
|
+
await interruptible2(this.drain(), AbortSignal.timeout(CLOSING_WAIT_MS)).catch(quiet3);
|
|
14749
|
+
}
|
|
14750
|
+
this.closed = true;
|
|
14751
|
+
this.resume?.();
|
|
14752
|
+
this.wake();
|
|
14753
|
+
await this.reader.cancel("closed").catch(quiet3);
|
|
14754
|
+
this.reader.releaseLock();
|
|
14755
|
+
await this.writer.abort("closed").catch(quiet3);
|
|
14756
|
+
this.writer.releaseLock();
|
|
14757
|
+
await this.control("close").catch(quiet3);
|
|
14758
|
+
} finally {
|
|
14759
|
+
this.closed = true;
|
|
14760
|
+
this.onClosed({ termios: this.tty.tcgets(), ispeed: this.ispeed, ospeed: this.ospeed });
|
|
14761
|
+
}
|
|
14762
|
+
}
|
|
14763
|
+
};
|
|
14764
|
+
|
|
13647
14765
|
// src/kernel/exec-ticket.ts
|
|
13648
14766
|
var TICKET_ENV = "SLICC_SEALED_TICKET";
|
|
13649
14767
|
var TICKET_TTL_MS = 3e4;
|
|
@@ -14016,8 +15134,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
14016
15134
|
} finally {
|
|
14017
15135
|
clearTimeout(timer);
|
|
14018
15136
|
}
|
|
14019
|
-
const
|
|
14020
|
-
if (
|
|
15137
|
+
const refused5 = driverVersionError(hello.protocol);
|
|
15138
|
+
if (refused5) throw errnoError("EPROTO", refused5);
|
|
14021
15139
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
14022
15140
|
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
|
|
14023
15141
|
}
|
|
@@ -14063,7 +15181,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
14063
15181
|
var S_ISGID4 = 1024;
|
|
14064
15182
|
var ID = /^\d{1,10}$/;
|
|
14065
15183
|
var MASK = /^0*[0-7]{1,4}$/;
|
|
14066
|
-
var
|
|
15184
|
+
var MAX_ID3 = 4294967294;
|
|
14067
15185
|
function idOption(value2) {
|
|
14068
15186
|
return value2 === void 0 ? 0 : Number(value2);
|
|
14069
15187
|
}
|
|
@@ -14075,7 +15193,7 @@ function checkIds(options) {
|
|
|
14075
15193
|
for (const key of ["uid", "gid"]) {
|
|
14076
15194
|
const value2 = options[key];
|
|
14077
15195
|
if (value2 === void 0) continue;
|
|
14078
|
-
if (!ID.test(value2) || Number(value2) >
|
|
15196
|
+
if (!ID.test(value2) || Number(value2) > MAX_ID3) throw errnoError("EINVAL", `${key}=${value2}`);
|
|
14079
15197
|
}
|
|
14080
15198
|
}
|
|
14081
15199
|
function checkMasks(options) {
|
|
@@ -14087,7 +15205,7 @@ function checkMasks(options) {
|
|
|
14087
15205
|
}
|
|
14088
15206
|
}
|
|
14089
15207
|
}
|
|
14090
|
-
function
|
|
15208
|
+
function modeOf2(attr2, mount) {
|
|
14091
15209
|
const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
|
|
14092
15210
|
const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
|
|
14093
15211
|
if (mount.caps.owners) return attr2.mode ?? fallback;
|
|
@@ -14248,9 +15366,9 @@ var HeldPaths = class extends Map {
|
|
|
14248
15366
|
this.unlinked.set(path, file);
|
|
14249
15367
|
}
|
|
14250
15368
|
keep(path, file) {
|
|
14251
|
-
const
|
|
14252
|
-
for (const id2 of
|
|
14253
|
-
this.uses.set(file, Math.max(
|
|
15369
|
+
const fresh4 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
15370
|
+
for (const id2 of fresh4) this.keptHandles.set(id2, file);
|
|
15371
|
+
this.uses.set(file, Math.max(fresh4.length, 1));
|
|
14254
15372
|
this.unlinked.set(path, file);
|
|
14255
15373
|
}
|
|
14256
15374
|
unkeep(path, file) {
|
|
@@ -14371,7 +15489,7 @@ function statOf2(attr2, path, mount) {
|
|
|
14371
15489
|
isSymbolicLink: attr2.kind === "symlink",
|
|
14372
15490
|
...attr2.kind === "fifo" ? { isFifo: true } : {},
|
|
14373
15491
|
size: attr2.size,
|
|
14374
|
-
mode:
|
|
15492
|
+
mode: modeOf2(attr2, mount),
|
|
14375
15493
|
mtime,
|
|
14376
15494
|
atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
|
|
14377
15495
|
ctime: mtime,
|
|
@@ -15462,8 +16580,8 @@ var MountFs = class _MountFs {
|
|
|
15462
16580
|
}
|
|
15463
16581
|
this.writable(mount, real);
|
|
15464
16582
|
const { uid, gid, mode } = change;
|
|
15465
|
-
const
|
|
15466
|
-
const set3 =
|
|
16583
|
+
const fresh4 = change.kind === "new" && { uid, gid, mode };
|
|
16584
|
+
const set3 = fresh4 ? await this.driverOwner(found, real, fresh4, false) : change;
|
|
15467
16585
|
this.table.forget(mount, real);
|
|
15468
16586
|
const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
|
|
15469
16587
|
await mount.conn.call({
|
|
@@ -15740,10 +16858,10 @@ async function runMkdir(entry, path, req) {
|
|
|
15740
16858
|
const plain3 = plainDirMode(entry, req.mode);
|
|
15741
16859
|
const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
|
|
15742
16860
|
const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
|
|
15743
|
-
const
|
|
16861
|
+
const fresh4 = plain3 !== void 0 && !await fs.exists(path);
|
|
15744
16862
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
15745
16863
|
await fixParents?.();
|
|
15746
|
-
if (
|
|
16864
|
+
if (fresh4) await fs.chmod(path, plain3);
|
|
15747
16865
|
return done2;
|
|
15748
16866
|
}
|
|
15749
16867
|
async function shownAs(entry, path, st) {
|
|
@@ -15955,10 +17073,10 @@ async function guard(g, path, req, cwd) {
|
|
|
15955
17073
|
}
|
|
15956
17074
|
async function opening(entry, path, mode) {
|
|
15957
17075
|
const g = guardOf(entry);
|
|
15958
|
-
const
|
|
17076
|
+
const want2 = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
|
|
15959
17077
|
const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
|
|
15960
|
-
if (g &&
|
|
15961
|
-
await need(g, "open", path,
|
|
17078
|
+
if (g && want2 !== 0 && !created4) {
|
|
17079
|
+
await need(g, "open", path, want2, { directory: false });
|
|
15962
17080
|
}
|
|
15963
17081
|
}
|
|
15964
17082
|
async function stillThere(entry, path, req) {
|
|
@@ -15982,7 +17100,7 @@ async function holdOp(entry, path, req) {
|
|
|
15982
17100
|
await entry.source?.open(path, real);
|
|
15983
17101
|
await stillThere(entry, path, req);
|
|
15984
17102
|
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
15985
|
-
const fid = req.ino === void 0 ? void 0 : await
|
|
17103
|
+
const fid = req.ino === void 0 ? void 0 : await idOf2(entry, real ?? path);
|
|
15986
17104
|
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
15987
17105
|
return json2(await heldOpen(entry, path, real, req, fid));
|
|
15988
17106
|
}
|
|
@@ -15996,7 +17114,7 @@ async function heldOpen(entry, path, real, req, fid) {
|
|
|
15996
17114
|
await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
|
|
15997
17115
|
return real !== void 0 && real !== path ? { handle, real } : handle;
|
|
15998
17116
|
}
|
|
15999
|
-
async function
|
|
17117
|
+
async function idOf2(entry, path) {
|
|
16000
17118
|
return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
|
|
16001
17119
|
}
|
|
16002
17120
|
async function ownEntry(entry, path, real) {
|
|
@@ -16068,8 +17186,8 @@ async function guarded(entry, path, req) {
|
|
|
16068
17186
|
return { paths };
|
|
16069
17187
|
} catch (err) {
|
|
16070
17188
|
const refusal2 = err.code === "EACCES";
|
|
16071
|
-
const
|
|
16072
|
-
return { refused:
|
|
17189
|
+
const refused5 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
|
|
17190
|
+
return { refused: refused5 };
|
|
16073
17191
|
}
|
|
16074
17192
|
}
|
|
16075
17193
|
async function copyOp(entry, path, req) {
|
|
@@ -16170,7 +17288,7 @@ async function bound(entry, path, handle) {
|
|
|
16170
17288
|
const now = await entry.fs.stat(path).catch(() => void 0);
|
|
16171
17289
|
if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
|
|
16172
17290
|
throw syncError("ENOENT", path);
|
|
16173
|
-
const same3 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await
|
|
17291
|
+
const same3 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf2(entry, path) === bound3.fid;
|
|
16174
17292
|
if (same3) {
|
|
16175
17293
|
entry.trust?.(handle, bound3.doubt);
|
|
16176
17294
|
return;
|
|
@@ -16265,13 +17383,13 @@ async function created2(entry, req) {
|
|
|
16265
17383
|
return seen === void 0 ? looked : json2({ ...looked.json, seen });
|
|
16266
17384
|
}
|
|
16267
17385
|
var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
|
|
16268
|
-
async function rmPermitted(entry, path, req,
|
|
17386
|
+
async function rmPermitted(entry, path, req, refused5) {
|
|
16269
17387
|
const st = await entry.fs.lstat(path).catch(() => void 0);
|
|
16270
|
-
if (!st?.isDirectory) return
|
|
17388
|
+
if (!st?.isDirectory) return refused5;
|
|
16271
17389
|
const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
|
|
16272
17390
|
if ("refused" in unlisted) return unlisted.refused;
|
|
16273
17391
|
const names2 = await entry.fs.readdir(path).catch(() => void 0);
|
|
16274
|
-
if (!names2) return
|
|
17392
|
+
if (!names2) return refused5;
|
|
16275
17393
|
let first;
|
|
16276
17394
|
for (const name2 of names2) {
|
|
16277
17395
|
const child = await dispatchOne(entry, {
|
|
@@ -16322,14 +17440,14 @@ async function newDevName(entry, path, req) {
|
|
|
16322
17440
|
const named = NAMING.has(req.op);
|
|
16323
17441
|
if (!named && !CREATIONS.has(req.op)) return void 0;
|
|
16324
17442
|
const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
|
|
16325
|
-
const
|
|
16326
|
-
if (!
|
|
17443
|
+
const refused5 = devCreation(target, entry.cred?.());
|
|
17444
|
+
if (!refused5) return void 0;
|
|
16327
17445
|
const there = await entry.fs.lstat(target).then(
|
|
16328
17446
|
() => true,
|
|
16329
17447
|
() => false
|
|
16330
17448
|
);
|
|
16331
17449
|
if (there) return void 0;
|
|
16332
|
-
return { ok: false, errno:
|
|
17450
|
+
return { ok: false, errno: refused5, message: `${target}: no new names in /dev or /proc` };
|
|
16333
17451
|
}
|
|
16334
17452
|
var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
|
|
16335
17453
|
async function linkFree(fs, path) {
|
|
@@ -16440,13 +17558,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
|
16440
17558
|
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
16441
17559
|
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
16442
17560
|
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
16443
|
-
var
|
|
17561
|
+
var encoder4 = new TextEncoder();
|
|
16444
17562
|
var decoder2 = new TextDecoder();
|
|
16445
17563
|
function encodeSabResult(result) {
|
|
16446
17564
|
if (!result.ok) {
|
|
16447
17565
|
return {
|
|
16448
17566
|
status: SAB_STATUS_ERRNO,
|
|
16449
|
-
payload:
|
|
17567
|
+
payload: encoder4.encode(
|
|
16450
17568
|
JSON.stringify({
|
|
16451
17569
|
errno: result.errno,
|
|
16452
17570
|
message: result.message,
|
|
@@ -16461,7 +17579,7 @@ function encodeSabResult(result) {
|
|
|
16461
17579
|
case "bytes":
|
|
16462
17580
|
return { status: SAB_STATUS_BYTES, payload: result.bytes };
|
|
16463
17581
|
case "json":
|
|
16464
|
-
return { status: SAB_STATUS_JSON, payload:
|
|
17582
|
+
return { status: SAB_STATUS_JSON, payload: encoder4.encode(JSON.stringify(result.json)) };
|
|
16465
17583
|
default:
|
|
16466
17584
|
return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
|
|
16467
17585
|
}
|
|
@@ -16788,9 +17906,9 @@ var Pages = class {
|
|
|
16788
17906
|
const page = (start2 + at) / PAGE;
|
|
16789
17907
|
if (this.pages.has(page)) continue;
|
|
16790
17908
|
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
16791
|
-
const
|
|
16792
|
-
|
|
16793
|
-
this.pages.set(page,
|
|
17909
|
+
const fresh4 = this.take(part.length);
|
|
17910
|
+
fresh4.set(part);
|
|
17911
|
+
this.pages.set(page, fresh4);
|
|
16794
17912
|
}
|
|
16795
17913
|
}
|
|
16796
17914
|
read(at, length2) {
|
|
@@ -17001,16 +18119,16 @@ var pinnedRoot;
|
|
|
17001
18119
|
var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
|
|
17002
18120
|
function mountTable(mounts = []) {
|
|
17003
18121
|
const root = pinnedRoot ?? rootKind();
|
|
17004
|
-
const
|
|
18122
|
+
const lines3 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
|
|
17005
18123
|
for (const m of mounts) {
|
|
17006
18124
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
17007
18125
|
if (m.state !== "ok") options.push(m.state);
|
|
17008
18126
|
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
17009
|
-
|
|
18127
|
+
lines3.push(
|
|
17010
18128
|
`${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
17011
18129
|
);
|
|
17012
18130
|
}
|
|
17013
|
-
return `${
|
|
18131
|
+
return `${lines3.join("\n")}
|
|
17014
18132
|
`;
|
|
17015
18133
|
}
|
|
17016
18134
|
|
|
@@ -18045,7 +19163,7 @@ function recordRefusal(opening2) {
|
|
|
18045
19163
|
const found = headerClass(opening2);
|
|
18046
19164
|
return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
|
|
18047
19165
|
}
|
|
18048
|
-
function
|
|
19166
|
+
function refused3(engine, session, err, opening2) {
|
|
18049
19167
|
const flags2 = session.verifyResult;
|
|
18050
19168
|
const code = verifyCode(flags2);
|
|
18051
19169
|
if (code && flags2 !== -1)
|
|
@@ -18066,7 +19184,7 @@ async function handshake(conn, o, session, inbox) {
|
|
|
18066
19184
|
done5 = session.handshake();
|
|
18067
19185
|
} catch (err) {
|
|
18068
19186
|
await send(conn, session).catch(() => void 0);
|
|
18069
|
-
throw
|
|
19187
|
+
throw refused3(o.engine, session, err, inbox.opening);
|
|
18070
19188
|
}
|
|
18071
19189
|
await send(conn, session);
|
|
18072
19190
|
if (done5) return;
|
|
@@ -18517,10 +19635,10 @@ async function anchorFor(engine, chain2, host, found) {
|
|
|
18517
19635
|
var FRAGMENT = 16;
|
|
18518
19636
|
var MIN_VALUE = 8;
|
|
18519
19637
|
var MIN_BASE64 = 8;
|
|
18520
|
-
var
|
|
19638
|
+
var encoder5 = new TextEncoder();
|
|
18521
19639
|
var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
18522
19640
|
function utf8(text) {
|
|
18523
|
-
return
|
|
19641
|
+
return encoder5.encode(text);
|
|
18524
19642
|
}
|
|
18525
19643
|
function base64(bytes, padded = true) {
|
|
18526
19644
|
let out = "";
|
|
@@ -18594,14 +19712,73 @@ function encodings(bytes) {
|
|
|
18594
19712
|
}
|
|
18595
19713
|
return [...standard, ...standard.map(urlSafe)];
|
|
18596
19714
|
}
|
|
18597
|
-
function
|
|
19715
|
+
function windows(length2) {
|
|
19716
|
+
const out = [];
|
|
19717
|
+
for (let offset = 0; offset + FRAGMENT <= length2; offset++) out.push(offset);
|
|
19718
|
+
return out;
|
|
19719
|
+
}
|
|
19720
|
+
function strings(document) {
|
|
19721
|
+
const out = [];
|
|
19722
|
+
const stack = [document];
|
|
19723
|
+
while (stack.length > 0) {
|
|
19724
|
+
const item = stack.pop();
|
|
19725
|
+
if (typeof item === "string") out.push(item);
|
|
19726
|
+
if (typeof item !== "object" || item === null) continue;
|
|
19727
|
+
for (const child of Object.values(item)) stack.push(child);
|
|
19728
|
+
}
|
|
19729
|
+
return out;
|
|
19730
|
+
}
|
|
19731
|
+
var DOMAIN = "(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}";
|
|
19732
|
+
var HOST = new RegExp(`^${DOMAIN}$`);
|
|
19733
|
+
var EMAIL = new RegExp(`^[^\\s@]+@${DOMAIN}$`);
|
|
19734
|
+
var URL_SHAPE = new RegExp(`^https?://${DOMAIN}(?::[0-9]{1,5})?((?:/[^/?#\\s]*)*)$`);
|
|
19735
|
+
var ARMOR = /^-----(?:BEGIN|END) [A-Z0-9 ]+-----$/;
|
|
19736
|
+
function unescaped(segment) {
|
|
19737
|
+
try {
|
|
19738
|
+
return decodeURIComponent(segment);
|
|
19739
|
+
} catch {
|
|
19740
|
+
return segment;
|
|
19741
|
+
}
|
|
19742
|
+
}
|
|
19743
|
+
function address(leaf) {
|
|
19744
|
+
if (HOST.test(leaf) || EMAIL.test(leaf)) return true;
|
|
19745
|
+
const url = URL_SHAPE.exec(leaf);
|
|
19746
|
+
if (!url) return false;
|
|
19747
|
+
return url[1].split("/").every((segment) => utf8(segment).length < FRAGMENT || EMAIL.test(unescaped(segment)));
|
|
19748
|
+
}
|
|
19749
|
+
function lines2(leaf) {
|
|
19750
|
+
if (address(leaf)) return [];
|
|
19751
|
+
return leaf.split(/[\r\n"\\]/).filter((line) => utf8(line).length >= FRAGMENT && !ARMOR.test(line));
|
|
19752
|
+
}
|
|
19753
|
+
function parsed2(value2) {
|
|
19754
|
+
try {
|
|
19755
|
+
const document = JSON.parse(value2);
|
|
19756
|
+
return typeof document === "object" && document !== null ? document : null;
|
|
19757
|
+
} catch {
|
|
19758
|
+
return null;
|
|
19759
|
+
}
|
|
19760
|
+
}
|
|
19761
|
+
function fragments(value2) {
|
|
19762
|
+
const raw2 = utf8(value2);
|
|
19763
|
+
const document = parsed2(value2);
|
|
19764
|
+
if (!document) return { pieces: [raw2], whole: true };
|
|
19765
|
+
return { pieces: [...new Set(strings(document).flatMap(lines2))].map(utf8), whole: false };
|
|
19766
|
+
}
|
|
19767
|
+
function shapes(value2, split3 = fragments(value2)) {
|
|
18598
19768
|
const raw2 = utf8(value2);
|
|
18599
19769
|
const seen = /* @__PURE__ */ new Set([value2]);
|
|
18600
19770
|
const out = [];
|
|
18601
|
-
if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
|
|
19771
|
+
if (raw2.length < FRAGMENT || !split3.whole) out.push({ bytes: raw2, form: "raw" });
|
|
19772
|
+
split3.pieces.forEach((bytes, piece) => {
|
|
19773
|
+
for (const offset of windows(bytes.length)) {
|
|
19774
|
+
out.push({
|
|
19775
|
+
bytes: bytes.subarray(offset, offset + FRAGMENT),
|
|
19776
|
+
form: "fragment",
|
|
19777
|
+
offset,
|
|
19778
|
+
piece
|
|
19779
|
+
});
|
|
19780
|
+
}
|
|
19781
|
+
});
|
|
18605
19782
|
const add = (text, form) => {
|
|
18606
19783
|
if (seen.has(text)) return;
|
|
18607
19784
|
seen.add(text);
|
|
@@ -18734,12 +19911,19 @@ function prepare(secrets, sameLength) {
|
|
|
18734
19911
|
const ids = /* @__PURE__ */ new Map();
|
|
18735
19912
|
const wrapIds = /* @__PURE__ */ new Map();
|
|
18736
19913
|
const values = secrets.map((secret) => utf8(secret.value));
|
|
19914
|
+
const splits = [];
|
|
18737
19915
|
secrets.forEach((secret, index) => {
|
|
18738
19916
|
if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
|
|
18739
19917
|
throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
|
|
18740
19918
|
}
|
|
18741
|
-
|
|
18742
|
-
|
|
19919
|
+
splits.push(fragments(secret.value));
|
|
19920
|
+
for (const shape of shapes(secret.value, splits[index])) {
|
|
19921
|
+
const source = {
|
|
19922
|
+
secret: index,
|
|
19923
|
+
form: shape.form,
|
|
19924
|
+
offset: shape.offset,
|
|
19925
|
+
piece: shape.piece
|
|
19926
|
+
};
|
|
18743
19927
|
register(main, sources, ids, shape.bytes, source);
|
|
18744
19928
|
if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
|
|
18745
19929
|
}
|
|
@@ -18750,7 +19934,16 @@ function prepare(secrets, sameLength) {
|
|
|
18750
19934
|
utf8(secret.placeholder),
|
|
18751
19935
|
utf8(`[redacted:${secret.name}]`)
|
|
18752
19936
|
]);
|
|
18753
|
-
return {
|
|
19937
|
+
return {
|
|
19938
|
+
secrets,
|
|
19939
|
+
splits,
|
|
19940
|
+
main,
|
|
19941
|
+
wrap,
|
|
19942
|
+
sources,
|
|
19943
|
+
wrapSources,
|
|
19944
|
+
replacements,
|
|
19945
|
+
sameLength
|
|
19946
|
+
};
|
|
18754
19947
|
}
|
|
18755
19948
|
function filled(input, start2, end) {
|
|
18756
19949
|
const out = new Uint8Array(end - start2);
|
|
@@ -18781,14 +19974,16 @@ function resolve(c, input, start2, end, source) {
|
|
|
18781
19974
|
growing: false
|
|
18782
19975
|
};
|
|
18783
19976
|
}
|
|
18784
|
-
const
|
|
19977
|
+
const split3 = c.splits[source.secret];
|
|
19978
|
+
const value2 = split3.pieces[source.piece];
|
|
18785
19979
|
let stop = end;
|
|
18786
19980
|
let at = source.offset + FRAGMENT;
|
|
18787
19981
|
while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
|
|
18788
19982
|
stop++;
|
|
18789
19983
|
at++;
|
|
18790
19984
|
}
|
|
18791
|
-
const
|
|
19985
|
+
const whole = split3.whole && source.offset === 0 && at === value2.length;
|
|
19986
|
+
const form = whole ? "raw" : "fragment";
|
|
18792
19987
|
const growing = stop === input.length && at < value2.length;
|
|
18793
19988
|
return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
|
|
18794
19989
|
}
|
|
@@ -18942,9 +20137,9 @@ function compile(secrets, options = {}) {
|
|
|
18942
20137
|
// src/kernel/scrub-file.ts
|
|
18943
20138
|
var SCRUB_IDLE_MS = 50;
|
|
18944
20139
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
18945
|
-
var
|
|
20140
|
+
var encoder6 = new TextEncoder();
|
|
18946
20141
|
function scrubberOf(secrets, sameLength = false) {
|
|
18947
|
-
const usable3 = secrets.filter((s) =>
|
|
20142
|
+
const usable3 = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
|
|
18948
20143
|
return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
|
|
18949
20144
|
}
|
|
18950
20145
|
async function writeAll2(file, bytes) {
|
|
@@ -18968,7 +20163,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
18968
20163
|
let timer;
|
|
18969
20164
|
let failed4;
|
|
18970
20165
|
const waiters = /* @__PURE__ */ new Set();
|
|
18971
|
-
const
|
|
20166
|
+
const quiet4 = () => !busy && pending === 0;
|
|
18972
20167
|
const wake = () => {
|
|
18973
20168
|
for (const waiter of [...waiters]) waiter();
|
|
18974
20169
|
};
|
|
@@ -18985,7 +20180,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
18985
20180
|
if (failed4 === void 0) failed4 = err;
|
|
18986
20181
|
}).finally(() => {
|
|
18987
20182
|
pending -= 1;
|
|
18988
|
-
if (
|
|
20183
|
+
if (quiet4()) wake();
|
|
18989
20184
|
});
|
|
18990
20185
|
};
|
|
18991
20186
|
const settling = (signal) => new Promise((resolve2, reject) => {
|
|
@@ -19003,7 +20198,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19003
20198
|
signal?.addEventListener("abort", stop, { once: true });
|
|
19004
20199
|
});
|
|
19005
20200
|
const settled = async () => {
|
|
19006
|
-
while (!
|
|
20201
|
+
while (!quiet4()) await settling();
|
|
19007
20202
|
};
|
|
19008
20203
|
const changed = async (signal) => {
|
|
19009
20204
|
const round = new AbortController();
|
|
@@ -19019,7 +20214,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19019
20214
|
};
|
|
19020
20215
|
const turn = async (signal) => {
|
|
19021
20216
|
for (let waited = false; ; waited = true) {
|
|
19022
|
-
if (
|
|
20217
|
+
if (quiet4() && (pollFile(file).writable || !file.changed)) {
|
|
19023
20218
|
busy = true;
|
|
19024
20219
|
return;
|
|
19025
20220
|
}
|
|
@@ -19048,13 +20243,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19048
20243
|
...bound2(file, "stat"),
|
|
19049
20244
|
poll: () => {
|
|
19050
20245
|
const state = pollFile(file);
|
|
19051
|
-
return
|
|
20246
|
+
return quiet4() ? state : { ...state, writable: false };
|
|
19052
20247
|
},
|
|
19053
20248
|
changed,
|
|
19054
20249
|
...file.tty ? { tty: file.tty } : {},
|
|
19055
20250
|
identity: file.identity ?? file,
|
|
19056
20251
|
...file.clipboard ? { clipboard: file.clipboard } : {},
|
|
19057
20252
|
...file.embedder ? { embedder: file.embedder } : {},
|
|
20253
|
+
...file.device ? { device: file.device } : {},
|
|
19058
20254
|
...file.write ? {
|
|
19059
20255
|
write: async (bytes, signal) => {
|
|
19060
20256
|
await turn(signal);
|
|
@@ -19066,7 +20262,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19066
20262
|
await send2(stream.push(taken), signal);
|
|
19067
20263
|
} finally {
|
|
19068
20264
|
busy = false;
|
|
19069
|
-
if (
|
|
20265
|
+
if (quiet4()) wake();
|
|
19070
20266
|
}
|
|
19071
20267
|
timer = setTimeout(release, idleMs);
|
|
19072
20268
|
return taken.length;
|
|
@@ -19125,9 +20321,9 @@ function scrubStdio(fds, scrubber) {
|
|
|
19125
20321
|
// src/kernel/net/sealed-scrubs.ts
|
|
19126
20322
|
function addTo(tree2, host, secrets) {
|
|
19127
20323
|
const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
|
|
19128
|
-
const
|
|
19129
|
-
if (
|
|
19130
|
-
entry.secrets = [...entry.secrets, ...
|
|
20324
|
+
const fresh4 = secrets.filter((s) => !entry.secrets.some((e) => e.value === s.value));
|
|
20325
|
+
if (fresh4.length === 0) return;
|
|
20326
|
+
entry.secrets = [...entry.secrets, ...fresh4];
|
|
19131
20327
|
entry.version += 1;
|
|
19132
20328
|
entry.scrubber = void 0;
|
|
19133
20329
|
tree2.set(host, entry);
|
|
@@ -19290,7 +20486,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
|
|
|
19290
20486
|
var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
19291
20487
|
var TARGET = /^[\x21-\x7e]+$/;
|
|
19292
20488
|
var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
|
|
19293
|
-
var
|
|
20489
|
+
var HOST2 = /^[a-z0-9.:-]+$/;
|
|
19294
20490
|
var UNSAFE = /[\r\n\0]/;
|
|
19295
20491
|
var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
|
|
19296
20492
|
function verifiedByOk(f) {
|
|
@@ -19344,7 +20540,7 @@ function scramOk(f) {
|
|
|
19344
20540
|
return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
|
|
19345
20541
|
}
|
|
19346
20542
|
function hostOk(host) {
|
|
19347
|
-
return typeof host === "string" &&
|
|
20543
|
+
return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
|
|
19348
20544
|
}
|
|
19349
20545
|
function sessionAskable(f, now) {
|
|
19350
20546
|
if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
|
|
@@ -19434,20 +20630,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
|
|
|
19434
20630
|
"caller",
|
|
19435
20631
|
"requester"
|
|
19436
20632
|
]);
|
|
19437
|
-
var
|
|
20633
|
+
var encoder7 = new TextEncoder();
|
|
19438
20634
|
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
19439
20635
|
function parseSealed(bytes) {
|
|
19440
20636
|
try {
|
|
19441
20637
|
if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
|
|
19442
|
-
let
|
|
20638
|
+
let parsed3;
|
|
19443
20639
|
try {
|
|
19444
|
-
|
|
20640
|
+
parsed3 = JSON.parse(decoder3.decode(bytes));
|
|
19445
20641
|
} catch {
|
|
19446
20642
|
throw new KernelError("EINVAL");
|
|
19447
20643
|
}
|
|
19448
|
-
if (typeof
|
|
20644
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
19449
20645
|
throw new KernelError("EINVAL");
|
|
19450
|
-
const req =
|
|
20646
|
+
const req = parsed3;
|
|
19451
20647
|
if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
|
|
19452
20648
|
throw new KernelError("EINVAL");
|
|
19453
20649
|
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
@@ -19543,7 +20739,7 @@ var SealedChannel = class {
|
|
|
19543
20739
|
await this.broker.tickets.record(req, reply, caller.pid);
|
|
19544
20740
|
if (this.inflight !== flight) return;
|
|
19545
20741
|
this.inflight = void 0;
|
|
19546
|
-
if (reply) this.reply =
|
|
20742
|
+
if (reply) this.reply = encoder7.encode(serialized(reply));
|
|
19547
20743
|
});
|
|
19548
20744
|
this.inflight = flight;
|
|
19549
20745
|
return length2;
|
|
@@ -19819,7 +21015,7 @@ function shown(text) {
|
|
|
19819
21015
|
const chars = [...safe];
|
|
19820
21016
|
return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
|
|
19821
21017
|
}
|
|
19822
|
-
function
|
|
21018
|
+
function unescaped2(double) {
|
|
19823
21019
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
19824
21020
|
}
|
|
19825
21021
|
function splitWords(text) {
|
|
@@ -19835,7 +21031,7 @@ function splitWords(text) {
|
|
|
19835
21031
|
word = void 0;
|
|
19836
21032
|
continue;
|
|
19837
21033
|
}
|
|
19838
|
-
word = (word ?? "") + (single ?? (double !== void 0 ?
|
|
21034
|
+
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
|
|
19839
21035
|
}
|
|
19840
21036
|
if (word !== void 0) words3.push(word);
|
|
19841
21037
|
return words3;
|
|
@@ -19872,7 +21068,7 @@ function splitPatterns(text) {
|
|
|
19872
21068
|
}
|
|
19873
21069
|
word ??= [];
|
|
19874
21070
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
19875
|
-
else addPart(word, single ?? (double !== void 0 ?
|
|
21071
|
+
else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
|
|
19876
21072
|
}
|
|
19877
21073
|
if (word !== void 0) words3.push(word);
|
|
19878
21074
|
return words3;
|
|
@@ -19923,13 +21119,13 @@ function commandMatches(cmnd, argv) {
|
|
|
19923
21119
|
if (cmnd === "*") return true;
|
|
19924
21120
|
const [path, ...args] = argv;
|
|
19925
21121
|
if (path !== cmnd.path) return false;
|
|
19926
|
-
const
|
|
19927
|
-
if (
|
|
21122
|
+
const want2 = cmnd.args;
|
|
21123
|
+
if (want2 === void 0) return true;
|
|
19928
21124
|
return globMatch(
|
|
19929
|
-
|
|
21125
|
+
want2.length,
|
|
19930
21126
|
args.length,
|
|
19931
|
-
(i) => isAny(
|
|
19932
|
-
(i, j) => wordMatch(
|
|
21127
|
+
(i) => isAny(want2[i]),
|
|
21128
|
+
(i, j) => wordMatch(want2[i], args[j])
|
|
19933
21129
|
);
|
|
19934
21130
|
}
|
|
19935
21131
|
function commandText(cmnd) {
|
|
@@ -20226,11 +21422,11 @@ function parsePolicy(text, file) {
|
|
|
20226
21422
|
const defaults2 = [];
|
|
20227
21423
|
const errors = [];
|
|
20228
21424
|
for (const [n, line] of text.split("\n").entries()) {
|
|
20229
|
-
const
|
|
20230
|
-
if (
|
|
20231
|
-
if ("error" in
|
|
20232
|
-
else if ("defaults" in
|
|
20233
|
-
else rules.push(...
|
|
21425
|
+
const parsed3 = parseLine(line, file);
|
|
21426
|
+
if (parsed3 === null) continue;
|
|
21427
|
+
if ("error" in parsed3) errors.push({ line: n + 1, message: parsed3.error });
|
|
21428
|
+
else if ("defaults" in parsed3) defaults2.push(parsed3.defaults);
|
|
21429
|
+
else rules.push(...parsed3.rules);
|
|
20234
21430
|
}
|
|
20235
21431
|
return { rules, defaults: defaults2, errors };
|
|
20236
21432
|
}
|
|
@@ -20283,11 +21479,11 @@ var REPLAY_TAGS_MAX = 64;
|
|
|
20283
21479
|
var REPLAY_ARGV_MAX = 4096;
|
|
20284
21480
|
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
20285
21481
|
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
20286
|
-
function
|
|
21482
|
+
function strings2(value2, max) {
|
|
20287
21483
|
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
20288
21484
|
}
|
|
20289
21485
|
function words(value2) {
|
|
20290
|
-
if (!
|
|
21486
|
+
if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
20291
21487
|
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
20292
21488
|
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
20293
21489
|
}
|
|
@@ -20322,7 +21518,7 @@ function replayOf(value2) {
|
|
|
20322
21518
|
const bound3 = filesOf(r.userWritable);
|
|
20323
21519
|
const named = (s) => typeof s === "string" && s !== "";
|
|
20324
21520
|
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
20325
|
-
const valid = 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 ||
|
|
21521
|
+
const valid = 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;
|
|
20326
21522
|
if (!valid) return failed2("EINVAL", NOT_A_REQUEST);
|
|
20327
21523
|
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
20328
21524
|
return {
|
|
@@ -20414,28 +21610,28 @@ function recordedCommand(argv) {
|
|
|
20414
21610
|
const [path = "", ...args] = argv;
|
|
20415
21611
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
20416
21612
|
}
|
|
20417
|
-
function flags(
|
|
21613
|
+
function flags(parsed3, arg, next3) {
|
|
20418
21614
|
for (let j = 1; j < arg.length; j++) {
|
|
20419
21615
|
const flag = arg[j];
|
|
20420
|
-
if (flag === "n")
|
|
20421
|
-
else if (flag === "l")
|
|
20422
|
-
else if (flag === "v")
|
|
20423
|
-
else if (flag === "k")
|
|
20424
|
-
else if (flag === "K")
|
|
21616
|
+
if (flag === "n") parsed3.nonInteractive = true;
|
|
21617
|
+
else if (flag === "l") parsed3.list = true;
|
|
21618
|
+
else if (flag === "v") parsed3.validate = true;
|
|
21619
|
+
else if (flag === "k") parsed3.reset = true;
|
|
21620
|
+
else if (flag === "K") parsed3.removeAll = true;
|
|
20425
21621
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
20426
21622
|
${USAGE}`;
|
|
20427
21623
|
else {
|
|
20428
21624
|
const user = arg.slice(j + 1) || next3;
|
|
20429
21625
|
if (!user) return `sudo: option requires an argument -- 'u'
|
|
20430
21626
|
${USAGE}`;
|
|
20431
|
-
|
|
21627
|
+
parsed3.user = user;
|
|
20432
21628
|
return arg.slice(j + 1) ? 0 : 1;
|
|
20433
21629
|
}
|
|
20434
21630
|
}
|
|
20435
21631
|
return 0;
|
|
20436
21632
|
}
|
|
20437
21633
|
function parseArgs(args) {
|
|
20438
|
-
const
|
|
21634
|
+
const parsed3 = {
|
|
20439
21635
|
list: false,
|
|
20440
21636
|
nonInteractive: false,
|
|
20441
21637
|
validate: false,
|
|
@@ -20452,19 +21648,19 @@ function parseArgs(args) {
|
|
|
20452
21648
|
break;
|
|
20453
21649
|
}
|
|
20454
21650
|
if (!arg.startsWith("-") || arg === "-") break;
|
|
20455
|
-
const used2 = flags(
|
|
21651
|
+
const used2 = flags(parsed3, arg, args[i + 1]);
|
|
20456
21652
|
if (typeof used2 === "string") return used2;
|
|
20457
21653
|
i += used2;
|
|
20458
21654
|
}
|
|
20459
|
-
|
|
20460
|
-
return usable(
|
|
21655
|
+
parsed3.command = args.slice(i);
|
|
21656
|
+
return usable(parsed3) ? parsed3 : USAGE;
|
|
20461
21657
|
}
|
|
20462
|
-
function usable(
|
|
20463
|
-
const command =
|
|
20464
|
-
const modes = [
|
|
20465
|
-
if (modes > 1 ||
|
|
21658
|
+
function usable(parsed3) {
|
|
21659
|
+
const command = parsed3.command.length > 0;
|
|
21660
|
+
const modes = [parsed3.list, parsed3.validate, parsed3.removeAll].filter(Boolean).length;
|
|
21661
|
+
if (modes > 1 || parsed3.removeAll && parsed3.reset) return false;
|
|
20466
21662
|
if (modes === 1) return !command;
|
|
20467
|
-
return command ||
|
|
21663
|
+
return command || parsed3.reset;
|
|
20468
21664
|
}
|
|
20469
21665
|
function policyOf(settings) {
|
|
20470
21666
|
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
@@ -20608,28 +21804,28 @@ var Sudo = class {
|
|
|
20608
21804
|
};
|
|
20609
21805
|
}
|
|
20610
21806
|
async check(cred, call) {
|
|
20611
|
-
const
|
|
21807
|
+
const lines3 = [];
|
|
20612
21808
|
let ok4 = true;
|
|
20613
21809
|
const report = ({ path, problems, ok: read }) => {
|
|
20614
21810
|
for (const problem of problems) {
|
|
20615
21811
|
const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
|
|
20616
|
-
|
|
21812
|
+
lines3.push([2, `visudo: ${where} ${problem.message}`]);
|
|
20617
21813
|
}
|
|
20618
|
-
if (read && problems.length === 0)
|
|
21814
|
+
if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
|
|
20619
21815
|
if (problems.length > 0) ok4 = false;
|
|
20620
21816
|
};
|
|
20621
21817
|
const values = (defaults2) => {
|
|
20622
|
-
for (const line of defaultsText(defaults2))
|
|
21818
|
+
for (const line of defaultsText(defaults2)) lines3.push([1, line]);
|
|
20623
21819
|
};
|
|
20624
21820
|
if (call.files !== void 0) {
|
|
20625
21821
|
const defaults2 = [];
|
|
20626
21822
|
for (const { name: name2, text } of call.files) {
|
|
20627
|
-
const
|
|
20628
|
-
defaults2.push(...
|
|
20629
|
-
report({ path: name2, rules: [], problems:
|
|
21823
|
+
const parsed3 = parsePolicy(text);
|
|
21824
|
+
defaults2.push(...parsed3.defaults);
|
|
21825
|
+
report({ path: name2, rules: [], problems: parsed3.errors, ok: true });
|
|
20630
21826
|
}
|
|
20631
21827
|
if (defaults2.length > 0) values(defaults2);
|
|
20632
|
-
return { lines:
|
|
21828
|
+
return { lines: lines3, ok: ok4 };
|
|
20633
21829
|
}
|
|
20634
21830
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
20635
21831
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
@@ -20638,7 +21834,7 @@ var Sudo = class {
|
|
|
20638
21834
|
const files = await this.policy();
|
|
20639
21835
|
for (const file of files) report(file);
|
|
20640
21836
|
values(files.flatMap((f) => f.defaults ?? []));
|
|
20641
|
-
return { lines:
|
|
21837
|
+
return { lines: lines3, ok: ok4 };
|
|
20642
21838
|
}
|
|
20643
21839
|
async groupIds() {
|
|
20644
21840
|
return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
|
|
@@ -20703,13 +21899,13 @@ var Sudo = class {
|
|
|
20703
21899
|
return void 0;
|
|
20704
21900
|
}
|
|
20705
21901
|
async request(caller, call, signal) {
|
|
20706
|
-
const
|
|
20707
|
-
if (typeof
|
|
21902
|
+
const parsed3 = parseArgs(call.args);
|
|
21903
|
+
if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
|
|
20708
21904
|
if (this.o.mode !== "enforce") {
|
|
20709
21905
|
const mode = this.o.mode;
|
|
20710
|
-
if (
|
|
21906
|
+
if (parsed3.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
20711
21907
|
` };
|
|
20712
|
-
return
|
|
21908
|
+
return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
|
|
20713
21909
|
}
|
|
20714
21910
|
const { cred } = caller;
|
|
20715
21911
|
const policy = await this.policyRules(caller);
|
|
@@ -20722,22 +21918,22 @@ var Sudo = class {
|
|
|
20722
21918
|
};
|
|
20723
21919
|
const applies = await this.applies(self, cred);
|
|
20724
21920
|
const settings = resolveDefaults(policy.defaults, applies);
|
|
20725
|
-
if (
|
|
21921
|
+
if (parsed3.removeAll) {
|
|
20726
21922
|
this.timestamps.remove(owner.uid);
|
|
20727
21923
|
return DONE;
|
|
20728
21924
|
}
|
|
20729
|
-
if (
|
|
21925
|
+
if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
|
|
20730
21926
|
this.timestamps.invalidate(owner, settings.timestampType);
|
|
20731
21927
|
return DONE;
|
|
20732
21928
|
}
|
|
20733
|
-
const target = await this.lookup(
|
|
20734
|
-
if (!target) return failed2("ENOENT", `sudo: unknown user ${
|
|
21929
|
+
const target = await this.lookup(parsed3.user);
|
|
21930
|
+
if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed3.user}`);
|
|
20735
21931
|
const root = cred.euid === 0;
|
|
20736
21932
|
const rules = root ? [] : policy.rules;
|
|
20737
21933
|
const mine = rules.filter((rule) => applies(rule.who));
|
|
20738
|
-
if (
|
|
20739
|
-
const session = { caller, self, target, owner, policy: policyOf(settings), parsed:
|
|
20740
|
-
if (
|
|
21934
|
+
if (parsed3.list) return await this.listed(self, root, mine, settings.matching);
|
|
21935
|
+
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed3, signal };
|
|
21936
|
+
if (parsed3.validate) return this.validate(session, root, mine, call.cwd);
|
|
20741
21937
|
return this.command(session, root, mine, call);
|
|
20742
21938
|
}
|
|
20743
21939
|
async replay(value2) {
|
|
@@ -20796,9 +21992,9 @@ var Sudo = class {
|
|
|
20796
21992
|
);
|
|
20797
21993
|
}
|
|
20798
21994
|
async command(s, root, mine, call) {
|
|
20799
|
-
const { caller, self, target, parsed:
|
|
21995
|
+
const { caller, self, target, parsed: parsed3 } = s;
|
|
20800
21996
|
const { cred } = caller;
|
|
20801
|
-
const [word = "", ...args] =
|
|
21997
|
+
const [word = "", ...args] = parsed3.command;
|
|
20802
21998
|
const cwd = await this.canonical(call.cwd);
|
|
20803
21999
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
20804
22000
|
return failed2(
|
|
@@ -20813,7 +22009,7 @@ var Sudo = class {
|
|
|
20813
22009
|
const matching = mine.filter(
|
|
20814
22010
|
(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)
|
|
20815
22011
|
);
|
|
20816
|
-
const grant = () => this.grant(self, target, cred,
|
|
22012
|
+
const grant = () => this.grant(self, target, cred, parsed3.command, path, cwd, call.term, bound3);
|
|
20817
22013
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
20818
22014
|
const quoted = argv.map(quoteWord).join(" ");
|
|
20819
22015
|
if (matching.length === 0) {
|
|
@@ -20822,38 +22018,38 @@ var Sudo = class {
|
|
|
20822
22018
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
20823
22019
|
);
|
|
20824
22020
|
}
|
|
20825
|
-
if (!
|
|
22021
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
|
|
20826
22022
|
this.timestamps.stamp(s.owner, s.policy, bound3);
|
|
20827
22023
|
this.logged(s, cwd, argv);
|
|
20828
22024
|
return grant();
|
|
20829
22025
|
}
|
|
20830
|
-
if (
|
|
22026
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20831
22027
|
return this.pendingFor(cred.ruid, async () => {
|
|
20832
|
-
const answer2 = await this.ask(s, { argv:
|
|
22028
|
+
const answer2 = await this.ask(s, { argv: parsed3.command, path, cwd, bound: bound3 });
|
|
20833
22029
|
if ("error" in answer2) return answer2;
|
|
20834
22030
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
20835
22031
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
20836
22032
|
}
|
|
20837
22033
|
const now = await this.bound(path, cwd);
|
|
20838
22034
|
if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20839
|
-
const shown2 = recordable(cwd, [...
|
|
22035
|
+
const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
|
|
20840
22036
|
const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
|
|
20841
22037
|
if (!("error" in result)) this.stamped(s, answer2, bound3);
|
|
20842
22038
|
return result;
|
|
20843
22039
|
});
|
|
20844
22040
|
}
|
|
20845
22041
|
async validate(s, root, mine, dir) {
|
|
20846
|
-
const { self, parsed:
|
|
22042
|
+
const { self, parsed: parsed3 } = s;
|
|
20847
22043
|
if (root) return DONE;
|
|
20848
22044
|
if (mine.length === 0) {
|
|
20849
22045
|
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
20850
22046
|
}
|
|
20851
22047
|
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
20852
|
-
if (!
|
|
22048
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
20853
22049
|
this.timestamps.stamp(s.owner, s.policy);
|
|
20854
22050
|
return DONE;
|
|
20855
22051
|
}
|
|
20856
|
-
if (
|
|
22052
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20857
22053
|
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
20858
22054
|
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
20859
22055
|
const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
|
|
@@ -20908,7 +22104,7 @@ var Sudo = class {
|
|
|
20908
22104
|
}
|
|
20909
22105
|
const shown2 = [];
|
|
20910
22106
|
for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
|
|
20911
|
-
const
|
|
22107
|
+
const lines3 = shown2.join("\n");
|
|
20912
22108
|
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
20913
22109
|
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
20914
22110
|
${params}
|
|
@@ -20916,7 +22112,7 @@ var Sudo = class {
|
|
|
20916
22112
|
`;
|
|
20917
22113
|
return {
|
|
20918
22114
|
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
20919
|
-
${
|
|
22115
|
+
${lines3}
|
|
20920
22116
|
`
|
|
20921
22117
|
};
|
|
20922
22118
|
}
|
|
@@ -21333,7 +22529,7 @@ async function epollSyscall(req, proc) {
|
|
|
21333
22529
|
|
|
21334
22530
|
// src/kernel/locks.ts
|
|
21335
22531
|
var overlaps = (a, b) => a.start < b.end && b.start < a.end;
|
|
21336
|
-
var clashes = (lock, owner,
|
|
22532
|
+
var clashes = (lock, owner, want2) => lock.owner !== owner && overlaps(lock, want2) && (lock.kind === "write" || want2.kind === "write");
|
|
21337
22533
|
function lockRange(start2, len) {
|
|
21338
22534
|
if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
|
|
21339
22535
|
const from = len < 0 ? start2 + len : start2;
|
|
@@ -21419,8 +22615,8 @@ var LockTable = class {
|
|
|
21419
22615
|
blocked = /* @__PURE__ */ new Map();
|
|
21420
22616
|
wakers = /* @__PURE__ */ new Map();
|
|
21421
22617
|
held = /* @__PURE__ */ new Map();
|
|
21422
|
-
holder(key, owner,
|
|
21423
|
-
return this.records.get(key)?.find((lock) => clashes(lock, owner,
|
|
22618
|
+
holder(key, owner, want2) {
|
|
22619
|
+
return this.records.get(key)?.find((lock) => clashes(lock, owner, want2));
|
|
21424
22620
|
}
|
|
21425
22621
|
set(key, owner, kind, start2, end) {
|
|
21426
22622
|
if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
|
|
@@ -21439,10 +22635,10 @@ var LockTable = class {
|
|
|
21439
22635
|
return true;
|
|
21440
22636
|
}
|
|
21441
22637
|
async wait(key, owner, kind, range2, blocking) {
|
|
21442
|
-
const
|
|
22638
|
+
const want2 = { key, kind, ...range2 };
|
|
21443
22639
|
let signal;
|
|
21444
22640
|
for (; ; ) {
|
|
21445
|
-
const holder = this.holder(key, owner,
|
|
22641
|
+
const holder = this.holder(key, owner, want2);
|
|
21446
22642
|
if (!holder) {
|
|
21447
22643
|
this.set(key, owner, kind, range2.start, range2.end);
|
|
21448
22644
|
return;
|
|
@@ -21450,7 +22646,7 @@ var LockTable = class {
|
|
|
21450
22646
|
if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
|
|
21451
22647
|
signal ??= blocking();
|
|
21452
22648
|
if (signal.aborted) throw new KernelError("EINTR");
|
|
21453
|
-
this.blocked.set(owner,
|
|
22649
|
+
this.blocked.set(owner, want2);
|
|
21454
22650
|
try {
|
|
21455
22651
|
await this.changed(key, signal);
|
|
21456
22652
|
} finally {
|
|
@@ -21462,9 +22658,9 @@ var LockTable = class {
|
|
|
21462
22658
|
const seen = /* @__PURE__ */ new Set();
|
|
21463
22659
|
for (let at = from; at !== void 0 && !seen.has(at); ) {
|
|
21464
22660
|
seen.add(at);
|
|
21465
|
-
const
|
|
21466
|
-
if (!
|
|
21467
|
-
const holder = this.holder(
|
|
22661
|
+
const want2 = this.blocked.get(at);
|
|
22662
|
+
if (!want2) return false;
|
|
22663
|
+
const holder = this.holder(want2.key, at, want2);
|
|
21468
22664
|
if (!holder) return false;
|
|
21469
22665
|
if (holder.owner === target) return true;
|
|
21470
22666
|
at = holder.owner;
|
|
@@ -21829,24 +23025,24 @@ function errno2(code, message3) {
|
|
|
21829
23025
|
}
|
|
21830
23026
|
function requestHead(method, url, headers, extra, first = []) {
|
|
21831
23027
|
if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
|
|
21832
|
-
const
|
|
21833
|
-
for (const [name2, value2] of first)
|
|
23028
|
+
const lines3 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
|
|
23029
|
+
for (const [name2, value2] of first) lines3.push(`${name2}: ${value2}`);
|
|
21834
23030
|
for (const [name2, value2] of headers) {
|
|
21835
23031
|
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
21836
23032
|
if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
|
|
21837
|
-
|
|
23033
|
+
lines3.push(`${name2}: ${value2}`);
|
|
21838
23034
|
}
|
|
21839
|
-
for (const [name2, value2] of extra)
|
|
21840
|
-
return latin1Bytes(`${
|
|
23035
|
+
for (const [name2, value2] of extra) lines3.push(`${name2}: ${value2}`);
|
|
23036
|
+
return latin1Bytes(`${lines3.join("\r\n")}\r
|
|
21841
23037
|
\r
|
|
21842
23038
|
`);
|
|
21843
23039
|
}
|
|
21844
23040
|
function parseHead(bytes) {
|
|
21845
|
-
const [statusLine = "", ...
|
|
23041
|
+
const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
|
|
21846
23042
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
21847
23043
|
if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
21848
23044
|
const headers = [];
|
|
21849
|
-
for (const line of
|
|
23045
|
+
for (const line of lines3) {
|
|
21850
23046
|
const colon = line.indexOf(":");
|
|
21851
23047
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
21852
23048
|
}
|
|
@@ -21936,8 +23132,8 @@ var KernelLoopback = class {
|
|
|
21936
23132
|
);
|
|
21937
23133
|
} catch (err) {
|
|
21938
23134
|
socket.close();
|
|
21939
|
-
const
|
|
21940
|
-
throw
|
|
23135
|
+
const refused5 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
23136
|
+
throw refused5 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
21941
23137
|
}
|
|
21942
23138
|
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
21943
23139
|
return socket;
|
|
@@ -22326,9 +23522,9 @@ function pem(der2) {
|
|
|
22326
23522
|
for (let i = 0; i < der2.length; i += 32768) {
|
|
22327
23523
|
b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
|
|
22328
23524
|
}
|
|
22329
|
-
const
|
|
23525
|
+
const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
|
|
22330
23526
|
return `-----BEGIN CERTIFICATE-----
|
|
22331
|
-
${
|
|
23527
|
+
${lines3.join("\n")}
|
|
22332
23528
|
-----END CERTIFICATE-----
|
|
22333
23529
|
`;
|
|
22334
23530
|
}
|
|
@@ -23112,13 +24308,13 @@ var HOSTS_PATH = "/etc/hosts";
|
|
|
23112
24308
|
function parseHosts(text) {
|
|
23113
24309
|
const table = /* @__PURE__ */ new Map();
|
|
23114
24310
|
for (const raw2 of text.split("\n")) {
|
|
23115
|
-
const [
|
|
23116
|
-
if (!
|
|
23117
|
-
if (ipv4Bytes(
|
|
24311
|
+
const [address2, ...names2] = raw2.replace(/#.*/, "").trim().split(/\s+/);
|
|
24312
|
+
if (!address2 || names2.length === 0) continue;
|
|
24313
|
+
if (ipv4Bytes(address2) === void 0 && ipv6Groups(address2) === void 0) continue;
|
|
23118
24314
|
for (const name2 of names2) {
|
|
23119
24315
|
const key = name2.toLowerCase().replace(/\.$/, "");
|
|
23120
24316
|
const known = table.get(key) ?? [];
|
|
23121
|
-
if (!known.includes(
|
|
24317
|
+
if (!known.includes(address2)) known.push(address2);
|
|
23122
24318
|
table.set(key, known);
|
|
23123
24319
|
}
|
|
23124
24320
|
}
|
|
@@ -23318,14 +24514,14 @@ var Resolver = class {
|
|
|
23318
24514
|
if (hostsEntry(key, this.own) !== void 0) return true;
|
|
23319
24515
|
return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
|
|
23320
24516
|
}
|
|
23321
|
-
async reverse(
|
|
23322
|
-
const v6 = ipv6Groups(
|
|
23323
|
-
const same3 = (a) => a ===
|
|
24517
|
+
async reverse(address2) {
|
|
24518
|
+
const v6 = ipv6Groups(address2)?.join(":");
|
|
24519
|
+
const same3 = (a) => a === address2 || v6 !== void 0 && ipv6Groups(a)?.join(":") === v6;
|
|
23324
24520
|
const file = await this.hosts?.().catch(() => void 0);
|
|
23325
24521
|
for (const table of [file, this.builtin]) {
|
|
23326
24522
|
for (const [name2, addresses] of table ?? []) if (addresses.some(same3)) return name2;
|
|
23327
24523
|
}
|
|
23328
|
-
if (ipv4Bytes(
|
|
24524
|
+
if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
|
|
23329
24525
|
return void 0;
|
|
23330
24526
|
}
|
|
23331
24527
|
async resolve(name2, family, blocking) {
|
|
@@ -23379,13 +24575,13 @@ var Resolver = class {
|
|
|
23379
24575
|
if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
|
|
23380
24576
|
return addresses;
|
|
23381
24577
|
}
|
|
23382
|
-
usable(name2,
|
|
23383
|
-
const v4 = ipv4Bytes(
|
|
23384
|
-
if (!v4 && (family === 4 || !ipv6Groups(
|
|
24578
|
+
usable(name2, address2, family) {
|
|
24579
|
+
const v4 = ipv4Bytes(address2) !== void 0;
|
|
24580
|
+
if (!v4 && (family === 4 || !ipv6Groups(address2))) return false;
|
|
23385
24581
|
if (v4 && family === 6) return false;
|
|
23386
|
-
const kind = this.routes.classify(
|
|
24582
|
+
const kind = this.routes.classify(address2);
|
|
23387
24583
|
if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
|
|
23388
|
-
this.log(`slicc-kernel: dropped the uplink's answer ${
|
|
24584
|
+
this.log(`slicc-kernel: dropped the uplink's answer ${address2} for ${name2}: it is ${kind}`);
|
|
23389
24585
|
return false;
|
|
23390
24586
|
}
|
|
23391
24587
|
remember(id2, addresses, ttl) {
|
|
@@ -23419,275 +24615,6 @@ function waitFor(lookup2, signal) {
|
|
|
23419
24615
|
});
|
|
23420
24616
|
}
|
|
23421
24617
|
|
|
23422
|
-
// src/kernel/tty.ts
|
|
23423
|
-
var ICRNL = 256;
|
|
23424
|
-
var IUTF8 = 16384;
|
|
23425
|
-
var OPOST = 1;
|
|
23426
|
-
var ONLCR = 4;
|
|
23427
|
-
var ISIG = 1;
|
|
23428
|
-
var ICANON = 2;
|
|
23429
|
-
var ECHO = 8;
|
|
23430
|
-
var ECHOE = 16;
|
|
23431
|
-
var ECHOCTL = 512;
|
|
23432
|
-
var NOFLSH = 128;
|
|
23433
|
-
var IEXTEN = 32768;
|
|
23434
|
-
var VINTR = 0;
|
|
23435
|
-
var VQUIT = 1;
|
|
23436
|
-
var VERASE = 2;
|
|
23437
|
-
var VKILL = 3;
|
|
23438
|
-
var VEOF = 4;
|
|
23439
|
-
var VSUSP = 10;
|
|
23440
|
-
var VWERASE = 14;
|
|
23441
|
-
function defaultTermios() {
|
|
23442
|
-
const cCc = new Array(32).fill(0);
|
|
23443
|
-
Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
|
|
23444
|
-
Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
|
|
23445
|
-
Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
|
|
23446
|
-
return {
|
|
23447
|
-
c_iflag: ICRNL | 1024 | 8192 | IUTF8,
|
|
23448
|
-
c_oflag: OPOST | ONLCR,
|
|
23449
|
-
c_cflag: 15 | 48 | 128,
|
|
23450
|
-
c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
|
|
23451
|
-
c_cc: cCc
|
|
23452
|
-
};
|
|
23453
|
-
}
|
|
23454
|
-
var encoder7 = new TextEncoder();
|
|
23455
|
-
function isContinuation(byte) {
|
|
23456
|
-
return (byte & 192) === 128;
|
|
23457
|
-
}
|
|
23458
|
-
var KernelTty = class {
|
|
23459
|
-
termios = defaultTermios();
|
|
23460
|
-
cols = 80;
|
|
23461
|
-
rows = 24;
|
|
23462
|
-
readable = [];
|
|
23463
|
-
line = [];
|
|
23464
|
-
waiters = [];
|
|
23465
|
-
hungUp = false;
|
|
23466
|
-
name;
|
|
23467
|
-
owner;
|
|
23468
|
-
onRead;
|
|
23469
|
-
readers = 0;
|
|
23470
|
-
screen;
|
|
23471
|
-
signal;
|
|
23472
|
-
constructor(screen, signal) {
|
|
23473
|
-
this.screen = screen;
|
|
23474
|
-
this.signal = signal;
|
|
23475
|
-
}
|
|
23476
|
-
receive(bytes) {
|
|
23477
|
-
for (const byte of bytes) this.key(byte);
|
|
23478
|
-
this.wake();
|
|
23479
|
-
}
|
|
23480
|
-
resize(cols, rows) {
|
|
23481
|
-
if (cols === this.cols && rows === this.rows) return;
|
|
23482
|
-
this.setSize(cols, rows);
|
|
23483
|
-
this.signal(SIG.WINCH);
|
|
23484
|
-
}
|
|
23485
|
-
hangup() {
|
|
23486
|
-
this.hungUp = true;
|
|
23487
|
-
this.wake();
|
|
23488
|
-
}
|
|
23489
|
-
signalHangup() {
|
|
23490
|
-
this.signal(SIG.HUP);
|
|
23491
|
-
}
|
|
23492
|
-
setSize(cols, rows) {
|
|
23493
|
-
this.cols = cols;
|
|
23494
|
-
this.rows = rows;
|
|
23495
|
-
}
|
|
23496
|
-
tcgets() {
|
|
23497
|
-
return { ...this.termios, c_cc: [...this.termios.c_cc] };
|
|
23498
|
-
}
|
|
23499
|
-
tcsets(termios, flush = false) {
|
|
23500
|
-
const wasCanonical = this.canonical;
|
|
23501
|
-
this.termios = { ...termios, c_cc: [...termios.c_cc] };
|
|
23502
|
-
if (flush) {
|
|
23503
|
-
this.line = [];
|
|
23504
|
-
this.readable = [];
|
|
23505
|
-
}
|
|
23506
|
-
if (wasCanonical && !this.canonical && this.line.length > 0) {
|
|
23507
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
23508
|
-
this.line = [];
|
|
23509
|
-
this.wake();
|
|
23510
|
-
}
|
|
23511
|
-
}
|
|
23512
|
-
winsize() {
|
|
23513
|
-
return [this.rows, this.cols];
|
|
23514
|
-
}
|
|
23515
|
-
file() {
|
|
23516
|
-
return new OpenFile({
|
|
23517
|
-
read: (max, signal) => this.read(max, signal),
|
|
23518
|
-
write: async (bytes) => {
|
|
23519
|
-
if (this.hungUp) throw new KernelError("EIO");
|
|
23520
|
-
this.output(bytes);
|
|
23521
|
-
return bytes.length;
|
|
23522
|
-
},
|
|
23523
|
-
poll: () => ({
|
|
23524
|
-
readable: this.readable.length > 0 || this.hungUp,
|
|
23525
|
-
writable: true,
|
|
23526
|
-
hangup: this.hungUp,
|
|
23527
|
-
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
23528
|
-
}),
|
|
23529
|
-
changed: (signal) => this.changed(signal),
|
|
23530
|
-
close: () => {
|
|
23531
|
-
},
|
|
23532
|
-
tty: this
|
|
23533
|
-
});
|
|
23534
|
-
}
|
|
23535
|
-
get canonical() {
|
|
23536
|
-
return (this.termios.c_lflag & ICANON) !== 0;
|
|
23537
|
-
}
|
|
23538
|
-
cc(slot2) {
|
|
23539
|
-
return this.termios.c_cc[slot2] ?? 0;
|
|
23540
|
-
}
|
|
23541
|
-
key(input) {
|
|
23542
|
-
const { c_iflag, c_lflag } = this.termios;
|
|
23543
|
-
const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
|
|
23544
|
-
if (c_lflag & ISIG && this.signalKey(byte)) return;
|
|
23545
|
-
if (!this.canonical) {
|
|
23546
|
-
this.readable.push(Uint8Array.of(byte));
|
|
23547
|
-
if (c_lflag & ECHO) this.echo(byte);
|
|
23548
|
-
return;
|
|
23549
|
-
}
|
|
23550
|
-
this.edit(byte);
|
|
23551
|
-
}
|
|
23552
|
-
signalKey(byte) {
|
|
23553
|
-
const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
|
|
23554
|
-
if (!sig || byte === 0) return false;
|
|
23555
|
-
if (this.termios.c_lflag & ECHO) this.echo(byte);
|
|
23556
|
-
if (!(this.termios.c_lflag & NOFLSH)) {
|
|
23557
|
-
this.line = [];
|
|
23558
|
-
this.readable = [];
|
|
23559
|
-
}
|
|
23560
|
-
this.signal(sig);
|
|
23561
|
-
return true;
|
|
23562
|
-
}
|
|
23563
|
-
edit(byte) {
|
|
23564
|
-
const echo = (this.termios.c_lflag & ECHO) !== 0;
|
|
23565
|
-
if (byte === this.cc(VERASE) || byte === 8) {
|
|
23566
|
-
this.rubOut(this.lastCharLength());
|
|
23567
|
-
} else if (byte === this.cc(VKILL)) {
|
|
23568
|
-
this.rubOut(this.line.length);
|
|
23569
|
-
} else if (byte === this.cc(VWERASE)) {
|
|
23570
|
-
this.rubOut(this.lastWordLength());
|
|
23571
|
-
} else if (byte === this.cc(VEOF)) {
|
|
23572
|
-
this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
|
|
23573
|
-
this.line = [];
|
|
23574
|
-
} else if (byte === 10) {
|
|
23575
|
-
this.line.push(byte);
|
|
23576
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
23577
|
-
this.line = [];
|
|
23578
|
-
if (echo) this.output(encoder7.encode("\n"));
|
|
23579
|
-
} else {
|
|
23580
|
-
this.line.push(byte);
|
|
23581
|
-
if (echo) this.echo(byte);
|
|
23582
|
-
}
|
|
23583
|
-
}
|
|
23584
|
-
lastWordLength() {
|
|
23585
|
-
let end = this.line.length;
|
|
23586
|
-
while (end > 0 && this.line[end - 1] === 32) end--;
|
|
23587
|
-
while (end > 0 && this.line[end - 1] !== 32) end--;
|
|
23588
|
-
return this.line.length - end;
|
|
23589
|
-
}
|
|
23590
|
-
lastCharLength() {
|
|
23591
|
-
const { line } = this;
|
|
23592
|
-
let n = Math.min(1, line.length);
|
|
23593
|
-
if (this.termios.c_iflag & IUTF8) {
|
|
23594
|
-
while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
|
|
23595
|
-
}
|
|
23596
|
-
return n;
|
|
23597
|
-
}
|
|
23598
|
-
rubOut(n) {
|
|
23599
|
-
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
23600
|
-
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
23601
|
-
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
23602
|
-
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
23603
|
-
this.output(encoder7.encode("\b \b".repeat(cells)), false);
|
|
23604
|
-
}
|
|
23605
|
-
}
|
|
23606
|
-
echo(byte) {
|
|
23607
|
-
if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
|
|
23608
|
-
this.output(Uint8Array.of(94, byte + 64), false);
|
|
23609
|
-
return;
|
|
23610
|
-
}
|
|
23611
|
-
this.output(Uint8Array.of(byte));
|
|
23612
|
-
}
|
|
23613
|
-
output(bytes, translate2 = true) {
|
|
23614
|
-
const { c_oflag } = this.termios;
|
|
23615
|
-
if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
|
|
23616
|
-
this.screen.write(bytes);
|
|
23617
|
-
return;
|
|
23618
|
-
}
|
|
23619
|
-
const out = [];
|
|
23620
|
-
for (const byte of bytes) {
|
|
23621
|
-
if (byte === 10) out.push(13);
|
|
23622
|
-
out.push(byte);
|
|
23623
|
-
}
|
|
23624
|
-
this.screen.write(Uint8Array.from(out));
|
|
23625
|
-
}
|
|
23626
|
-
get reading() {
|
|
23627
|
-
return this.readers > 0;
|
|
23628
|
-
}
|
|
23629
|
-
async read(max, signal) {
|
|
23630
|
-
this.onRead?.();
|
|
23631
|
-
this.readers++;
|
|
23632
|
-
try {
|
|
23633
|
-
return await this.take(max, signal);
|
|
23634
|
-
} finally {
|
|
23635
|
-
this.readers--;
|
|
23636
|
-
}
|
|
23637
|
-
}
|
|
23638
|
-
async take(max, signal) {
|
|
23639
|
-
while (this.readable.length === 0) {
|
|
23640
|
-
if (this.hungUp) return new Uint8Array(0);
|
|
23641
|
-
await this.changed(signal);
|
|
23642
|
-
}
|
|
23643
|
-
const head = this.readable[0];
|
|
23644
|
-
if (head === null) {
|
|
23645
|
-
this.readable.shift();
|
|
23646
|
-
return new Uint8Array(0);
|
|
23647
|
-
}
|
|
23648
|
-
if (head === void 0) return new Uint8Array(0);
|
|
23649
|
-
const chunks = [];
|
|
23650
|
-
let n = 0;
|
|
23651
|
-
while (this.readable.length > 0 && n < max) {
|
|
23652
|
-
const next3 = this.readable[0];
|
|
23653
|
-
if (next3 === null || next3 === void 0) break;
|
|
23654
|
-
const take = next3.subarray(0, max - n);
|
|
23655
|
-
chunks.push(take);
|
|
23656
|
-
n += take.length;
|
|
23657
|
-
if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
|
|
23658
|
-
else this.readable.shift();
|
|
23659
|
-
if (this.canonical) break;
|
|
23660
|
-
}
|
|
23661
|
-
const out = new Uint8Array(n);
|
|
23662
|
-
let at = 0;
|
|
23663
|
-
for (const chunk2 of chunks) {
|
|
23664
|
-
out.set(chunk2, at);
|
|
23665
|
-
at += chunk2.length;
|
|
23666
|
-
}
|
|
23667
|
-
return out;
|
|
23668
|
-
}
|
|
23669
|
-
changed(signal) {
|
|
23670
|
-
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
23671
|
-
return new Promise((resolve2, reject) => {
|
|
23672
|
-
const onAbort = () => {
|
|
23673
|
-
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
23674
|
-
reject(new KernelError("EINTR"));
|
|
23675
|
-
};
|
|
23676
|
-
const waiter = () => {
|
|
23677
|
-
signal?.removeEventListener("abort", onAbort);
|
|
23678
|
-
resolve2();
|
|
23679
|
-
};
|
|
23680
|
-
this.waiters.push(waiter);
|
|
23681
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
23682
|
-
});
|
|
23683
|
-
}
|
|
23684
|
-
wake() {
|
|
23685
|
-
const waiters = this.waiters;
|
|
23686
|
-
this.waiters = [];
|
|
23687
|
-
for (const resolve2 of waiters) resolve2();
|
|
23688
|
-
}
|
|
23689
|
-
};
|
|
23690
|
-
|
|
23691
24618
|
// src/kernel/pty.ts
|
|
23692
24619
|
var PtyPair = class {
|
|
23693
24620
|
slave;
|
|
@@ -23880,6 +24807,7 @@ function ptySyscall(req, ctx) {
|
|
|
23880
24807
|
return done4;
|
|
23881
24808
|
case "pty-ctty": {
|
|
23882
24809
|
const tty = terminalOf(ctx, req.fd);
|
|
24810
|
+
if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
|
|
23883
24811
|
const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
|
|
23884
24812
|
if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done4;
|
|
23885
24813
|
if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
|
|
@@ -24661,10 +25589,10 @@ function basicOf(value2) {
|
|
|
24661
25589
|
function placeholdersIn(text) {
|
|
24662
25590
|
return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
|
|
24663
25591
|
}
|
|
24664
|
-
function scan(
|
|
25592
|
+
function scan(lines3) {
|
|
24665
25593
|
const found = [];
|
|
24666
|
-
for (let i = 1; i <
|
|
24667
|
-
const m = HEADER_LINE.exec(
|
|
25594
|
+
for (let i = 1; i < lines3.length; i++) {
|
|
25595
|
+
const m = HEADER_LINE.exec(lines3[i]);
|
|
24668
25596
|
if (!m) return void 0;
|
|
24669
25597
|
const value2 = m[2];
|
|
24670
25598
|
const basic = basicOf(value2);
|
|
@@ -24687,10 +25615,10 @@ function usable2(values) {
|
|
|
24687
25615
|
}
|
|
24688
25616
|
async function substituteHead(head, ask) {
|
|
24689
25617
|
if (!head.endsWith("\r\n\r\n")) return void 0;
|
|
24690
|
-
const
|
|
24691
|
-
if (
|
|
24692
|
-
const request3 = REQUEST_LINE2.exec(
|
|
24693
|
-
const found = request3 ? scan(
|
|
25618
|
+
const lines3 = head.slice(0, -4).split("\r\n");
|
|
25619
|
+
if (lines3.some((l) => l.includes("\n") || l.includes("\r"))) return void 0;
|
|
25620
|
+
const request3 = REQUEST_LINE2.exec(lines3[0]);
|
|
25621
|
+
const found = request3 ? scan(lines3) : void 0;
|
|
24694
25622
|
if (!request3 || !found || found.length !== 1) return void 0;
|
|
24695
25623
|
const at = found[0];
|
|
24696
25624
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
@@ -24702,8 +25630,8 @@ async function substituteHead(head, ask) {
|
|
|
24702
25630
|
);
|
|
24703
25631
|
if (Object.keys(values).length === 0) return void 0;
|
|
24704
25632
|
const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
|
|
24705
|
-
const out = [...
|
|
24706
|
-
out[at.line] = `${
|
|
25633
|
+
const out = [...lines3];
|
|
25634
|
+
out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
|
|
24707
25635
|
return { head: `${out.join("\r\n")}\r
|
|
24708
25636
|
\r
|
|
24709
25637
|
`, values };
|
|
@@ -25026,9 +25954,9 @@ function serverHeader(header4, server) {
|
|
|
25026
25954
|
return server.plus && server.binding ? END_POINT : "n,,";
|
|
25027
25955
|
}
|
|
25028
25956
|
function rewritten(first, header4) {
|
|
25029
|
-
const
|
|
25030
|
-
if (!
|
|
25031
|
-
return { mechanism: header4 === END_POINT ? PLUS : PLAIN3, data: `${header4}${
|
|
25957
|
+
const parsed3 = clientFirstOf(first.data);
|
|
25958
|
+
if (!parsed3 || parsed3.header === header4) return first;
|
|
25959
|
+
return { mechanism: header4 === END_POINT ? PLUS : PLAIN3, data: `${header4}${parsed3.bare}` };
|
|
25032
25960
|
}
|
|
25033
25961
|
var PgClient = class {
|
|
25034
25962
|
clientFirst;
|
|
@@ -25048,10 +25976,10 @@ var PgClient = class {
|
|
|
25048
25976
|
}
|
|
25049
25977
|
if (server.sasl && this.clientFirst === void 0) {
|
|
25050
25978
|
const first = initial(message3);
|
|
25051
|
-
const
|
|
25052
|
-
if (!first || !
|
|
25979
|
+
const parsed3 = first && clientFirstOf(first.data);
|
|
25980
|
+
if (!first || !parsed3) return { bytes: message3, done: true };
|
|
25053
25981
|
this.clientFirst = first.data;
|
|
25054
|
-
this.header = serverHeader(
|
|
25982
|
+
this.header = serverHeader(parsed3.header, server);
|
|
25055
25983
|
const sent = rewritten(first, this.header);
|
|
25056
25984
|
if (sent === first) return { bytes: message3, done: false };
|
|
25057
25985
|
return {
|
|
@@ -25216,7 +26144,7 @@ function headEnd2(bytes) {
|
|
|
25216
26144
|
}
|
|
25217
26145
|
return -1;
|
|
25218
26146
|
}
|
|
25219
|
-
function
|
|
26147
|
+
function modeOf3(start2) {
|
|
25220
26148
|
const sniffs = [
|
|
25221
26149
|
["http1", sniffHttp1(start2)],
|
|
25222
26150
|
["smtp", sniffSmtp(start2)],
|
|
@@ -25325,7 +26253,7 @@ var SessionWriter = class {
|
|
|
25325
26253
|
joined5.set(bytes, this.buffer.length);
|
|
25326
26254
|
this.buffer.fill(0);
|
|
25327
26255
|
this.buffer = joined5;
|
|
25328
|
-
if (this.mode === "sniff") this.mode =
|
|
26256
|
+
if (this.mode === "sniff") this.mode = modeOf3(latin12(joined5.subarray(0, 8))) ?? "sniff";
|
|
25329
26257
|
if (this.mode === "pg" && !this.sawPg) {
|
|
25330
26258
|
this.sawPg = true;
|
|
25331
26259
|
if (!this.s.verified) void this.proof("pg");
|
|
@@ -25812,12 +26740,12 @@ function secure(prepared, seal) {
|
|
|
25812
26740
|
prepared.own?.release();
|
|
25813
26741
|
}
|
|
25814
26742
|
};
|
|
25815
|
-
const
|
|
26743
|
+
const abandon2 = () => {
|
|
25816
26744
|
if (ran) return;
|
|
25817
26745
|
prepared.options.session.free();
|
|
25818
26746
|
prepared.own?.release();
|
|
25819
26747
|
};
|
|
25820
|
-
return { make, abandon };
|
|
26748
|
+
return { make, abandon: abandon2 };
|
|
25821
26749
|
}
|
|
25822
26750
|
function supportOf(net) {
|
|
25823
26751
|
const support = net.tlsClient;
|
|
@@ -25834,19 +26762,19 @@ async function uplinkAddress(proc, host) {
|
|
|
25834
26762
|
async function connectSecure(proc, socket, host, port, req) {
|
|
25835
26763
|
const servername = serverName(req.servername || host);
|
|
25836
26764
|
if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
|
|
25837
|
-
const
|
|
25838
|
-
const route2 = proc.net.uplinkFor(
|
|
26765
|
+
const address2 = await uplinkAddress(proc, host);
|
|
26766
|
+
const route2 = proc.net.uplinkFor(address2);
|
|
25839
26767
|
if (!route2)
|
|
25840
26768
|
throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
|
|
25841
26769
|
const support = supportOf(proc.net);
|
|
25842
26770
|
let info;
|
|
25843
|
-
const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
|
|
26771
|
+
const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
|
|
25844
26772
|
try {
|
|
25845
|
-
await socket.connectSecure({ family: "inet", host:
|
|
26773
|
+
await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
|
|
25846
26774
|
info = await conn.upgrade(make);
|
|
25847
26775
|
});
|
|
25848
26776
|
} finally {
|
|
25849
|
-
|
|
26777
|
+
abandon2();
|
|
25850
26778
|
}
|
|
25851
26779
|
return sessionOf(info, servername);
|
|
25852
26780
|
}
|
|
@@ -25879,14 +26807,14 @@ async function upgradeSecure(proc, socket, req) {
|
|
|
25879
26807
|
const host = req.servername || (peer?.family === "inet" ? peer.host : "");
|
|
25880
26808
|
const servername = serverName(host);
|
|
25881
26809
|
const port = peer?.family === "inet" ? peer.port : 0;
|
|
25882
|
-
const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
26810
|
+
const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
25883
26811
|
proc,
|
|
25884
26812
|
port
|
|
25885
26813
|
});
|
|
25886
26814
|
try {
|
|
25887
26815
|
return sessionOf(await socket.upgradeSecure(make), servername);
|
|
25888
26816
|
} finally {
|
|
25889
|
-
|
|
26817
|
+
abandon2();
|
|
25890
26818
|
}
|
|
25891
26819
|
}
|
|
25892
26820
|
|
|
@@ -27810,6 +28738,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
27810
28738
|
"fd-open-clipboard",
|
|
27811
28739
|
"fd-clipboard-abort",
|
|
27812
28740
|
"fd-open-embedder",
|
|
28741
|
+
"fd-dev-nodes",
|
|
28742
|
+
"fd-open-dev",
|
|
28743
|
+
"fd-dev-ioctl",
|
|
28744
|
+
"fd-dev-meta",
|
|
27813
28745
|
"tty-get",
|
|
27814
28746
|
"tty-set",
|
|
27815
28747
|
"tty-winsz",
|
|
@@ -28186,7 +29118,7 @@ var WasmProcess = class {
|
|
|
28186
29118
|
async dispatch(req, cancelled) {
|
|
28187
29119
|
try {
|
|
28188
29120
|
if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
|
|
28189
|
-
if (isTtySyscall(req)) return this.ttySyscall(req);
|
|
29121
|
+
if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
|
|
28190
29122
|
if (isJobSyscall(req)) return this.jobSyscall(req);
|
|
28191
29123
|
if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
|
|
28192
29124
|
if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
|
|
@@ -28250,7 +29182,7 @@ var WasmProcess = class {
|
|
|
28250
29182
|
return owner;
|
|
28251
29183
|
}
|
|
28252
29184
|
pathKeys = /* @__PURE__ */ new Map();
|
|
28253
|
-
async lockPath(target,
|
|
29185
|
+
async lockPath(target, want2) {
|
|
28254
29186
|
if (target.fd !== void 0) {
|
|
28255
29187
|
const st = await this.fds.get(target.fd).file.stat?.();
|
|
28256
29188
|
if (!st) throw new KernelError("EBADF");
|
|
@@ -28260,9 +29192,9 @@ var WasmProcess = class {
|
|
|
28260
29192
|
if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
|
|
28261
29193
|
const path = target.path;
|
|
28262
29194
|
const g = this.guarded();
|
|
28263
|
-
const checked = `${
|
|
28264
|
-
if (g &&
|
|
28265
|
-
await openable(g, "lock", path,
|
|
29195
|
+
const checked = `${want2}:${path}`;
|
|
29196
|
+
if (g && want2 !== 0 && !this.lockChecked.has(checked)) {
|
|
29197
|
+
await openable(g, "lock", path, want2, false);
|
|
28266
29198
|
this.lockChecked.add(checked);
|
|
28267
29199
|
}
|
|
28268
29200
|
return path;
|
|
@@ -28275,8 +29207,8 @@ var WasmProcess = class {
|
|
|
28275
29207
|
const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
|
|
28276
29208
|
if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
|
|
28277
29209
|
if (access2 && !access2.stat) return objectKey(access2);
|
|
28278
|
-
const
|
|
28279
|
-
const path = await this.lockPath(target,
|
|
29210
|
+
const want2 = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
|
|
29211
|
+
const path = await this.lockPath(target, want2);
|
|
28280
29212
|
if (known !== void 0) return known;
|
|
28281
29213
|
const st = await this.options.fs.stat?.(path).catch((err) => {
|
|
28282
29214
|
if (!file) throw err;
|
|
@@ -28362,7 +29294,7 @@ var WasmProcess = class {
|
|
|
28362
29294
|
const file = this.fds.get(req.fd).file;
|
|
28363
29295
|
const read = req.peek ? file.peek : file.read;
|
|
28364
29296
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
28365
|
-
if (file.tty) this.checkForeground(file.tty);
|
|
29297
|
+
if (file.tty && !file.device) this.checkForeground(file.tty);
|
|
28366
29298
|
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
28367
29299
|
if (file.embedder) return this.readEmbedder(file, req, cancelled);
|
|
28368
29300
|
if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
|
|
@@ -28459,6 +29391,46 @@ var WasmProcess = class {
|
|
|
28459
29391
|
throw err;
|
|
28460
29392
|
});
|
|
28461
29393
|
}
|
|
29394
|
+
async openDev(path, flags2, cancelled) {
|
|
29395
|
+
const devices = this.options.devices;
|
|
29396
|
+
if (!devices) throw new KernelError("ENOENT");
|
|
29397
|
+
const shown2 = await this.options.identity?.();
|
|
29398
|
+
const tags = this.options.embedder?.tagsOf(this.pid);
|
|
29399
|
+
const stamp = {
|
|
29400
|
+
pid: shown2?.pid ?? this.pid,
|
|
29401
|
+
uid: this.cred.euid,
|
|
29402
|
+
gid: this.cred.egid,
|
|
29403
|
+
groups: [...this.cred.groups],
|
|
29404
|
+
users: devices.users,
|
|
29405
|
+
...tags?.length ? { tag: tags[0], tags: [...tags] } : {}
|
|
29406
|
+
};
|
|
29407
|
+
const guard2 = this.guarded();
|
|
29408
|
+
const file = await devices.open(path, flags2, {
|
|
29409
|
+
stamp,
|
|
29410
|
+
...guard2 ? { guard: guard2 } : {},
|
|
29411
|
+
signal: this.blockingSignal(cancelled)
|
|
29412
|
+
});
|
|
29413
|
+
return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
|
|
29414
|
+
}
|
|
29415
|
+
devMeta(path, change) {
|
|
29416
|
+
const devices = this.options.devices;
|
|
29417
|
+
if (!devices) throw new KernelError("ENOENT");
|
|
29418
|
+
devices.meta(path, change, this.guarded());
|
|
29419
|
+
return { ok: true, kind: "void" };
|
|
29420
|
+
}
|
|
29421
|
+
async devIoctl(req, cancelled) {
|
|
29422
|
+
const file = this.fds.get(req.fd).file;
|
|
29423
|
+
if (!file.device) {
|
|
29424
|
+
if (req.request >>> 0 === 21531) return pollFile(file).nread ?? 0;
|
|
29425
|
+
throw new KernelError("ENOTTY");
|
|
29426
|
+
}
|
|
29427
|
+
const result = await file.device.ioctl(
|
|
29428
|
+
req.request >>> 0,
|
|
29429
|
+
req.arg,
|
|
29430
|
+
this.blockingSignal(cancelled)
|
|
29431
|
+
);
|
|
29432
|
+
return result ?? null;
|
|
29433
|
+
}
|
|
28462
29434
|
async openEmbedder(flags2) {
|
|
28463
29435
|
const broker = this.options.embedder;
|
|
28464
29436
|
if (!broker) throw new KernelError("ENOENT");
|
|
@@ -28546,11 +29518,11 @@ var WasmProcess = class {
|
|
|
28546
29518
|
} else if (req.create) {
|
|
28547
29519
|
await nodes.onEntry(path, async () => {
|
|
28548
29520
|
const marks = this.options.owners || plain3 !== void 0;
|
|
28549
|
-
const
|
|
29521
|
+
const fresh4 = marks && !await present3(fs, path, true);
|
|
28550
29522
|
this.fds.installAt(fd, this.openVfsFile(open2, await nodes.identify(path), fs));
|
|
28551
29523
|
await this.fds.get(fd).file.stat?.();
|
|
28552
|
-
if (
|
|
28553
|
-
else if (
|
|
29524
|
+
if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
|
|
29525
|
+
else if (fresh4) {
|
|
28554
29526
|
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
28555
29527
|
}
|
|
28556
29528
|
});
|
|
@@ -28649,8 +29621,8 @@ var WasmProcess = class {
|
|
|
28649
29621
|
if (req.exclusive) return inParent(g, "open", req.path);
|
|
28650
29622
|
if (req.created && guard2.created?.take(req.path)) return;
|
|
28651
29623
|
const access2 = req.flags & WRITE_ACCESS;
|
|
28652
|
-
const
|
|
28653
|
-
await openable(g, "open", req.path,
|
|
29624
|
+
const want2 = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
|
|
29625
|
+
await openable(g, "open", req.path, want2, req.create === true);
|
|
28654
29626
|
}
|
|
28655
29627
|
openTty(name2) {
|
|
28656
29628
|
const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
|
|
@@ -28714,7 +29686,7 @@ var WasmProcess = class {
|
|
|
28714
29686
|
}
|
|
28715
29687
|
terminalByName(name2) {
|
|
28716
29688
|
const n = ptsNumber(name2);
|
|
28717
|
-
const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
|
|
29689
|
+
const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) ?? this.options.devices?.openTty(name2) : this.options.ptys?.get(n).slave;
|
|
28718
29690
|
if (!tty) throw new KernelError("ENOENT");
|
|
28719
29691
|
return tty;
|
|
28720
29692
|
}
|
|
@@ -28836,6 +29808,16 @@ var WasmProcess = class {
|
|
|
28836
29808
|
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
28837
29809
|
case "fd-open-embedder":
|
|
28838
29810
|
return { ok: true, kind: "json", json: await this.openEmbedder(req.flags) };
|
|
29811
|
+
case "fd-dev-nodes":
|
|
29812
|
+
return { ok: true, kind: "json", json: this.options.devices?.list() ?? [] };
|
|
29813
|
+
case "fd-open-dev":
|
|
29814
|
+
return { ok: true, kind: "json", json: await this.openDev(req.path, req.flags, cancelled) };
|
|
29815
|
+
case "fd-dev-ioctl":
|
|
29816
|
+
return { ok: true, kind: "json", json: await this.devIoctl(req, cancelled) };
|
|
29817
|
+
case "fd-dev-meta": {
|
|
29818
|
+
const { op: _, path, ...change } = req;
|
|
29819
|
+
return this.devMeta(path, change);
|
|
29820
|
+
}
|
|
28839
29821
|
case "fd-clipboard-abort": {
|
|
28840
29822
|
const file = this.fds.get(req.fd);
|
|
28841
29823
|
file.file.clipboard?.abort();
|
|
@@ -28880,6 +29862,11 @@ var WasmProcess = class {
|
|
|
28880
29862
|
return { ok: true, kind: "void" };
|
|
28881
29863
|
}
|
|
28882
29864
|
const file = this.fds.get(req.fd).file;
|
|
29865
|
+
const device = file.device;
|
|
29866
|
+
if (device && (req.op === "fd-chmod" || req.op === "fd-chown" || req.op === "fd-utimes")) {
|
|
29867
|
+
const { op: _, fd: __, ...change } = req;
|
|
29868
|
+
return this.devMeta(device.path, change);
|
|
29869
|
+
}
|
|
28883
29870
|
if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
|
|
28884
29871
|
switch (req.op) {
|
|
28885
29872
|
case "fd-pread":
|
|
@@ -29049,14 +30036,19 @@ var WasmProcess = class {
|
|
|
29049
30036
|
...file.fifo !== void 0 ? { fifo: file.fifo } : {}
|
|
29050
30037
|
};
|
|
29051
30038
|
}
|
|
29052
|
-
ttySyscall(req) {
|
|
30039
|
+
async ttySyscall(req, cancelled) {
|
|
29053
30040
|
const tty = this.tty(req.fd);
|
|
29054
30041
|
const jobs = this.options.jobs;
|
|
30042
|
+
const device = this.fds.get(req.fd).file.device;
|
|
30043
|
+
if (device && req.op.startsWith("tty-pgrp")) throw new KernelError("ENOTTY");
|
|
29055
30044
|
switch (req.op) {
|
|
29056
30045
|
case "tty-get":
|
|
29057
30046
|
return { ok: true, kind: "json", json: tty.tcgets() };
|
|
29058
30047
|
case "tty-set":
|
|
29059
|
-
|
|
30048
|
+
if (device) {
|
|
30049
|
+
const how = { flush: req.flush === true, drain: req.drain === true };
|
|
30050
|
+
await device.setTermios(req.termios, how, this.blockingSignal(cancelled));
|
|
30051
|
+
} else tty.tcsets(req.termios, req.flush === true);
|
|
29060
30052
|
return { ok: true, kind: "void" };
|
|
29061
30053
|
case "tty-winsz": {
|
|
29062
30054
|
const size = tty.winsize();
|
|
@@ -29937,6 +30929,7 @@ function processOptions(opts, header4, hooks, created4) {
|
|
|
29937
30929
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
29938
30930
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
29939
30931
|
...embedderOptions(opts),
|
|
30932
|
+
devices: opts.devices,
|
|
29940
30933
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
29941
30934
|
...opts.source ? { source: opts.source } : {},
|
|
29942
30935
|
noLinks: opts.noLinks,
|
|
@@ -30688,14 +31681,14 @@ var LockService = class {
|
|
|
30688
31681
|
);
|
|
30689
31682
|
});
|
|
30690
31683
|
}
|
|
30691
|
-
async holder(key, owner,
|
|
31684
|
+
async holder(key, owner, want2) {
|
|
30692
31685
|
return await this.ask({
|
|
30693
31686
|
op: "holder",
|
|
30694
31687
|
key,
|
|
30695
31688
|
owner,
|
|
30696
|
-
kind:
|
|
30697
|
-
start:
|
|
30698
|
-
end:
|
|
31689
|
+
kind: want2.kind,
|
|
31690
|
+
start: want2.start,
|
|
31691
|
+
end: want2.end
|
|
30699
31692
|
}) ?? void 0;
|
|
30700
31693
|
}
|
|
30701
31694
|
async set(key, owner, kind, start2, end) {
|
|
@@ -30974,9 +31967,9 @@ function local(name2) {
|
|
|
30974
31967
|
function literal2(name2) {
|
|
30975
31968
|
return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
|
|
30976
31969
|
}
|
|
30977
|
-
function addressData(
|
|
30978
|
-
if (type === TYPE_A) return ipv4Bytes(
|
|
30979
|
-
const groups = type === TYPE_AAAA ? ipv6Groups(
|
|
31970
|
+
function addressData(address2, type) {
|
|
31971
|
+
if (type === TYPE_A) return ipv4Bytes(address2);
|
|
31972
|
+
const groups = type === TYPE_AAAA ? ipv6Groups(address2) : void 0;
|
|
30980
31973
|
return groups?.flatMap((g) => [g >> 8, g & 255]);
|
|
30981
31974
|
}
|
|
30982
31975
|
var DnsStub = class {
|
|
@@ -30989,10 +31982,10 @@ var DnsStub = class {
|
|
|
30989
31982
|
this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
|
|
30990
31983
|
}
|
|
30991
31984
|
async answer(query, udp) {
|
|
30992
|
-
const
|
|
30993
|
-
if (!
|
|
30994
|
-
if (!
|
|
30995
|
-
const q =
|
|
31985
|
+
const parsed3 = parseQuery(query);
|
|
31986
|
+
if (!parsed3) return void 0;
|
|
31987
|
+
if (!parsed3.ok) return errorReply2(parsed3.id, parsed3.flags, parsed3.rcode);
|
|
31988
|
+
const q = parsed3.question;
|
|
30996
31989
|
const reply = await this.reply(query, q).catch(
|
|
30997
31990
|
() => answerReply(query, q, SERVFAIL, [], false)
|
|
30998
31991
|
);
|
|
@@ -31001,8 +31994,8 @@ var DnsStub = class {
|
|
|
31001
31994
|
async reply(query, q) {
|
|
31002
31995
|
if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
|
|
31003
31996
|
if (q.type === TYPE_PTR) {
|
|
31004
|
-
const
|
|
31005
|
-
const name2 =
|
|
31997
|
+
const address2 = reverseAddress(q.name);
|
|
31998
|
+
const name2 = address2 !== void 0 ? await this.reverse(address2) : void 0;
|
|
31006
31999
|
if (name2 !== void 0)
|
|
31007
32000
|
return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
|
|
31008
32001
|
return this.forward(query, q);
|
|
@@ -31032,8 +32025,8 @@ var DnsStub = class {
|
|
|
31032
32025
|
known(name2) {
|
|
31033
32026
|
return this.resolver.known(name2).catch(() => false);
|
|
31034
32027
|
}
|
|
31035
|
-
reverse(
|
|
31036
|
-
return this.resolver.reverse(
|
|
32028
|
+
reverse(address2) {
|
|
32029
|
+
return this.resolver.reverse(address2).catch(() => void 0);
|
|
31037
32030
|
}
|
|
31038
32031
|
async forward(query, q) {
|
|
31039
32032
|
if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
|
|
@@ -31212,7 +32205,7 @@ function honors(traits, redirect) {
|
|
|
31212
32205
|
const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
|
|
31213
32206
|
return redirect === "manual" && raw2;
|
|
31214
32207
|
}
|
|
31215
|
-
function
|
|
32208
|
+
function refused4(message3) {
|
|
31216
32209
|
return new TransportError(message3, 502, void 0, "transport");
|
|
31217
32210
|
}
|
|
31218
32211
|
var RemoteTransport = class {
|
|
@@ -31303,7 +32296,7 @@ var RemoteTransport = class {
|
|
|
31303
32296
|
const { signal, redirect } = request3;
|
|
31304
32297
|
signal.throwIfAborted();
|
|
31305
32298
|
if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
|
|
31306
|
-
throw
|
|
32299
|
+
throw refused4(`this transport cannot honor redirect: ${redirect}`);
|
|
31307
32300
|
}
|
|
31308
32301
|
let finished = false;
|
|
31309
32302
|
const abort = () => {
|
|
@@ -31354,7 +32347,7 @@ var RemoteTransport = class {
|
|
|
31354
32347
|
};
|
|
31355
32348
|
if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
|
|
31356
32349
|
await cancel();
|
|
31357
|
-
throw
|
|
32350
|
+
throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
|
|
31358
32351
|
}
|
|
31359
32352
|
return {
|
|
31360
32353
|
...url !== void 0 ? { url } : {},
|
|
@@ -31844,6 +32837,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
|
|
|
31844
32837
|
"proc-list",
|
|
31845
32838
|
"proc-identity",
|
|
31846
32839
|
"fd-info",
|
|
32840
|
+
"fd-dev-nodes",
|
|
31847
32841
|
"sig-mask",
|
|
31848
32842
|
"sig-inherited",
|
|
31849
32843
|
"proc-setpgid",
|
|
@@ -31940,8 +32934,8 @@ var WasmMemories = class {
|
|
|
31940
32934
|
const learned = this.learned;
|
|
31941
32935
|
if (!learned) return this.base;
|
|
31942
32936
|
const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
|
|
31943
|
-
const
|
|
31944
|
-
if (
|
|
32937
|
+
const raised2 = learned.ceiling + steps * DECAY_STEP;
|
|
32938
|
+
if (raised2 < (this.base ?? DEFAULT_CEILING)) return raised2;
|
|
31945
32939
|
this.learned = void 0;
|
|
31946
32940
|
return this.base;
|
|
31947
32941
|
}
|
|
@@ -32084,9 +33078,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
32084
33078
|
let open2 = false;
|
|
32085
33079
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
32086
33080
|
async function hello(message3) {
|
|
32087
|
-
const
|
|
32088
|
-
if (
|
|
32089
|
-
reply({ hello: { protocol: DRIVER_PROTOCOL, error:
|
|
33081
|
+
const refused5 = driverVersionError(message3.protocol);
|
|
33082
|
+
if (refused5) {
|
|
33083
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused5, errno: "EPROTO" } });
|
|
32090
33084
|
return;
|
|
32091
33085
|
}
|
|
32092
33086
|
try {
|
|
@@ -32392,7 +33386,7 @@ function unescape(field4) {
|
|
|
32392
33386
|
);
|
|
32393
33387
|
}
|
|
32394
33388
|
function parseFstab(text) {
|
|
32395
|
-
const
|
|
33389
|
+
const lines3 = [];
|
|
32396
33390
|
text.split("\n").forEach((raw2, index) => {
|
|
32397
33391
|
const content = raw2.replace(/#.*$/, "").trim();
|
|
32398
33392
|
if (!content) return;
|
|
@@ -32401,12 +33395,12 @@ function parseFstab(text) {
|
|
|
32401
33395
|
if (options.split(",").includes("noauto")) return;
|
|
32402
33396
|
const spec = { source, target, type, options: {} };
|
|
32403
33397
|
try {
|
|
32404
|
-
|
|
33398
|
+
lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
32405
33399
|
} catch (err) {
|
|
32406
|
-
|
|
33400
|
+
lines3.push({ line: index + 1, spec, invalid: err });
|
|
32407
33401
|
}
|
|
32408
33402
|
});
|
|
32409
|
-
return
|
|
33403
|
+
return lines3;
|
|
32410
33404
|
}
|
|
32411
33405
|
function codeOf6(err) {
|
|
32412
33406
|
const code = err?.code;
|
|
@@ -32424,20 +33418,20 @@ function wait(ms, signal) {
|
|
|
32424
33418
|
signal.addEventListener("abort", done5, { once: true });
|
|
32425
33419
|
});
|
|
32426
33420
|
}
|
|
32427
|
-
async function mountFstab(
|
|
33421
|
+
async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
32428
33422
|
}, settled = () => {
|
|
32429
33423
|
}) {
|
|
32430
33424
|
return Promise.all(
|
|
32431
|
-
|
|
33425
|
+
lines3.map(async (each) => {
|
|
32432
33426
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
32433
33427
|
settled(each, result);
|
|
32434
33428
|
return result;
|
|
32435
33429
|
})
|
|
32436
33430
|
);
|
|
32437
33431
|
}
|
|
32438
|
-
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
32439
|
-
let failed4 =
|
|
32440
|
-
for (let attempt = 0; !
|
|
33432
|
+
async function mountLine({ line, spec, invalid: invalid2 }, mount, signal, retries, discard) {
|
|
33433
|
+
let failed4 = invalid2;
|
|
33434
|
+
for (let attempt = 0; !invalid2 && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
32441
33435
|
try {
|
|
32442
33436
|
const entry = await mount(spec);
|
|
32443
33437
|
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
@@ -32605,14 +33599,14 @@ var RESTORE_AGAIN_MS = 3e4;
|
|
|
32605
33599
|
function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
32606
33600
|
const handles = /* @__PURE__ */ new Map();
|
|
32607
33601
|
let nextFh = 0;
|
|
32608
|
-
const
|
|
33602
|
+
const fresh4 = (remote) => {
|
|
32609
33603
|
if (remote.client.token === remote.token && !remote.client.gone) return remote;
|
|
32610
33604
|
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
32611
33605
|
};
|
|
32612
33606
|
const live2 = (fh) => {
|
|
32613
33607
|
const remote = handles.get(fh);
|
|
32614
33608
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
32615
|
-
return
|
|
33609
|
+
return fresh4(remote);
|
|
32616
33610
|
};
|
|
32617
33611
|
const call = (op, body = {}) => current().call(op, body);
|
|
32618
33612
|
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);
|
|
@@ -32694,7 +33688,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32694
33688
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
32695
33689
|
handles.delete(fh);
|
|
32696
33690
|
try {
|
|
32697
|
-
await
|
|
33691
|
+
await fresh4(remote).client.call("release", { fh: remote.fh }, true);
|
|
32698
33692
|
} finally {
|
|
32699
33693
|
if (remote.restore) await settle3(current(), remote.restore);
|
|
32700
33694
|
}
|
|
@@ -32744,7 +33738,7 @@ async function resize(h, path, size) {
|
|
|
32744
33738
|
await h.release(fh);
|
|
32745
33739
|
}
|
|
32746
33740
|
}
|
|
32747
|
-
function
|
|
33741
|
+
function sleep2(ms, signal) {
|
|
32748
33742
|
return new Promise((resolve2) => {
|
|
32749
33743
|
const done5 = () => {
|
|
32750
33744
|
clearTimeout(timer);
|
|
@@ -32772,22 +33766,22 @@ function keepWatching(loop, signal) {
|
|
|
32772
33766
|
const stop = () => session2.abort();
|
|
32773
33767
|
signal.addEventListener("abort", stop);
|
|
32774
33768
|
let timer;
|
|
32775
|
-
const
|
|
33769
|
+
const quiet4 = () => {
|
|
32776
33770
|
clearTimeout(timer);
|
|
32777
33771
|
timer = setTimeout(stop, silenceMs);
|
|
32778
33772
|
};
|
|
32779
33773
|
let opened2 = false;
|
|
32780
33774
|
try {
|
|
32781
33775
|
const client = await loop.connect();
|
|
32782
|
-
|
|
33776
|
+
quiet4();
|
|
32783
33777
|
await client.watch(
|
|
32784
33778
|
session2.signal,
|
|
32785
33779
|
(event) => {
|
|
32786
|
-
|
|
33780
|
+
quiet4();
|
|
32787
33781
|
if (!event.ping) loop.event(event);
|
|
32788
33782
|
},
|
|
32789
33783
|
() => {
|
|
32790
|
-
|
|
33784
|
+
quiet4();
|
|
32791
33785
|
opened2 = true;
|
|
32792
33786
|
up = true;
|
|
32793
33787
|
loop.up(client);
|
|
@@ -32816,7 +33810,7 @@ function keepWatching(loop, signal) {
|
|
|
32816
33810
|
loop.down();
|
|
32817
33811
|
}
|
|
32818
33812
|
ready2.resolve();
|
|
32819
|
-
await
|
|
33813
|
+
await sleep2(wait2, signal);
|
|
32820
33814
|
wait2 = Math.min(wait2 * 2, most);
|
|
32821
33815
|
}
|
|
32822
33816
|
})();
|
|
@@ -34300,6 +35294,7 @@ var Launcher = class {
|
|
|
34300
35294
|
scrubs = /* @__PURE__ */ new Map();
|
|
34301
35295
|
clipboard;
|
|
34302
35296
|
embedder;
|
|
35297
|
+
devices;
|
|
34303
35298
|
rootFs;
|
|
34304
35299
|
fileWatches;
|
|
34305
35300
|
nextPid = 1e3;
|
|
@@ -34324,6 +35319,7 @@ var Launcher = class {
|
|
|
34324
35319
|
);
|
|
34325
35320
|
this.embedder = this.embedderBroker(options.embedder);
|
|
34326
35321
|
this.embedder.denied = options.onDenial;
|
|
35322
|
+
this.devices = this.deviceNodes(options.devices);
|
|
34327
35323
|
this.sealed = new SealedBroker(
|
|
34328
35324
|
options.sealed,
|
|
34329
35325
|
this.usersMode,
|
|
@@ -34539,16 +35535,16 @@ var Launcher = class {
|
|
|
34539
35535
|
if (!found) {
|
|
34540
35536
|
found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
|
|
34541
35537
|
const tiers = await this.tiers(scope, appended2);
|
|
34542
|
-
const
|
|
35538
|
+
const refused5 = [];
|
|
34543
35539
|
const commands = await scanCommands(
|
|
34544
35540
|
this.scanned,
|
|
34545
35541
|
tiers,
|
|
34546
35542
|
tiers.length - appended2.length,
|
|
34547
35543
|
void 0,
|
|
34548
|
-
|
|
35544
|
+
refused5,
|
|
34549
35545
|
await this.globalRoots(scope)
|
|
34550
35546
|
);
|
|
34551
|
-
this.refusedOf.set(commands,
|
|
35547
|
+
this.refusedOf.set(commands, refused5.map(refusing));
|
|
34552
35548
|
return commands;
|
|
34553
35549
|
}).then(
|
|
34554
35550
|
(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)
|
|
@@ -34755,14 +35751,14 @@ var Launcher = class {
|
|
|
34755
35751
|
return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
|
|
34756
35752
|
}
|
|
34757
35753
|
async refusedAt(path, name2, scope) {
|
|
34758
|
-
const
|
|
34759
|
-
if (
|
|
35754
|
+
const refused5 = this.refusedOf.get(await this.commands(scope)) ?? [];
|
|
35755
|
+
if (refused5.length === 0) return void 0;
|
|
34760
35756
|
const st = await this.base.stat(path).catch(() => void 0);
|
|
34761
35757
|
if (!st?.isFile) return void 0;
|
|
34762
35758
|
const real = await physicalPathAsync(this.base, path).catch(() => path);
|
|
34763
35759
|
const same3 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
|
|
34764
35760
|
const owners = [];
|
|
34765
|
-
for (const c of
|
|
35761
|
+
for (const c of refused5) {
|
|
34766
35762
|
const origin = c.origin;
|
|
34767
35763
|
for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
|
|
34768
35764
|
if (!await same3(own)) continue;
|
|
@@ -34772,11 +35768,11 @@ var Launcher = class {
|
|
|
34772
35768
|
}
|
|
34773
35769
|
const stem = name2.replace(/\.wasm$/, "");
|
|
34774
35770
|
const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
|
|
34775
|
-
return named ?? owners[0] ?? await this.refusedCopy(real, name2,
|
|
35771
|
+
return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
|
|
34776
35772
|
}
|
|
34777
|
-
async refusedCopy(real, name2,
|
|
35773
|
+
async refusedCopy(real, name2, refused5) {
|
|
34778
35774
|
const dir = real.slice(0, real.lastIndexOf("/"));
|
|
34779
|
-
for (const c of
|
|
35775
|
+
for (const c of refused5) {
|
|
34780
35776
|
const origin = c.origin;
|
|
34781
35777
|
if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
|
|
34782
35778
|
const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
|
|
@@ -34943,8 +35939,8 @@ var Launcher = class {
|
|
|
34943
35939
|
const via = planned?.via ?? [];
|
|
34944
35940
|
for (const [i, file] of via.entries()) {
|
|
34945
35941
|
if (!byPath(file)) continue;
|
|
34946
|
-
const
|
|
34947
|
-
await need(g, "exec", this.fs.resolvePath(req.cwd, file),
|
|
35942
|
+
const want2 = i < via.length - 1 ? R_OK | X_OK : X_OK;
|
|
35943
|
+
await need(g, "exec", this.fs.resolvePath(req.cwd, file), want2);
|
|
34948
35944
|
}
|
|
34949
35945
|
for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
|
|
34950
35946
|
if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
|
|
@@ -34984,12 +35980,12 @@ var Launcher = class {
|
|
|
34984
35980
|
return void 0;
|
|
34985
35981
|
}
|
|
34986
35982
|
async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
|
|
34987
|
-
const
|
|
35983
|
+
const refused5 = (err) => {
|
|
34988
35984
|
throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
|
|
34989
35985
|
};
|
|
34990
|
-
const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(
|
|
35986
|
+
const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused5);
|
|
34991
35987
|
const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
|
|
34992
|
-
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(
|
|
35988
|
+
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
|
|
34993
35989
|
if (planned) return planned;
|
|
34994
35990
|
throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
|
|
34995
35991
|
}
|
|
@@ -35625,6 +36621,7 @@ var Launcher = class {
|
|
|
35625
36621
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
35626
36622
|
clipboard: this.clipboard,
|
|
35627
36623
|
embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
|
|
36624
|
+
devices: this.devices,
|
|
35628
36625
|
...this.dac ? { dac: this.dac } : {},
|
|
35629
36626
|
program: req.program,
|
|
35630
36627
|
changes: this.changes.sab,
|
|
@@ -35808,8 +36805,8 @@ var Launcher = class {
|
|
|
35808
36805
|
this.sampledAt = Math.max(this.sampledAt, now);
|
|
35809
36806
|
this.load = this.load.map((avg, i) => {
|
|
35810
36807
|
const window = LOAD_WINDOWS_MS[i];
|
|
35811
|
-
const
|
|
35812
|
-
return avg * Math.exp(-elapsed / window) + running *
|
|
36808
|
+
const fresh4 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
|
|
36809
|
+
return avg * Math.exp(-elapsed / window) + running * fresh4;
|
|
35813
36810
|
});
|
|
35814
36811
|
}
|
|
35815
36812
|
idleSince;
|
|
@@ -36156,7 +37153,7 @@ var Launcher = class {
|
|
|
36156
37153
|
forker(ppid, parent) {
|
|
36157
37154
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
36158
37155
|
return async (state, given) => {
|
|
36159
|
-
const
|
|
37156
|
+
const fresh4 = (fds, quietOom) => this.start({
|
|
36160
37157
|
...image,
|
|
36161
37158
|
quietOom,
|
|
36162
37159
|
cwd: state.cwd ?? parent.cwd,
|
|
@@ -36172,7 +37169,7 @@ var Launcher = class {
|
|
|
36172
37169
|
tagBase: this.tagBases.get(ppid),
|
|
36173
37170
|
links: this.links.get(ppid)
|
|
36174
37171
|
});
|
|
36175
|
-
return childHandle(await this.admitted(parent.program.abi !== "js", given,
|
|
37172
|
+
return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
|
|
36176
37173
|
};
|
|
36177
37174
|
}
|
|
36178
37175
|
keySignal(tty, fallback, sig, send2) {
|
|
@@ -36213,6 +37210,14 @@ var Launcher = class {
|
|
|
36213
37210
|
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
36214
37211
|
this.sealed.factsOf = (pid) => this.processFacts(pid);
|
|
36215
37212
|
}
|
|
37213
|
+
deviceNodes(hook) {
|
|
37214
|
+
const nodes = new DeviceNodes(this.usersMode, {
|
|
37215
|
+
groupId: async (name2) => (await this.users.groups()).find((g) => g.name === name2)?.gid,
|
|
37216
|
+
userId: async (name2) => (await this.users.lookup(name2))?.uid
|
|
37217
|
+
});
|
|
37218
|
+
if (hook) nodes.register(0, hook);
|
|
37219
|
+
return nodes;
|
|
37220
|
+
}
|
|
36216
37221
|
embedderBroker(hook) {
|
|
36217
37222
|
const broker = new EmbedderBroker(
|
|
36218
37223
|
this.usersMode,
|
|
@@ -36502,8 +37507,8 @@ var Launcher = class {
|
|
|
36502
37507
|
for (const [path, mode] of layout) {
|
|
36503
37508
|
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
36504
37509
|
const real = await this.realLayoutPath(path);
|
|
36505
|
-
const
|
|
36506
|
-
if (
|
|
37510
|
+
const fresh4 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
37511
|
+
if (fresh4) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
36507
37512
|
}
|
|
36508
37513
|
}
|
|
36509
37514
|
async realLayoutPath(given) {
|
|
@@ -36637,10 +37642,10 @@ var Launcher = class {
|
|
|
36637
37642
|
}
|
|
36638
37643
|
async mountFstab() {
|
|
36639
37644
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
36640
|
-
const
|
|
36641
|
-
for (const { spec } of
|
|
37645
|
+
const lines3 = parseFstab(text);
|
|
37646
|
+
for (const { spec } of lines3) this.mounts.note(spec, "pending");
|
|
36642
37647
|
return mountFstab(
|
|
36643
|
-
|
|
37648
|
+
lines3,
|
|
36644
37649
|
(spec) => this.mount(spec).catch((err) => {
|
|
36645
37650
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
36646
37651
|
throw err;
|
|
@@ -37537,7 +38542,7 @@ function isAttestRequest(req) {
|
|
|
37537
38542
|
return ATTEST_OPS.has(req.op);
|
|
37538
38543
|
}
|
|
37539
38544
|
function isUsersRequest(req) {
|
|
37540
|
-
return USERS_OPS.has(req.op);
|
|
38545
|
+
return USERS_OPS.has(req.op) || req.op === "devices-add" || req.op === "devices-remove";
|
|
37541
38546
|
}
|
|
37542
38547
|
function transferOf2(req, result) {
|
|
37543
38548
|
if (result instanceof MessagePort) return [result];
|
|
@@ -37570,7 +38575,18 @@ function failure(err) {
|
|
|
37570
38575
|
...typeof code === "string" ? { code } : {}
|
|
37571
38576
|
};
|
|
37572
38577
|
}
|
|
38578
|
+
function pageReplied(data, requests, devices) {
|
|
38579
|
+
const embedderReply = embedderReplyOf(data);
|
|
38580
|
+
if (embedderReply) requests.answer(embedderReply);
|
|
38581
|
+
const devicesReply = devicesReplyOf(data);
|
|
38582
|
+
if (devicesReply) devices.answer(devicesReply);
|
|
38583
|
+
return embedderReply !== void 0 || devicesReply !== void 0;
|
|
38584
|
+
}
|
|
38585
|
+
function devicesOp(l, req) {
|
|
38586
|
+
return req.op === "devices-add" ? l.devices.add(req.spec, req.client) : l.devices.remove(req.node);
|
|
38587
|
+
}
|
|
37573
38588
|
function usersOp(l, req) {
|
|
38589
|
+
if (req.op === "devices-add" || req.op === "devices-remove") return devicesOp(l, req);
|
|
37574
38590
|
if (req.op === "users-add") return l.users.add(req.user);
|
|
37575
38591
|
if (req.op === "users-remove") return l.users.remove(req.name, req.options);
|
|
37576
38592
|
if (req.op === "groups-list") return l.users.groups();
|
|
@@ -37580,7 +38596,7 @@ function usersOp(l, req) {
|
|
|
37580
38596
|
return l.users.list();
|
|
37581
38597
|
}
|
|
37582
38598
|
var clientIds = 0;
|
|
37583
|
-
async function connectClient(deps, { user, tag,
|
|
38599
|
+
async function connectClient(deps, { user, tag, requests: bind2, devices }) {
|
|
37584
38600
|
const l = await deps.ready();
|
|
37585
38601
|
tagOption(tag);
|
|
37586
38602
|
if (user !== void 0 && !await l.users.lookup(user)) {
|
|
@@ -37594,6 +38610,7 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
|
|
|
37594
38610
|
signal: signalNumber,
|
|
37595
38611
|
session: client,
|
|
37596
38612
|
...bind2 === "attach" ? { attachRequests: true } : {},
|
|
38613
|
+
...devices === "attach" ? { attachDevices: true } : {},
|
|
37597
38614
|
...user !== void 0 ? { user } : {},
|
|
37598
38615
|
...tag !== void 0 ? { tag } : {},
|
|
37599
38616
|
...deps.locks ? { locks: deps.locks } : {},
|
|
@@ -37601,13 +38618,15 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
|
|
|
37601
38618
|
});
|
|
37602
38619
|
deps.clients.add(served2);
|
|
37603
38620
|
const unbind = bind2 && bind2 !== "attach" ? l.embedder.register(client, deps.requests.hookFor(client, bind2)) : void 0;
|
|
38621
|
+
const unbindDevices = devices === true ? l.devices.register(client, deps.devices.hookFor(client)) : void 0;
|
|
37604
38622
|
void served2.closed.then(() => {
|
|
37605
38623
|
deps.clients.delete(served2);
|
|
37606
38624
|
unbind?.();
|
|
38625
|
+
unbindDevices?.();
|
|
37607
38626
|
});
|
|
37608
|
-
return unbind ? { port: port2, client } : port2;
|
|
38627
|
+
return unbind || devices ? { port: port2, client } : port2;
|
|
37609
38628
|
}
|
|
37610
|
-
function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests) {
|
|
38629
|
+
function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests, devices) {
|
|
37611
38630
|
return {
|
|
37612
38631
|
cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
|
|
37613
38632
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
@@ -37619,7 +38638,8 @@ function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, request
|
|
|
37619
38638
|
...req.sealed ? { sealed } : {},
|
|
37620
38639
|
...req.sealedFetch ? { sealedFetch } : {},
|
|
37621
38640
|
...req.clipboard ? { clipboard } : {},
|
|
37622
|
-
...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {}
|
|
38641
|
+
...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {},
|
|
38642
|
+
...req.devices ? { devices: devices.hookFor(0) } : {}
|
|
37623
38643
|
};
|
|
37624
38644
|
}
|
|
37625
38645
|
function hostProtect(init, lookup2) {
|
|
@@ -37673,6 +38693,20 @@ function processOps(ready2, reply) {
|
|
|
37673
38693
|
}
|
|
37674
38694
|
return { terminal, sudoRun, act: onTerminal };
|
|
37675
38695
|
}
|
|
38696
|
+
function lockHolder() {
|
|
38697
|
+
let holding;
|
|
38698
|
+
return (locks) => {
|
|
38699
|
+
holding ??= new Promise((resolve2) => {
|
|
38700
|
+
const name2 = `slicc-kernel:${crypto.randomUUID()}`;
|
|
38701
|
+
void locks.request(name2, () => {
|
|
38702
|
+
resolve2(name2);
|
|
38703
|
+
return new Promise(() => {
|
|
38704
|
+
});
|
|
38705
|
+
});
|
|
38706
|
+
});
|
|
38707
|
+
return holding;
|
|
38708
|
+
};
|
|
38709
|
+
}
|
|
37676
38710
|
function serveKernel(port, deps) {
|
|
37677
38711
|
let launcher;
|
|
37678
38712
|
let remote;
|
|
@@ -37682,24 +38716,14 @@ function serveKernel(port, deps) {
|
|
|
37682
38716
|
const sealed = sealedAsker(port);
|
|
37683
38717
|
const clipboard = clipboardAsker(port);
|
|
37684
38718
|
const requests = embedderAsker(port);
|
|
38719
|
+
const devices = devicesAsker(port);
|
|
37685
38720
|
const integrity = integrityAsker(port);
|
|
37686
38721
|
const approvals = sudoApprovals(port);
|
|
37687
38722
|
const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
|
|
37688
|
-
|
|
37689
|
-
|
|
37690
|
-
|
|
37691
|
-
|
|
37692
|
-
void locks2.request(name2, () => {
|
|
37693
|
-
resolve2(name2);
|
|
37694
|
-
return new Promise(() => {
|
|
37695
|
-
});
|
|
37696
|
-
});
|
|
37697
|
-
});
|
|
37698
|
-
return holding;
|
|
37699
|
-
}
|
|
37700
|
-
const connect2 = (user, tag, bind2) => connectClient(
|
|
37701
|
-
{ ready: ready2, clients, requests, held: locks ? () => holdLock(locks) : void 0, locks },
|
|
37702
|
-
{ user, tag, bind: bind2 }
|
|
38723
|
+
const holdLock = lockHolder();
|
|
38724
|
+
const connect2 = (req) => connectClient(
|
|
38725
|
+
{ ready: ready2, clients, requests, devices, held: locks && (() => holdLock(locks)), locks },
|
|
38726
|
+
req
|
|
37703
38727
|
);
|
|
37704
38728
|
async function ready2() {
|
|
37705
38729
|
if (!launcher) throw new Error("the kernel is not initialized");
|
|
@@ -37730,7 +38754,8 @@ function serveKernel(port, deps) {
|
|
|
37730
38754
|
sealed.fetch,
|
|
37731
38755
|
clipboard.hook,
|
|
37732
38756
|
integrity.lookup,
|
|
37733
|
-
requests
|
|
38757
|
+
requests,
|
|
38758
|
+
devices
|
|
37734
38759
|
),
|
|
37735
38760
|
...req.approve ? { approve: approvals.ask } : {},
|
|
37736
38761
|
...net,
|
|
@@ -37762,7 +38787,7 @@ function serveKernel(port, deps) {
|
|
|
37762
38787
|
if (req.op === "sealed-placeholders") return (await ready2()).setPlaceholders(req.list);
|
|
37763
38788
|
if (req.op === "open-terminal") return processes2.terminal(req);
|
|
37764
38789
|
if (req.op === "sudo-run") return processes2.sudoRun(req);
|
|
37765
|
-
if (req.op === "connect") return connect2(req
|
|
38790
|
+
if (req.op === "connect") return connect2(req);
|
|
37766
38791
|
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
37767
38792
|
if (req.op === "routes" || req.op === "transport-traits") {
|
|
37768
38793
|
return networkOp(await ready2(), req, remote);
|
|
@@ -37799,8 +38824,7 @@ function serveKernel(port, deps) {
|
|
|
37799
38824
|
if (sealedReply) return sealed.answer(sealedReply);
|
|
37800
38825
|
const clipboardReply = event.data.clipboardReply;
|
|
37801
38826
|
if (clipboardReply) return clipboard.answer(clipboardReply);
|
|
37802
|
-
|
|
37803
|
-
if (embedderReply) return requests.answer(embedderReply);
|
|
38827
|
+
if (pageReplied(event.data, requests, devices)) return;
|
|
37804
38828
|
const integrityReply = event.data.integrityReply;
|
|
37805
38829
|
if (integrityReply) return integrity.answer(integrityReply);
|
|
37806
38830
|
const grant = event.data.hostfsGrant;
|