@ai-ecoverse/slicc-kernel 1.182.0 → 1.183.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -5
- package/dist/driver-worker.js +290 -90
- package/dist/driver-worker.js.map +4 -4
- package/dist/index.js +328 -102
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +1705 -695
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +298 -98
- package/dist/node-driver-worker.js.map +4 -4
- package/dist/node-process-worker.cjs +849 -193
- package/dist/node-process-worker.cjs.map +4 -4
- package/dist/node-process-worker.js +849 -193
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +1875 -743
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +805 -149
- package/dist/process-worker.js.map +4 -4
- package/dist/types/client/attach.d.ts +2 -0
- package/dist/types/client/protocol.d.ts +1 -0
- package/dist/types/client/serve-client.d.ts +1 -0
- package/dist/types/devices-relay.d.ts +74 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/kernel/devices.d.ts +155 -0
- package/dist/types/kernel/embedder.d.ts +2 -0
- package/dist/types/kernel/fd-table.d.ts +3 -1
- package/dist/types/kernel/host.d.ts +2 -0
- package/dist/types/kernel/process.d.ts +20 -0
- package/dist/types/kernel/serial-ioctl.d.ts +73 -0
- package/dist/types/launcher.d.ts +4 -0
- package/dist/types/node.d.ts +6 -2
- package/dist/types/process/js/js-dev.d.ts +24 -0
- package/dist/types/process/kernel-streams.d.ts +11 -1
- package/dist/types/process/process-pty.d.ts +1 -0
- package/dist/types/process/wasi/wasi-fds.d.ts +1 -0
- package/dist/types/process/wasi/wasi-termios.d.ts +1 -0
- package/dist/types/serve.d.ts +14 -1
- package/package.json +2 -2
package/dist/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");
|
|
@@ -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
|
}
|
|
@@ -5075,10 +5075,129 @@ function portCdpHook(ask, gone) {
|
|
|
5075
5075
|
});
|
|
5076
5076
|
}
|
|
5077
5077
|
|
|
5078
|
-
// 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
|
+
}
|
|
5079
5085
|
function fresh2(bytes) {
|
|
5080
5086
|
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice();
|
|
5081
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
|
+
}
|
|
5082
5201
|
function embedderAsker(port) {
|
|
5083
5202
|
const asks = /* @__PURE__ */ new Map();
|
|
5084
5203
|
let next3 = 0;
|
|
@@ -5117,7 +5236,7 @@ function embedderAsker(port) {
|
|
|
5117
5236
|
const got = await asked.body.read().catch(() => ({ done: true, value: void 0 }));
|
|
5118
5237
|
if (got.done) post({ id: id2, bodyEnd: true });
|
|
5119
5238
|
else {
|
|
5120
|
-
const bytes =
|
|
5239
|
+
const bytes = fresh3(got.value);
|
|
5121
5240
|
post({ id: id2, body: bytes }, [bytes.buffer]);
|
|
5122
5241
|
}
|
|
5123
5242
|
};
|
|
@@ -5288,11 +5407,11 @@ function permOf(st, link2) {
|
|
|
5288
5407
|
...link2 !== void 0 ? { link: link2 } : {}
|
|
5289
5408
|
};
|
|
5290
5409
|
}
|
|
5291
|
-
function permits(cred, perm,
|
|
5410
|
+
function permits(cred, perm, want2, directory = true) {
|
|
5292
5411
|
const mode = perm.mode ?? (directory ? 493 : 420);
|
|
5293
|
-
if (cred.euid === 0) return (
|
|
5412
|
+
if (cred.euid === 0) return (want2 & X_OK) === 0 || directory || (mode & 73) !== 0;
|
|
5294
5413
|
const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
|
|
5295
|
-
return (mode >> shift &
|
|
5414
|
+
return (mode >> shift & want2) === want2;
|
|
5296
5415
|
}
|
|
5297
5416
|
function sticky(cred, dir, entry) {
|
|
5298
5417
|
if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
|
|
@@ -5367,27 +5486,27 @@ async function present(fs, path, directory) {
|
|
|
5367
5486
|
const st = await fs.lstat(path).catch(() => void 0);
|
|
5368
5487
|
return st !== void 0 && (!directory || st.isDirectory);
|
|
5369
5488
|
}
|
|
5370
|
-
async function finalOf(fs, walked,
|
|
5489
|
+
async function finalOf(fs, walked, want2, options) {
|
|
5371
5490
|
const perm = walked.perm;
|
|
5372
5491
|
if (options.follow === false && perm.link !== void 0) {
|
|
5373
5492
|
return [{ ...perm, mode: 511 }, false];
|
|
5374
5493
|
}
|
|
5375
|
-
if ((
|
|
5494
|
+
if ((want2 & X_OK) === 0 || options.directory !== void 0) {
|
|
5376
5495
|
return [perm, options.directory !== false];
|
|
5377
5496
|
}
|
|
5378
5497
|
const look = options.follow === false ? fs.lstat : fs.stat;
|
|
5379
5498
|
const st = await look.call(fs, walked.path).catch(() => void 0);
|
|
5380
5499
|
return st ? [permOf(st), st.isDirectory] : [perm, false];
|
|
5381
5500
|
}
|
|
5382
|
-
async function access(fs, cred, path,
|
|
5501
|
+
async function access(fs, cred, path, want2, options = {}) {
|
|
5383
5502
|
const walked = await walk(fs, cred, path, options.follow !== false, options.links);
|
|
5384
5503
|
if (walked.denied !== void 0) {
|
|
5385
5504
|
if (walked.link || await present(fs, walked.denied, true)) return walked;
|
|
5386
5505
|
return { path, perm: void 0, missing: true };
|
|
5387
5506
|
}
|
|
5388
5507
|
if (!walked.perm) return { ...walked, missing: true };
|
|
5389
|
-
const [perm, directory] = await finalOf(fs, walked,
|
|
5390
|
-
if (
|
|
5508
|
+
const [perm, directory] = await finalOf(fs, walked, want2, options);
|
|
5509
|
+
if (want2 === 0 || permits(cred, perm, want2, directory)) return walked;
|
|
5391
5510
|
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
5392
5511
|
return { ...walked, missing: true };
|
|
5393
5512
|
}
|
|
@@ -5493,11 +5612,11 @@ function ttyOpenable(g, tty) {
|
|
|
5493
5612
|
if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
|
|
5494
5613
|
deny(g, "open", tty.name ?? "/dev/tty");
|
|
5495
5614
|
}
|
|
5496
|
-
async function need(g, op, path,
|
|
5497
|
-
if (
|
|
5498
|
-
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));
|
|
5499
5618
|
if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown2);
|
|
5500
|
-
else if (
|
|
5619
|
+
else if (want2 & R_OK && g.fs.commandTarget) await readsAs(g, op, walked.path, shown2);
|
|
5501
5620
|
return walked;
|
|
5502
5621
|
}
|
|
5503
5622
|
async function readsAs(g, op, path, shown2) {
|
|
@@ -5646,10 +5765,10 @@ async function creatable(g, op, path) {
|
|
|
5646
5765
|
const target = await need(g, op, path, 0);
|
|
5647
5766
|
if (target.denied === void 0) await need(g, op, parentOf5(target.path), W_OK | X_OK, DIR, path);
|
|
5648
5767
|
}
|
|
5649
|
-
async function openable(g, op, path,
|
|
5768
|
+
async function openable(g, op, path, want2, create) {
|
|
5650
5769
|
const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
|
|
5651
5770
|
if (create && !st) return creatable(g, op, path);
|
|
5652
|
-
await need(g, op, path,
|
|
5771
|
+
await need(g, op, path, want2, { directory: st?.isDirectory ?? false });
|
|
5653
5772
|
}
|
|
5654
5773
|
|
|
5655
5774
|
// src/kernel/cred.ts
|
|
@@ -6090,8 +6209,8 @@ var FdTable = class _FdTable {
|
|
|
6090
6209
|
}
|
|
6091
6210
|
stdioTerminal() {
|
|
6092
6211
|
for (const fd of [0, 1, 2]) {
|
|
6093
|
-
const
|
|
6094
|
-
if (tty) return tty;
|
|
6212
|
+
const file = this.fds.get(fd)?.file;
|
|
6213
|
+
if (file?.tty && !file.device) return file.tty;
|
|
6095
6214
|
}
|
|
6096
6215
|
return void 0;
|
|
6097
6216
|
}
|
|
@@ -6392,13 +6511,13 @@ function addressBits(host) {
|
|
|
6392
6511
|
function parsePrefix(text) {
|
|
6393
6512
|
const parts2 = text.trim().split("/");
|
|
6394
6513
|
if (parts2.length > 2) return void 0;
|
|
6395
|
-
const [
|
|
6396
|
-
const
|
|
6397
|
-
if (!
|
|
6398
|
-
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;
|
|
6399
6518
|
const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
6400
6519
|
if (length2 < 0 || length2 > max) return void 0;
|
|
6401
|
-
return { ...
|
|
6520
|
+
return { ...parsed3, length: length2 };
|
|
6402
6521
|
}
|
|
6403
6522
|
function within4(prefix, addr) {
|
|
6404
6523
|
if (prefix.v6 !== addr.v6) return false;
|
|
@@ -6424,10 +6543,10 @@ var Routes = class {
|
|
|
6424
6543
|
this.ipv6 = ipv62;
|
|
6425
6544
|
}
|
|
6426
6545
|
set(table) {
|
|
6427
|
-
const
|
|
6428
|
-
const bad = (table.prefixes ?? []).find((_, i) => !
|
|
6546
|
+
const parsed3 = (table.prefixes ?? []).map(parsePrefix);
|
|
6547
|
+
const bad = (table.prefixes ?? []).find((_, i) => !parsed3[i]);
|
|
6429
6548
|
if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
|
|
6430
|
-
this.table =
|
|
6549
|
+
this.table = parsed3;
|
|
6431
6550
|
this.exit = table.exit === true;
|
|
6432
6551
|
}
|
|
6433
6552
|
classify(host) {
|
|
@@ -6963,10 +7082,10 @@ var KernelSocket = class _KernelSocket {
|
|
|
6963
7082
|
}
|
|
6964
7083
|
};
|
|
6965
7084
|
let made2;
|
|
6966
|
-
const
|
|
7085
|
+
const abandon2 = () => {
|
|
6967
7086
|
if (!keeps(signal.reason)) view2.close();
|
|
6968
7087
|
};
|
|
6969
|
-
signal.addEventListener("abort",
|
|
7088
|
+
signal.addEventListener("abort", abandon2, { once: true });
|
|
6970
7089
|
try {
|
|
6971
7090
|
made2 = await make(view2);
|
|
6972
7091
|
} catch (err) {
|
|
@@ -6979,7 +7098,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
6979
7098
|
throw signal.aborted ? signal.reason : err;
|
|
6980
7099
|
} finally {
|
|
6981
7100
|
handshaking = false;
|
|
6982
|
-
signal.removeEventListener("abort",
|
|
7101
|
+
signal.removeEventListener("abort", abandon2);
|
|
6983
7102
|
}
|
|
6984
7103
|
const plain3 = uplinkLink(made2.conn, (err) => {
|
|
6985
7104
|
this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
|
|
@@ -8128,7 +8247,7 @@ var UserDb = class {
|
|
|
8128
8247
|
joined(name2, extra);
|
|
8129
8248
|
group.push({ name: name2, gid: uid, members: [] });
|
|
8130
8249
|
passwd.push(row);
|
|
8131
|
-
const
|
|
8250
|
+
const fresh4 = !await this.fs.exists(home);
|
|
8132
8251
|
await this.fs.mkdir(home, { recursive: true });
|
|
8133
8252
|
try {
|
|
8134
8253
|
await this.provision(row, this.umaskFor(options), shared2);
|
|
@@ -8136,7 +8255,7 @@ var UserDb = class {
|
|
|
8136
8255
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
8137
8256
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
8138
8257
|
} catch (err) {
|
|
8139
|
-
if (
|
|
8258
|
+
if (fresh4) await this.fs.rm(home, { recursive: true, force: true });
|
|
8140
8259
|
throw err;
|
|
8141
8260
|
}
|
|
8142
8261
|
remember();
|
|
@@ -8220,7 +8339,7 @@ var UserDb = class {
|
|
|
8220
8339
|
};
|
|
8221
8340
|
|
|
8222
8341
|
// src/client/protocol.ts
|
|
8223
|
-
var PROTOCOL = [1,
|
|
8342
|
+
var PROTOCOL = [1, 18];
|
|
8224
8343
|
var FS_METHODS = [
|
|
8225
8344
|
"readFile",
|
|
8226
8345
|
"writeFile",
|
|
@@ -8480,8 +8599,10 @@ function hostRegistries(send2, gone, port) {
|
|
|
8480
8599
|
const cdps = cdpRegistry(send2, gone);
|
|
8481
8600
|
const facades = facadeRegistry(send2, gone);
|
|
8482
8601
|
const requests = requestBinding(send2, port);
|
|
8602
|
+
const devices = deviceBinding(send2, port);
|
|
8483
8603
|
return {
|
|
8484
8604
|
requests,
|
|
8605
|
+
devices,
|
|
8485
8606
|
answer(l, req) {
|
|
8486
8607
|
if (req.op === "serve-cdp") return cdps.serve(l, req);
|
|
8487
8608
|
if (req.op === "unserve-cdp") return cdps.drop(req.registration);
|
|
@@ -8490,6 +8611,7 @@ function hostRegistries(send2, gone, port) {
|
|
|
8490
8611
|
},
|
|
8491
8612
|
close() {
|
|
8492
8613
|
requests.close();
|
|
8614
|
+
devices.close();
|
|
8493
8615
|
cdps.close();
|
|
8494
8616
|
facades.close();
|
|
8495
8617
|
}
|
|
@@ -8603,6 +8725,28 @@ function answered(pending, req, reply, detach) {
|
|
|
8603
8725
|
}
|
|
8604
8726
|
);
|
|
8605
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
|
+
}
|
|
8606
8750
|
function serveClient(port, host) {
|
|
8607
8751
|
let state = "new";
|
|
8608
8752
|
let self;
|
|
@@ -8636,9 +8780,9 @@ function serveClient(port, host) {
|
|
|
8636
8780
|
ended.resolve();
|
|
8637
8781
|
}
|
|
8638
8782
|
async function hello(message3) {
|
|
8639
|
-
const
|
|
8640
|
-
if (
|
|
8641
|
-
send2({ hello: { protocol: PROTOCOL, error:
|
|
8783
|
+
const refused5 = await helloRefused(host, hosts, message3);
|
|
8784
|
+
if (refused5) {
|
|
8785
|
+
send2({ hello: { protocol: PROTOCOL, error: refused5 } });
|
|
8642
8786
|
detach();
|
|
8643
8787
|
return;
|
|
8644
8788
|
}
|
|
@@ -8922,7 +9066,7 @@ function satisfies(version, range2) {
|
|
|
8922
9066
|
}
|
|
8923
9067
|
|
|
8924
9068
|
// src/version.ts
|
|
8925
|
-
var KERNEL_VERSION = true ? "1.
|
|
9069
|
+
var KERNEL_VERSION = true ? "1.183.0" : "0.0.0-dev";
|
|
8926
9070
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8927
9071
|
function procVersion() {
|
|
8928
9072
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9214,10 +9358,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
|
|
|
9214
9358
|
const own = commandsOf(pkg, manifest);
|
|
9215
9359
|
const named = new Set(own.map((command) => command.name));
|
|
9216
9360
|
const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
|
|
9217
|
-
const
|
|
9361
|
+
const refused5 = engineRefusal(manifest, kernel);
|
|
9218
9362
|
const name2 = pkg.slice(root.length + 1);
|
|
9219
9363
|
const dependency = direct?.has(name2) === false;
|
|
9220
|
-
const marks = { ...
|
|
9364
|
+
const marks = { ...refused5 ? { refused: refused5 } : {}, ...dependency ? { dependency } : {} };
|
|
9221
9365
|
out.push({
|
|
9222
9366
|
name: name2,
|
|
9223
9367
|
pkg,
|
|
@@ -9249,7 +9393,7 @@ function winners(tier) {
|
|
|
9249
9393
|
}
|
|
9250
9394
|
return out;
|
|
9251
9395
|
}
|
|
9252
|
-
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()) {
|
|
9253
9397
|
const found = /* @__PURE__ */ new Map();
|
|
9254
9398
|
const physical = physicalResolver(fs);
|
|
9255
9399
|
const tiers = (typeof modules === "string" ? [modules] : modules).map(
|
|
@@ -9263,7 +9407,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
|
|
|
9263
9407
|
const dependencies = [];
|
|
9264
9408
|
for (const [index, roots] of tiers.entries()) {
|
|
9265
9409
|
const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
|
|
9266
|
-
if (
|
|
9410
|
+
if (refused5) refused5.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
|
|
9267
9411
|
await take(tier.filter((entry) => !entry.dependency));
|
|
9268
9412
|
dependencies.push(tier.filter((entry) => entry.dependency));
|
|
9269
9413
|
}
|
|
@@ -9867,9 +10011,9 @@ var TAGS_VERSION = 1;
|
|
|
9867
10011
|
async function readTags(fs) {
|
|
9868
10012
|
const text = await fs.readFile(TAGS_PATH).catch(() => "");
|
|
9869
10013
|
try {
|
|
9870
|
-
const
|
|
9871
|
-
if (!
|
|
9872
|
-
const { v: _, ...tables } =
|
|
10014
|
+
const parsed3 = JSON.parse(text);
|
|
10015
|
+
if (!parsed3 || typeof parsed3 !== "object") return {};
|
|
10016
|
+
const { v: _, ...tables } = parsed3;
|
|
9873
10017
|
return tables;
|
|
9874
10018
|
} catch {
|
|
9875
10019
|
return {};
|
|
@@ -9939,27 +10083,27 @@ var Crontabs = class {
|
|
|
9939
10083
|
const json5 = JSON.stringify({ v: TAGS_VERSION, ...kept });
|
|
9940
10084
|
if (json5 === await fs.readFile(TAGS_PATH).catch(() => "")) return;
|
|
9941
10085
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
9942
|
-
const
|
|
9943
|
-
await fs.writeFile(
|
|
9944
|
-
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);
|
|
9945
10089
|
}
|
|
9946
10090
|
async install(path, target, text, caller) {
|
|
9947
10091
|
if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
|
|
9948
10092
|
const { errors } = parseCrontab(text, path);
|
|
9949
10093
|
if (errors.length > 0) {
|
|
9950
|
-
const
|
|
9951
|
-
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") };
|
|
9952
10096
|
}
|
|
9953
10097
|
const { fs, owners } = this.o;
|
|
9954
10098
|
if (!await fs.exists(SPOOL_DIR)) {
|
|
9955
10099
|
await fs.mkdir(SPOOL_DIR, { recursive: true });
|
|
9956
10100
|
if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
|
|
9957
10101
|
}
|
|
9958
|
-
const
|
|
10102
|
+
const fresh4 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
|
|
9959
10103
|
const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
|
|
9960
10104
|
await this.retag(path, text, caller);
|
|
9961
|
-
await fs.writeFile(
|
|
9962
|
-
await fs.rename(
|
|
10105
|
+
await fs.writeFile(fresh4, text, owner);
|
|
10106
|
+
await fs.rename(fresh4, path);
|
|
9963
10107
|
return { text: "" };
|
|
9964
10108
|
}
|
|
9965
10109
|
};
|
|
@@ -10280,15 +10424,15 @@ var UnlinkedKeeper = class {
|
|
|
10280
10424
|
const releases = [];
|
|
10281
10425
|
const take = async () => {
|
|
10282
10426
|
for (; ; ) {
|
|
10283
|
-
const
|
|
10284
|
-
if (
|
|
10285
|
-
|
|
10286
|
-
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])) {
|
|
10287
10431
|
for (const release of releases.splice(0)) release();
|
|
10288
|
-
|
|
10289
|
-
|
|
10432
|
+
fresh4.push(...paths.splice(0));
|
|
10433
|
+
fresh4.sort();
|
|
10290
10434
|
}
|
|
10291
|
-
for (const path of
|
|
10435
|
+
for (const path of fresh4) {
|
|
10292
10436
|
paths.push(path);
|
|
10293
10437
|
releases.push(await this.gate.acquire(path));
|
|
10294
10438
|
}
|
|
@@ -10684,10 +10828,10 @@ var Output = class {
|
|
|
10684
10828
|
};
|
|
10685
10829
|
function readStamp(text) {
|
|
10686
10830
|
try {
|
|
10687
|
-
const
|
|
10688
|
-
const jobs =
|
|
10689
|
-
const reboot =
|
|
10690
|
-
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 : {};
|
|
10691
10835
|
return { jobs: { ...jobs }, reboot: { ...reboot }, when: { ...when } };
|
|
10692
10836
|
} catch {
|
|
10693
10837
|
return { jobs: {}, reboot: {}, when: {} };
|
|
@@ -10807,10 +10951,10 @@ var CronService = class {
|
|
|
10807
10951
|
const text = JSON.stringify(stamp);
|
|
10808
10952
|
const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
|
|
10809
10953
|
if (old === text || old === void 0 && text === EMPTY_STAMP) return;
|
|
10810
|
-
const
|
|
10954
|
+
const fresh4 = `${STAMP_PATH}.new`;
|
|
10811
10955
|
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
10812
|
-
await fs.writeFile(
|
|
10813
|
-
await fs.rename(
|
|
10956
|
+
await fs.writeFile(fresh4, text, owner);
|
|
10957
|
+
await fs.rename(fresh4, STAMP_PATH);
|
|
10814
10958
|
}
|
|
10815
10959
|
async load() {
|
|
10816
10960
|
const { fs } = this.host;
|
|
@@ -10863,10 +11007,10 @@ var CronService = class {
|
|
|
10863
11007
|
await this.once(path, text, errors);
|
|
10864
11008
|
return table;
|
|
10865
11009
|
}
|
|
10866
|
-
async once(path, version,
|
|
11010
|
+
async once(path, version, lines3) {
|
|
10867
11011
|
if (this.reported.get(path) === version) return;
|
|
10868
11012
|
this.reported.set(path, version);
|
|
10869
|
-
for (const line of
|
|
11013
|
+
for (const line of lines3) await this.error(line);
|
|
10870
11014
|
}
|
|
10871
11015
|
async error(line) {
|
|
10872
11016
|
if (this.stopping.signal.aborted) return;
|
|
@@ -11100,8 +11244,8 @@ function canonicalHost2(host) {
|
|
|
11100
11244
|
function localV4(a, b) {
|
|
11101
11245
|
return a === 127 || a === 0 || a === 169 && b === 254;
|
|
11102
11246
|
}
|
|
11103
|
-
function hextets(
|
|
11104
|
-
const [head, tail2, extra] =
|
|
11247
|
+
function hextets(address2) {
|
|
11248
|
+
const [head, tail2, extra] = address2.split("::");
|
|
11105
11249
|
if (extra !== void 0) return void 0;
|
|
11106
11250
|
const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
11107
11251
|
const front = part(head);
|
|
@@ -11109,8 +11253,8 @@ function hextets(address) {
|
|
|
11109
11253
|
const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
11110
11254
|
return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
|
|
11111
11255
|
}
|
|
11112
|
-
function localV6(
|
|
11113
|
-
const g = hextets(
|
|
11256
|
+
function localV6(address2) {
|
|
11257
|
+
const g = hextets(address2);
|
|
11114
11258
|
if (!g) return true;
|
|
11115
11259
|
if ((g[0] & 65472) === 65152) return true;
|
|
11116
11260
|
const zeroPrefix = g.slice(0, 5).every((x) => x === 0);
|
|
@@ -11483,8 +11627,8 @@ var RealmProxy = class {
|
|
|
11483
11627
|
}
|
|
11484
11628
|
async connect(conn, incoming, req, idle2) {
|
|
11485
11629
|
const target = tunnelTarget(req);
|
|
11486
|
-
const
|
|
11487
|
-
if (
|
|
11630
|
+
const refused5 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
11631
|
+
if (refused5) throw refused5;
|
|
11488
11632
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
11489
11633
|
try {
|
|
11490
11634
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -11928,15 +12072,15 @@ async function followLinks(fs, path) {
|
|
|
11928
12072
|
}
|
|
11929
12073
|
var NOBODY = { ruid: -1, euid: -1, suid: -1, rgid: -1, egid: -1, sgid: -1, groups: [] };
|
|
11930
12074
|
var EMPTY2 = new Uint8Array(0);
|
|
11931
|
-
function
|
|
12075
|
+
function quiet2(promise) {
|
|
11932
12076
|
return promise.catch(() => void 0);
|
|
11933
12077
|
}
|
|
11934
12078
|
async function readsAs2(fs, cred, target) {
|
|
11935
|
-
const walked = await
|
|
12079
|
+
const walked = await quiet2(access(fs, cred, target, R_OK, { directory: false }));
|
|
11936
12080
|
return walked !== void 0 && walked.denied === void 0 && !walked.missing;
|
|
11937
12081
|
}
|
|
11938
12082
|
async function shownTarget(fs, target) {
|
|
11939
|
-
const st = await
|
|
12083
|
+
const st = await quiet2(fs.stat(target));
|
|
11940
12084
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
11941
12085
|
}
|
|
11942
12086
|
async function madeFor(owner, virtual) {
|
|
@@ -12259,10 +12403,10 @@ var FreshModules = class {
|
|
|
12259
12403
|
this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
|
|
12260
12404
|
}
|
|
12261
12405
|
forget(pid) {
|
|
12262
|
-
for (const [path,
|
|
12406
|
+
for (const [path, fresh4] of this.fresh) if (fresh4.pid === pid) this.fresh.delete(path);
|
|
12263
12407
|
}
|
|
12264
|
-
drop(
|
|
12265
|
-
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);
|
|
12266
12410
|
}
|
|
12267
12411
|
moved(from, to) {
|
|
12268
12412
|
const source = normalizePath(from);
|
|
@@ -12271,10 +12415,10 @@ var FreshModules = class {
|
|
|
12271
12415
|
for (const key of [...this.fresh.keys()]) {
|
|
12272
12416
|
if (key === target || within6(key, target)) this.fresh.delete(key);
|
|
12273
12417
|
}
|
|
12274
|
-
for (const [key,
|
|
12418
|
+
for (const [key, fresh4] of [...this.fresh]) {
|
|
12275
12419
|
if (key !== source && !within6(key, source)) continue;
|
|
12276
12420
|
this.fresh.delete(key);
|
|
12277
|
-
this.fresh.set(target + key.slice(source.length),
|
|
12421
|
+
this.fresh.set(target + key.slice(source.length), fresh4);
|
|
12278
12422
|
}
|
|
12279
12423
|
}
|
|
12280
12424
|
wrap(fs) {
|
|
@@ -12284,15 +12428,15 @@ var FreshModules = class {
|
|
|
12284
12428
|
};
|
|
12285
12429
|
const decide = async (given, known) => {
|
|
12286
12430
|
const path = normalizePath(given);
|
|
12287
|
-
const
|
|
12288
|
-
if (!
|
|
12431
|
+
const fresh4 = this.fresh.get(path);
|
|
12432
|
+
if (!fresh4) return;
|
|
12289
12433
|
const bytes = known && known.length >= MAGIC.length ? known : await head(path);
|
|
12290
|
-
const
|
|
12291
|
-
|
|
12292
|
-
if (
|
|
12434
|
+
const want2 = isModule(bytes) ? 73 & ~fresh4.umask : 0;
|
|
12435
|
+
fresh4.setup = false;
|
|
12436
|
+
if (want2 === fresh4.added) return;
|
|
12293
12437
|
const mode = (await fs.stat(path)).mode & 4095;
|
|
12294
|
-
await fs.chmod(path, mode & ~
|
|
12295
|
-
|
|
12438
|
+
await fs.chmod(path, mode & ~fresh4.added | want2);
|
|
12439
|
+
fresh4.added = want2;
|
|
12296
12440
|
};
|
|
12297
12441
|
const overrides = {
|
|
12298
12442
|
writeFile: async (path, content, owner) => {
|
|
@@ -12305,9 +12449,9 @@ var FreshModules = class {
|
|
|
12305
12449
|
}
|
|
12306
12450
|
},
|
|
12307
12451
|
chmod: async (path, mode) => {
|
|
12308
|
-
const
|
|
12309
|
-
if (
|
|
12310
|
-
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);
|
|
12311
12455
|
await fs.chmod(path, mode);
|
|
12312
12456
|
},
|
|
12313
12457
|
rm: async (path, options) => {
|
|
@@ -12324,16 +12468,16 @@ var FreshModules = class {
|
|
|
12324
12468
|
link: async (from, to, follow2) => {
|
|
12325
12469
|
await fs.link(from, to, follow2);
|
|
12326
12470
|
const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
|
|
12327
|
-
const
|
|
12328
|
-
if (
|
|
12471
|
+
const fresh4 = this.fresh.get(normalizePath(source));
|
|
12472
|
+
if (fresh4) this.fresh.set(normalizePath(to), fresh4);
|
|
12329
12473
|
}
|
|
12330
12474
|
};
|
|
12331
12475
|
const { append } = fs;
|
|
12332
12476
|
if (append) {
|
|
12333
12477
|
overrides.append = async (path, bytes, options) => {
|
|
12334
|
-
const
|
|
12478
|
+
const fresh4 = this.fresh.has(normalizePath(path));
|
|
12335
12479
|
const end = await append.call(fs, path, bytes, options);
|
|
12336
|
-
if (
|
|
12480
|
+
if (fresh4 && end - bytes.length < MAGIC.length) await decide(path);
|
|
12337
12481
|
return end;
|
|
12338
12482
|
};
|
|
12339
12483
|
}
|
|
@@ -12660,7 +12804,7 @@ var FsWatchers = class {
|
|
|
12660
12804
|
};
|
|
12661
12805
|
const content = (run2, types) => async (...args) => {
|
|
12662
12806
|
const at = normalizePath(args[0]);
|
|
12663
|
-
const
|
|
12807
|
+
const fresh4 = this.covers(at) && !await fs.exists(at).catch(() => true);
|
|
12664
12808
|
let done5 = false;
|
|
12665
12809
|
try {
|
|
12666
12810
|
await run2(...args);
|
|
@@ -12669,11 +12813,11 @@ var FsWatchers = class {
|
|
|
12669
12813
|
const real = await realOf(at);
|
|
12670
12814
|
const paths = real === at ? [at] : [at, real];
|
|
12671
12815
|
this.report(paths);
|
|
12672
|
-
if (done5) for (const type of types(
|
|
12816
|
+
if (done5) for (const type of types(fresh4, args)) this.emit(type, paths);
|
|
12673
12817
|
}
|
|
12674
12818
|
};
|
|
12675
|
-
const written2 = (
|
|
12676
|
-
const overwritten = (
|
|
12819
|
+
const written2 = (fresh4) => fresh4 ? ["rename", "change"] : ["change"];
|
|
12820
|
+
const overwritten = (fresh4, args) => fresh4 || args[1].length === 0 ? written2(fresh4) : ["change", "change"];
|
|
12677
12821
|
const reshaping = (run2) => async (...args) => {
|
|
12678
12822
|
this.reshaped();
|
|
12679
12823
|
try {
|
|
@@ -12706,14 +12850,14 @@ var FsWatchers = class {
|
|
|
12706
12850
|
if (created4.length > 0 || !await fs.exists(ancestor)) created4.push(ancestor);
|
|
12707
12851
|
}
|
|
12708
12852
|
}
|
|
12709
|
-
const
|
|
12853
|
+
const fresh4 = !options?.recursive || this.typed.size > 0 && !await fs.exists(path);
|
|
12710
12854
|
let done5 = false;
|
|
12711
12855
|
try {
|
|
12712
12856
|
await fs.mkdir(path, options);
|
|
12713
12857
|
done5 = true;
|
|
12714
12858
|
} finally {
|
|
12715
12859
|
this.report([...created4, path]);
|
|
12716
|
-
if (done5) this.emit("rename",
|
|
12860
|
+
if (done5) this.emit("rename", fresh4 ? [...created4, path] : created4);
|
|
12717
12861
|
}
|
|
12718
12862
|
},
|
|
12719
12863
|
rm: reshaping(async (path, options) => {
|
|
@@ -13223,15 +13367,15 @@ function embedderFailure(err) {
|
|
|
13223
13367
|
return new KernelError(ERRNOS[String(name2)] ?? "EIO", lineOf2(err));
|
|
13224
13368
|
}
|
|
13225
13369
|
function headerOf(bytes) {
|
|
13226
|
-
let
|
|
13370
|
+
let parsed3;
|
|
13227
13371
|
try {
|
|
13228
|
-
|
|
13372
|
+
parsed3 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
13229
13373
|
} catch {
|
|
13230
13374
|
throw new KernelError("EINVAL", "the request header is not JSON");
|
|
13231
13375
|
}
|
|
13232
|
-
if (typeof
|
|
13376
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
13233
13377
|
throw new KernelError("EINVAL", "the request header is not an object");
|
|
13234
|
-
const { kind, ...options } =
|
|
13378
|
+
const { kind, ...options } = parsed3;
|
|
13235
13379
|
if (typeof kind !== "string" || kind.length > 64 || !KIND.test(kind))
|
|
13236
13380
|
throw new KernelError("EINVAL", "the request has no valid kind");
|
|
13237
13381
|
return { kind, options };
|
|
@@ -13658,6 +13802,966 @@ var EmbedderBroker = class {
|
|
|
13658
13802
|
}
|
|
13659
13803
|
};
|
|
13660
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
|
+
|
|
13661
14765
|
// src/kernel/exec-ticket.ts
|
|
13662
14766
|
var TICKET_ENV = "SLICC_SEALED_TICKET";
|
|
13663
14767
|
var TICKET_TTL_MS = 3e4;
|
|
@@ -14030,8 +15134,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
14030
15134
|
} finally {
|
|
14031
15135
|
clearTimeout(timer);
|
|
14032
15136
|
}
|
|
14033
|
-
const
|
|
14034
|
-
if (
|
|
15137
|
+
const refused5 = driverVersionError(hello.protocol);
|
|
15138
|
+
if (refused5) throw errnoError("EPROTO", refused5);
|
|
14035
15139
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
14036
15140
|
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
|
|
14037
15141
|
}
|
|
@@ -14077,7 +15181,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
14077
15181
|
var S_ISGID4 = 1024;
|
|
14078
15182
|
var ID = /^\d{1,10}$/;
|
|
14079
15183
|
var MASK = /^0*[0-7]{1,4}$/;
|
|
14080
|
-
var
|
|
15184
|
+
var MAX_ID3 = 4294967294;
|
|
14081
15185
|
function idOption(value2) {
|
|
14082
15186
|
return value2 === void 0 ? 0 : Number(value2);
|
|
14083
15187
|
}
|
|
@@ -14089,7 +15193,7 @@ function checkIds(options) {
|
|
|
14089
15193
|
for (const key of ["uid", "gid"]) {
|
|
14090
15194
|
const value2 = options[key];
|
|
14091
15195
|
if (value2 === void 0) continue;
|
|
14092
|
-
if (!ID.test(value2) || Number(value2) >
|
|
15196
|
+
if (!ID.test(value2) || Number(value2) > MAX_ID3) throw errnoError("EINVAL", `${key}=${value2}`);
|
|
14093
15197
|
}
|
|
14094
15198
|
}
|
|
14095
15199
|
function checkMasks(options) {
|
|
@@ -14101,7 +15205,7 @@ function checkMasks(options) {
|
|
|
14101
15205
|
}
|
|
14102
15206
|
}
|
|
14103
15207
|
}
|
|
14104
|
-
function
|
|
15208
|
+
function modeOf2(attr2, mount) {
|
|
14105
15209
|
const type = attr2.kind === "directory" ? 16384 : attr2.kind === "symlink" ? 40960 : attr2.kind === "fifo" ? 4096 : 32768;
|
|
14106
15210
|
const fallback = type | (attr2.kind === "directory" ? 493 : attr2.kind === "symlink" ? 511 : 420);
|
|
14107
15211
|
if (mount.caps.owners) return attr2.mode ?? fallback;
|
|
@@ -14262,9 +15366,9 @@ var HeldPaths = class extends Map {
|
|
|
14262
15366
|
this.unlinked.set(path, file);
|
|
14263
15367
|
}
|
|
14264
15368
|
keep(path, file) {
|
|
14265
|
-
const
|
|
14266
|
-
for (const id2 of
|
|
14267
|
-
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));
|
|
14268
15372
|
this.unlinked.set(path, file);
|
|
14269
15373
|
}
|
|
14270
15374
|
unkeep(path, file) {
|
|
@@ -14385,7 +15489,7 @@ function statOf2(attr2, path, mount) {
|
|
|
14385
15489
|
isSymbolicLink: attr2.kind === "symlink",
|
|
14386
15490
|
...attr2.kind === "fifo" ? { isFifo: true } : {},
|
|
14387
15491
|
size: attr2.size,
|
|
14388
|
-
mode:
|
|
15492
|
+
mode: modeOf2(attr2, mount),
|
|
14389
15493
|
mtime,
|
|
14390
15494
|
atime: attr2.atime === void 0 ? mtime : new Date(attr2.atime),
|
|
14391
15495
|
ctime: mtime,
|
|
@@ -15476,8 +16580,8 @@ var MountFs = class _MountFs {
|
|
|
15476
16580
|
}
|
|
15477
16581
|
this.writable(mount, real);
|
|
15478
16582
|
const { uid, gid, mode } = change;
|
|
15479
|
-
const
|
|
15480
|
-
const set3 =
|
|
16583
|
+
const fresh4 = change.kind === "new" && { uid, gid, mode };
|
|
16584
|
+
const set3 = fresh4 ? await this.driverOwner(found, real, fresh4, false) : change;
|
|
15481
16585
|
this.table.forget(mount, real);
|
|
15482
16586
|
const attrs = Object.entries({ uid: set3.uid, gid: set3.gid, mode: set3.mode });
|
|
15483
16587
|
await mount.conn.call({
|
|
@@ -15754,10 +16858,10 @@ async function runMkdir(entry, path, req) {
|
|
|
15754
16858
|
const plain3 = plainDirMode(entry, req.mode);
|
|
15755
16859
|
const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
|
|
15756
16860
|
const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
|
|
15757
|
-
const
|
|
16861
|
+
const fresh4 = plain3 !== void 0 && !await fs.exists(path);
|
|
15758
16862
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
15759
16863
|
await fixParents?.();
|
|
15760
|
-
if (
|
|
16864
|
+
if (fresh4) await fs.chmod(path, plain3);
|
|
15761
16865
|
return done2;
|
|
15762
16866
|
}
|
|
15763
16867
|
async function shownAs(entry, path, st) {
|
|
@@ -15969,10 +17073,10 @@ async function guard(g, path, req, cwd) {
|
|
|
15969
17073
|
}
|
|
15970
17074
|
async function opening(entry, path, mode) {
|
|
15971
17075
|
const g = guardOf(entry);
|
|
15972
|
-
const
|
|
17076
|
+
const want2 = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
|
|
15973
17077
|
const created4 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
|
|
15974
|
-
if (g &&
|
|
15975
|
-
await need(g, "open", path,
|
|
17078
|
+
if (g && want2 !== 0 && !created4) {
|
|
17079
|
+
await need(g, "open", path, want2, { directory: false });
|
|
15976
17080
|
}
|
|
15977
17081
|
}
|
|
15978
17082
|
async function stillThere(entry, path, req) {
|
|
@@ -15996,7 +17100,7 @@ async function holdOp(entry, path, req) {
|
|
|
15996
17100
|
await entry.source?.open(path, real);
|
|
15997
17101
|
await stillThere(entry, path, req);
|
|
15998
17102
|
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
15999
|
-
const fid = req.ino === void 0 ? void 0 : await
|
|
17103
|
+
const fid = req.ino === void 0 ? void 0 : await idOf2(entry, real ?? path);
|
|
16000
17104
|
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
16001
17105
|
return json2(await heldOpen(entry, path, real, req, fid));
|
|
16002
17106
|
}
|
|
@@ -16010,7 +17114,7 @@ async function heldOpen(entry, path, real, req, fid) {
|
|
|
16010
17114
|
await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
|
|
16011
17115
|
return real !== void 0 && real !== path ? { handle, real } : handle;
|
|
16012
17116
|
}
|
|
16013
|
-
async function
|
|
17117
|
+
async function idOf2(entry, path) {
|
|
16014
17118
|
return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
|
|
16015
17119
|
}
|
|
16016
17120
|
async function ownEntry(entry, path, real) {
|
|
@@ -16082,8 +17186,8 @@ async function guarded(entry, path, req) {
|
|
|
16082
17186
|
return { paths };
|
|
16083
17187
|
} catch (err) {
|
|
16084
17188
|
const refusal2 = err.code === "EACCES";
|
|
16085
|
-
const
|
|
16086
|
-
return { refused:
|
|
17189
|
+
const refused5 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
|
|
17190
|
+
return { refused: refused5 };
|
|
16087
17191
|
}
|
|
16088
17192
|
}
|
|
16089
17193
|
async function copyOp(entry, path, req) {
|
|
@@ -16184,7 +17288,7 @@ async function bound(entry, path, handle) {
|
|
|
16184
17288
|
const now = await entry.fs.stat(path).catch(() => void 0);
|
|
16185
17289
|
if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
|
|
16186
17290
|
throw syncError("ENOENT", path);
|
|
16187
|
-
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;
|
|
16188
17292
|
if (same3) {
|
|
16189
17293
|
entry.trust?.(handle, bound3.doubt);
|
|
16190
17294
|
return;
|
|
@@ -16279,13 +17383,13 @@ async function created2(entry, req) {
|
|
|
16279
17383
|
return seen === void 0 ? looked : json2({ ...looked.json, seen });
|
|
16280
17384
|
}
|
|
16281
17385
|
var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
|
|
16282
|
-
async function rmPermitted(entry, path, req,
|
|
17386
|
+
async function rmPermitted(entry, path, req, refused5) {
|
|
16283
17387
|
const st = await entry.fs.lstat(path).catch(() => void 0);
|
|
16284
|
-
if (!st?.isDirectory) return
|
|
17388
|
+
if (!st?.isDirectory) return refused5;
|
|
16285
17389
|
const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
|
|
16286
17390
|
if ("refused" in unlisted) return unlisted.refused;
|
|
16287
17391
|
const names2 = await entry.fs.readdir(path).catch(() => void 0);
|
|
16288
|
-
if (!names2) return
|
|
17392
|
+
if (!names2) return refused5;
|
|
16289
17393
|
let first;
|
|
16290
17394
|
for (const name2 of names2) {
|
|
16291
17395
|
const child = await dispatchOne(entry, {
|
|
@@ -16336,14 +17440,14 @@ async function newDevName(entry, path, req) {
|
|
|
16336
17440
|
const named = NAMING.has(req.op);
|
|
16337
17441
|
if (!named && !CREATIONS.has(req.op)) return void 0;
|
|
16338
17442
|
const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
|
|
16339
|
-
const
|
|
16340
|
-
if (!
|
|
17443
|
+
const refused5 = devCreation(target, entry.cred?.());
|
|
17444
|
+
if (!refused5) return void 0;
|
|
16341
17445
|
const there = await entry.fs.lstat(target).then(
|
|
16342
17446
|
() => true,
|
|
16343
17447
|
() => false
|
|
16344
17448
|
);
|
|
16345
17449
|
if (there) return void 0;
|
|
16346
|
-
return { ok: false, errno:
|
|
17450
|
+
return { ok: false, errno: refused5, message: `${target}: no new names in /dev or /proc` };
|
|
16347
17451
|
}
|
|
16348
17452
|
var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
|
|
16349
17453
|
async function linkFree(fs, path) {
|
|
@@ -16454,13 +17558,13 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
|
16454
17558
|
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
16455
17559
|
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
16456
17560
|
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
16457
|
-
var
|
|
17561
|
+
var encoder4 = new TextEncoder();
|
|
16458
17562
|
var decoder2 = new TextDecoder();
|
|
16459
17563
|
function encodeSabResult(result) {
|
|
16460
17564
|
if (!result.ok) {
|
|
16461
17565
|
return {
|
|
16462
17566
|
status: SAB_STATUS_ERRNO,
|
|
16463
|
-
payload:
|
|
17567
|
+
payload: encoder4.encode(
|
|
16464
17568
|
JSON.stringify({
|
|
16465
17569
|
errno: result.errno,
|
|
16466
17570
|
message: result.message,
|
|
@@ -16475,7 +17579,7 @@ function encodeSabResult(result) {
|
|
|
16475
17579
|
case "bytes":
|
|
16476
17580
|
return { status: SAB_STATUS_BYTES, payload: result.bytes };
|
|
16477
17581
|
case "json":
|
|
16478
|
-
return { status: SAB_STATUS_JSON, payload:
|
|
17582
|
+
return { status: SAB_STATUS_JSON, payload: encoder4.encode(JSON.stringify(result.json)) };
|
|
16479
17583
|
default:
|
|
16480
17584
|
return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
|
|
16481
17585
|
}
|
|
@@ -16802,9 +17906,9 @@ var Pages = class {
|
|
|
16802
17906
|
const page = (start2 + at) / PAGE;
|
|
16803
17907
|
if (this.pages.has(page)) continue;
|
|
16804
17908
|
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
16805
|
-
const
|
|
16806
|
-
|
|
16807
|
-
this.pages.set(page,
|
|
17909
|
+
const fresh4 = this.take(part.length);
|
|
17910
|
+
fresh4.set(part);
|
|
17911
|
+
this.pages.set(page, fresh4);
|
|
16808
17912
|
}
|
|
16809
17913
|
}
|
|
16810
17914
|
read(at, length2) {
|
|
@@ -17015,16 +18119,16 @@ var pinnedRoot;
|
|
|
17015
18119
|
var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
|
|
17016
18120
|
function mountTable(mounts = []) {
|
|
17017
18121
|
const root = pinnedRoot ?? rootKind();
|
|
17018
|
-
const
|
|
18122
|
+
const lines3 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
|
|
17019
18123
|
for (const m of mounts) {
|
|
17020
18124
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
17021
18125
|
if (m.state !== "ok") options.push(m.state);
|
|
17022
18126
|
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
17023
|
-
|
|
18127
|
+
lines3.push(
|
|
17024
18128
|
`${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
17025
18129
|
);
|
|
17026
18130
|
}
|
|
17027
|
-
return `${
|
|
18131
|
+
return `${lines3.join("\n")}
|
|
17028
18132
|
`;
|
|
17029
18133
|
}
|
|
17030
18134
|
|
|
@@ -18059,7 +19163,7 @@ function recordRefusal(opening2) {
|
|
|
18059
19163
|
const found = headerClass(opening2);
|
|
18060
19164
|
return found && new KernelError("EPROTO", `${found[0]}: ${found[1]}`);
|
|
18061
19165
|
}
|
|
18062
|
-
function
|
|
19166
|
+
function refused3(engine, session, err, opening2) {
|
|
18063
19167
|
const flags2 = session.verifyResult;
|
|
18064
19168
|
const code = verifyCode(flags2);
|
|
18065
19169
|
if (code && flags2 !== -1)
|
|
@@ -18080,7 +19184,7 @@ async function handshake(conn, o, session, inbox) {
|
|
|
18080
19184
|
done5 = session.handshake();
|
|
18081
19185
|
} catch (err) {
|
|
18082
19186
|
await send(conn, session).catch(() => void 0);
|
|
18083
|
-
throw
|
|
19187
|
+
throw refused3(o.engine, session, err, inbox.opening);
|
|
18084
19188
|
}
|
|
18085
19189
|
await send(conn, session);
|
|
18086
19190
|
if (done5) return;
|
|
@@ -18531,10 +19635,10 @@ async function anchorFor(engine, chain2, host, found) {
|
|
|
18531
19635
|
var FRAGMENT = 16;
|
|
18532
19636
|
var MIN_VALUE = 8;
|
|
18533
19637
|
var MIN_BASE64 = 8;
|
|
18534
|
-
var
|
|
19638
|
+
var encoder5 = new TextEncoder();
|
|
18535
19639
|
var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
18536
19640
|
function utf8(text) {
|
|
18537
|
-
return
|
|
19641
|
+
return encoder5.encode(text);
|
|
18538
19642
|
}
|
|
18539
19643
|
function base64(bytes, padded = true) {
|
|
18540
19644
|
let out = "";
|
|
@@ -18608,14 +19712,73 @@ function encodings(bytes) {
|
|
|
18608
19712
|
}
|
|
18609
19713
|
return [...standard, ...standard.map(urlSafe)];
|
|
18610
19714
|
}
|
|
18611
|
-
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)) {
|
|
18612
19768
|
const raw2 = utf8(value2);
|
|
18613
19769
|
const seen = /* @__PURE__ */ new Set([value2]);
|
|
18614
19770
|
const out = [];
|
|
18615
|
-
if (raw2.length < FRAGMENT) out.push({ bytes: raw2, form: "raw" });
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
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
|
+
});
|
|
18619
19782
|
const add = (text, form) => {
|
|
18620
19783
|
if (seen.has(text)) return;
|
|
18621
19784
|
seen.add(text);
|
|
@@ -18748,12 +19911,19 @@ function prepare(secrets, sameLength) {
|
|
|
18748
19911
|
const ids = /* @__PURE__ */ new Map();
|
|
18749
19912
|
const wrapIds = /* @__PURE__ */ new Map();
|
|
18750
19913
|
const values = secrets.map((secret) => utf8(secret.value));
|
|
19914
|
+
const splits = [];
|
|
18751
19915
|
secrets.forEach((secret, index) => {
|
|
18752
19916
|
if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
|
|
18753
19917
|
throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
|
|
18754
19918
|
}
|
|
18755
|
-
|
|
18756
|
-
|
|
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
|
+
};
|
|
18757
19927
|
register(main, sources, ids, shape.bytes, source);
|
|
18758
19928
|
if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
|
|
18759
19929
|
}
|
|
@@ -18764,7 +19934,16 @@ function prepare(secrets, sameLength) {
|
|
|
18764
19934
|
utf8(secret.placeholder),
|
|
18765
19935
|
utf8(`[redacted:${secret.name}]`)
|
|
18766
19936
|
]);
|
|
18767
|
-
return {
|
|
19937
|
+
return {
|
|
19938
|
+
secrets,
|
|
19939
|
+
splits,
|
|
19940
|
+
main,
|
|
19941
|
+
wrap,
|
|
19942
|
+
sources,
|
|
19943
|
+
wrapSources,
|
|
19944
|
+
replacements,
|
|
19945
|
+
sameLength
|
|
19946
|
+
};
|
|
18768
19947
|
}
|
|
18769
19948
|
function filled(input, start2, end) {
|
|
18770
19949
|
const out = new Uint8Array(end - start2);
|
|
@@ -18795,14 +19974,16 @@ function resolve(c, input, start2, end, source) {
|
|
|
18795
19974
|
growing: false
|
|
18796
19975
|
};
|
|
18797
19976
|
}
|
|
18798
|
-
const
|
|
19977
|
+
const split3 = c.splits[source.secret];
|
|
19978
|
+
const value2 = split3.pieces[source.piece];
|
|
18799
19979
|
let stop = end;
|
|
18800
19980
|
let at = source.offset + FRAGMENT;
|
|
18801
19981
|
while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
|
|
18802
19982
|
stop++;
|
|
18803
19983
|
at++;
|
|
18804
19984
|
}
|
|
18805
|
-
const
|
|
19985
|
+
const whole = split3.whole && source.offset === 0 && at === value2.length;
|
|
19986
|
+
const form = whole ? "raw" : "fragment";
|
|
18806
19987
|
const growing = stop === input.length && at < value2.length;
|
|
18807
19988
|
return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
|
|
18808
19989
|
}
|
|
@@ -18956,9 +20137,9 @@ function compile(secrets, options = {}) {
|
|
|
18956
20137
|
// src/kernel/scrub-file.ts
|
|
18957
20138
|
var SCRUB_IDLE_MS = 50;
|
|
18958
20139
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
18959
|
-
var
|
|
20140
|
+
var encoder6 = new TextEncoder();
|
|
18960
20141
|
function scrubberOf(secrets, sameLength = false) {
|
|
18961
|
-
const usable3 = secrets.filter((s) =>
|
|
20142
|
+
const usable3 = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
|
|
18962
20143
|
return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
|
|
18963
20144
|
}
|
|
18964
20145
|
async function writeAll2(file, bytes) {
|
|
@@ -18982,7 +20163,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
18982
20163
|
let timer;
|
|
18983
20164
|
let failed4;
|
|
18984
20165
|
const waiters = /* @__PURE__ */ new Set();
|
|
18985
|
-
const
|
|
20166
|
+
const quiet4 = () => !busy && pending === 0;
|
|
18986
20167
|
const wake = () => {
|
|
18987
20168
|
for (const waiter of [...waiters]) waiter();
|
|
18988
20169
|
};
|
|
@@ -18999,7 +20180,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
18999
20180
|
if (failed4 === void 0) failed4 = err;
|
|
19000
20181
|
}).finally(() => {
|
|
19001
20182
|
pending -= 1;
|
|
19002
|
-
if (
|
|
20183
|
+
if (quiet4()) wake();
|
|
19003
20184
|
});
|
|
19004
20185
|
};
|
|
19005
20186
|
const settling = (signal) => new Promise((resolve2, reject) => {
|
|
@@ -19017,7 +20198,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19017
20198
|
signal?.addEventListener("abort", stop, { once: true });
|
|
19018
20199
|
});
|
|
19019
20200
|
const settled = async () => {
|
|
19020
|
-
while (!
|
|
20201
|
+
while (!quiet4()) await settling();
|
|
19021
20202
|
};
|
|
19022
20203
|
const changed = async (signal) => {
|
|
19023
20204
|
const round = new AbortController();
|
|
@@ -19033,7 +20214,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19033
20214
|
};
|
|
19034
20215
|
const turn = async (signal) => {
|
|
19035
20216
|
for (let waited = false; ; waited = true) {
|
|
19036
|
-
if (
|
|
20217
|
+
if (quiet4() && (pollFile(file).writable || !file.changed)) {
|
|
19037
20218
|
busy = true;
|
|
19038
20219
|
return;
|
|
19039
20220
|
}
|
|
@@ -19062,13 +20243,14 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19062
20243
|
...bound2(file, "stat"),
|
|
19063
20244
|
poll: () => {
|
|
19064
20245
|
const state = pollFile(file);
|
|
19065
|
-
return
|
|
20246
|
+
return quiet4() ? state : { ...state, writable: false };
|
|
19066
20247
|
},
|
|
19067
20248
|
changed,
|
|
19068
20249
|
...file.tty ? { tty: file.tty } : {},
|
|
19069
20250
|
identity: file.identity ?? file,
|
|
19070
20251
|
...file.clipboard ? { clipboard: file.clipboard } : {},
|
|
19071
20252
|
...file.embedder ? { embedder: file.embedder } : {},
|
|
20253
|
+
...file.device ? { device: file.device } : {},
|
|
19072
20254
|
...file.write ? {
|
|
19073
20255
|
write: async (bytes, signal) => {
|
|
19074
20256
|
await turn(signal);
|
|
@@ -19080,7 +20262,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19080
20262
|
await send2(stream.push(taken), signal);
|
|
19081
20263
|
} finally {
|
|
19082
20264
|
busy = false;
|
|
19083
|
-
if (
|
|
20265
|
+
if (quiet4()) wake();
|
|
19084
20266
|
}
|
|
19085
20267
|
timer = setTimeout(release, idleMs);
|
|
19086
20268
|
return taken.length;
|
|
@@ -19139,9 +20321,9 @@ function scrubStdio(fds, scrubber) {
|
|
|
19139
20321
|
// src/kernel/net/sealed-scrubs.ts
|
|
19140
20322
|
function addTo(tree2, host, secrets) {
|
|
19141
20323
|
const entry = tree2.get(host) ?? { secrets: [], version: 0, scrubber: void 0 };
|
|
19142
|
-
const
|
|
19143
|
-
if (
|
|
19144
|
-
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];
|
|
19145
20327
|
entry.version += 1;
|
|
19146
20328
|
entry.scrubber = void 0;
|
|
19147
20329
|
tree2.set(host, entry);
|
|
@@ -19304,7 +20486,7 @@ var HEADER = new RegExp(`^header:${TOKEN3}$`);
|
|
|
19304
20486
|
var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
19305
20487
|
var TARGET = /^[\x21-\x7e]+$/;
|
|
19306
20488
|
var SESSION_EXIT = /^(tailscale|device:[A-Za-z0-9._:-]+)$/;
|
|
19307
|
-
var
|
|
20489
|
+
var HOST2 = /^[a-z0-9.:-]+$/;
|
|
19308
20490
|
var UNSAFE = /[\r\n\0]/;
|
|
19309
20491
|
var ANCHOR_ID = /^[A-Za-z0-9+/]{43}=$/;
|
|
19310
20492
|
function verifiedByOk(f) {
|
|
@@ -19358,7 +20540,7 @@ function scramOk(f) {
|
|
|
19358
20540
|
return texts.every((t) => typeof t === "string" && SCRAM_TEXT.test(t));
|
|
19359
20541
|
}
|
|
19360
20542
|
function hostOk(host) {
|
|
19361
|
-
return typeof host === "string" &&
|
|
20543
|
+
return typeof host === "string" && HOST2.test(host) && boundHost(host) === host;
|
|
19362
20544
|
}
|
|
19363
20545
|
function sessionAskable(f, now) {
|
|
19364
20546
|
if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
|
|
@@ -19448,20 +20630,20 @@ var ASK_FIELDS = /* @__PURE__ */ new Set([
|
|
|
19448
20630
|
"caller",
|
|
19449
20631
|
"requester"
|
|
19450
20632
|
]);
|
|
19451
|
-
var
|
|
20633
|
+
var encoder7 = new TextEncoder();
|
|
19452
20634
|
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
19453
20635
|
function parseSealed(bytes) {
|
|
19454
20636
|
try {
|
|
19455
20637
|
if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
|
|
19456
|
-
let
|
|
20638
|
+
let parsed3;
|
|
19457
20639
|
try {
|
|
19458
|
-
|
|
20640
|
+
parsed3 = JSON.parse(decoder3.decode(bytes));
|
|
19459
20641
|
} catch {
|
|
19460
20642
|
throw new KernelError("EINVAL");
|
|
19461
20643
|
}
|
|
19462
|
-
if (typeof
|
|
20644
|
+
if (typeof parsed3 !== "object" || parsed3 === null || Array.isArray(parsed3))
|
|
19463
20645
|
throw new KernelError("EINVAL");
|
|
19464
|
-
const req =
|
|
20646
|
+
const req = parsed3;
|
|
19465
20647
|
if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
|
|
19466
20648
|
throw new KernelError("EINVAL");
|
|
19467
20649
|
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
@@ -19557,7 +20739,7 @@ var SealedChannel = class {
|
|
|
19557
20739
|
await this.broker.tickets.record(req, reply, caller.pid);
|
|
19558
20740
|
if (this.inflight !== flight) return;
|
|
19559
20741
|
this.inflight = void 0;
|
|
19560
|
-
if (reply) this.reply =
|
|
20742
|
+
if (reply) this.reply = encoder7.encode(serialized(reply));
|
|
19561
20743
|
});
|
|
19562
20744
|
this.inflight = flight;
|
|
19563
20745
|
return length2;
|
|
@@ -19833,7 +21015,7 @@ function shown(text) {
|
|
|
19833
21015
|
const chars = [...safe];
|
|
19834
21016
|
return `'${chars.length > SHOWN_MAX ? `${chars.slice(0, SHOWN_MAX - 1).join("")}\u2026` : safe}'`;
|
|
19835
21017
|
}
|
|
19836
|
-
function
|
|
21018
|
+
function unescaped2(double) {
|
|
19837
21019
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
19838
21020
|
}
|
|
19839
21021
|
function splitWords(text) {
|
|
@@ -19849,7 +21031,7 @@ function splitWords(text) {
|
|
|
19849
21031
|
word = void 0;
|
|
19850
21032
|
continue;
|
|
19851
21033
|
}
|
|
19852
|
-
word = (word ?? "") + (single ?? (double !== void 0 ?
|
|
21034
|
+
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped2(double) : escaped ?? plain3));
|
|
19853
21035
|
}
|
|
19854
21036
|
if (word !== void 0) words3.push(word);
|
|
19855
21037
|
return words3;
|
|
@@ -19886,7 +21068,7 @@ function splitPatterns(text) {
|
|
|
19886
21068
|
}
|
|
19887
21069
|
word ??= [];
|
|
19888
21070
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
19889
|
-
else addPart(word, single ?? (double !== void 0 ?
|
|
21071
|
+
else addPart(word, single ?? (double !== void 0 ? unescaped2(double) : escaped));
|
|
19890
21072
|
}
|
|
19891
21073
|
if (word !== void 0) words3.push(word);
|
|
19892
21074
|
return words3;
|
|
@@ -19937,13 +21119,13 @@ function commandMatches(cmnd, argv) {
|
|
|
19937
21119
|
if (cmnd === "*") return true;
|
|
19938
21120
|
const [path, ...args] = argv;
|
|
19939
21121
|
if (path !== cmnd.path) return false;
|
|
19940
|
-
const
|
|
19941
|
-
if (
|
|
21122
|
+
const want2 = cmnd.args;
|
|
21123
|
+
if (want2 === void 0) return true;
|
|
19942
21124
|
return globMatch(
|
|
19943
|
-
|
|
21125
|
+
want2.length,
|
|
19944
21126
|
args.length,
|
|
19945
|
-
(i) => isAny(
|
|
19946
|
-
(i, j) => wordMatch(
|
|
21127
|
+
(i) => isAny(want2[i]),
|
|
21128
|
+
(i, j) => wordMatch(want2[i], args[j])
|
|
19947
21129
|
);
|
|
19948
21130
|
}
|
|
19949
21131
|
function commandText(cmnd) {
|
|
@@ -20240,11 +21422,11 @@ function parsePolicy(text, file) {
|
|
|
20240
21422
|
const defaults2 = [];
|
|
20241
21423
|
const errors = [];
|
|
20242
21424
|
for (const [n, line] of text.split("\n").entries()) {
|
|
20243
|
-
const
|
|
20244
|
-
if (
|
|
20245
|
-
if ("error" in
|
|
20246
|
-
else if ("defaults" in
|
|
20247
|
-
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);
|
|
20248
21430
|
}
|
|
20249
21431
|
return { rules, defaults: defaults2, errors };
|
|
20250
21432
|
}
|
|
@@ -20297,11 +21479,11 @@ var REPLAY_TAGS_MAX = 64;
|
|
|
20297
21479
|
var REPLAY_ARGV_MAX = 4096;
|
|
20298
21480
|
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
20299
21481
|
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
20300
|
-
function
|
|
21482
|
+
function strings2(value2, max) {
|
|
20301
21483
|
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
20302
21484
|
}
|
|
20303
21485
|
function words(value2) {
|
|
20304
|
-
if (!
|
|
21486
|
+
if (!strings2(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
20305
21487
|
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
20306
21488
|
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
20307
21489
|
}
|
|
@@ -20336,7 +21518,7 @@ function replayOf(value2) {
|
|
|
20336
21518
|
const bound3 = filesOf(r.userWritable);
|
|
20337
21519
|
const named = (s) => typeof s === "string" && s !== "";
|
|
20338
21520
|
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
20339
|
-
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;
|
|
20340
21522
|
if (!valid) return failed2("EINVAL", NOT_A_REQUEST);
|
|
20341
21523
|
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
20342
21524
|
return {
|
|
@@ -20428,28 +21610,28 @@ function recordedCommand(argv) {
|
|
|
20428
21610
|
const [path = "", ...args] = argv;
|
|
20429
21611
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
20430
21612
|
}
|
|
20431
|
-
function flags(
|
|
21613
|
+
function flags(parsed3, arg, next3) {
|
|
20432
21614
|
for (let j = 1; j < arg.length; j++) {
|
|
20433
21615
|
const flag = arg[j];
|
|
20434
|
-
if (flag === "n")
|
|
20435
|
-
else if (flag === "l")
|
|
20436
|
-
else if (flag === "v")
|
|
20437
|
-
else if (flag === "k")
|
|
20438
|
-
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;
|
|
20439
21621
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
20440
21622
|
${USAGE}`;
|
|
20441
21623
|
else {
|
|
20442
21624
|
const user = arg.slice(j + 1) || next3;
|
|
20443
21625
|
if (!user) return `sudo: option requires an argument -- 'u'
|
|
20444
21626
|
${USAGE}`;
|
|
20445
|
-
|
|
21627
|
+
parsed3.user = user;
|
|
20446
21628
|
return arg.slice(j + 1) ? 0 : 1;
|
|
20447
21629
|
}
|
|
20448
21630
|
}
|
|
20449
21631
|
return 0;
|
|
20450
21632
|
}
|
|
20451
21633
|
function parseArgs(args) {
|
|
20452
|
-
const
|
|
21634
|
+
const parsed3 = {
|
|
20453
21635
|
list: false,
|
|
20454
21636
|
nonInteractive: false,
|
|
20455
21637
|
validate: false,
|
|
@@ -20466,19 +21648,19 @@ function parseArgs(args) {
|
|
|
20466
21648
|
break;
|
|
20467
21649
|
}
|
|
20468
21650
|
if (!arg.startsWith("-") || arg === "-") break;
|
|
20469
|
-
const used2 = flags(
|
|
21651
|
+
const used2 = flags(parsed3, arg, args[i + 1]);
|
|
20470
21652
|
if (typeof used2 === "string") return used2;
|
|
20471
21653
|
i += used2;
|
|
20472
21654
|
}
|
|
20473
|
-
|
|
20474
|
-
return usable(
|
|
21655
|
+
parsed3.command = args.slice(i);
|
|
21656
|
+
return usable(parsed3) ? parsed3 : USAGE;
|
|
20475
21657
|
}
|
|
20476
|
-
function usable(
|
|
20477
|
-
const command =
|
|
20478
|
-
const modes = [
|
|
20479
|
-
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;
|
|
20480
21662
|
if (modes === 1) return !command;
|
|
20481
|
-
return command ||
|
|
21663
|
+
return command || parsed3.reset;
|
|
20482
21664
|
}
|
|
20483
21665
|
function policyOf(settings) {
|
|
20484
21666
|
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
@@ -20622,28 +21804,28 @@ var Sudo = class {
|
|
|
20622
21804
|
};
|
|
20623
21805
|
}
|
|
20624
21806
|
async check(cred, call) {
|
|
20625
|
-
const
|
|
21807
|
+
const lines3 = [];
|
|
20626
21808
|
let ok4 = true;
|
|
20627
21809
|
const report = ({ path, problems, ok: read }) => {
|
|
20628
21810
|
for (const problem of problems) {
|
|
20629
21811
|
const where = problem.line === void 0 ? `${path}: sudo skips it:` : `${path}:${problem.line}:`;
|
|
20630
|
-
|
|
21812
|
+
lines3.push([2, `visudo: ${where} ${problem.message}`]);
|
|
20631
21813
|
}
|
|
20632
|
-
if (read && problems.length === 0)
|
|
21814
|
+
if (read && problems.length === 0) lines3.push([1, `${path}: parsed OK`]);
|
|
20633
21815
|
if (problems.length > 0) ok4 = false;
|
|
20634
21816
|
};
|
|
20635
21817
|
const values = (defaults2) => {
|
|
20636
|
-
for (const line of defaultsText(defaults2))
|
|
21818
|
+
for (const line of defaultsText(defaults2)) lines3.push([1, line]);
|
|
20637
21819
|
};
|
|
20638
21820
|
if (call.files !== void 0) {
|
|
20639
21821
|
const defaults2 = [];
|
|
20640
21822
|
for (const { name: name2, text } of call.files) {
|
|
20641
|
-
const
|
|
20642
|
-
defaults2.push(...
|
|
20643
|
-
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 });
|
|
20644
21826
|
}
|
|
20645
21827
|
if (defaults2.length > 0) values(defaults2);
|
|
20646
|
-
return { lines:
|
|
21828
|
+
return { lines: lines3, ok: ok4 };
|
|
20647
21829
|
}
|
|
20648
21830
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
20649
21831
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
@@ -20652,7 +21834,7 @@ var Sudo = class {
|
|
|
20652
21834
|
const files = await this.policy();
|
|
20653
21835
|
for (const file of files) report(file);
|
|
20654
21836
|
values(files.flatMap((f) => f.defaults ?? []));
|
|
20655
|
-
return { lines:
|
|
21837
|
+
return { lines: lines3, ok: ok4 };
|
|
20656
21838
|
}
|
|
20657
21839
|
async groupIds() {
|
|
20658
21840
|
return new Map((await this.o.users.groups()).map((g) => [g.name, g.gid]));
|
|
@@ -20717,13 +21899,13 @@ var Sudo = class {
|
|
|
20717
21899
|
return void 0;
|
|
20718
21900
|
}
|
|
20719
21901
|
async request(caller, call, signal) {
|
|
20720
|
-
const
|
|
20721
|
-
if (typeof
|
|
21902
|
+
const parsed3 = parseArgs(call.args);
|
|
21903
|
+
if (typeof parsed3 === "string") return failed2("EINVAL", parsed3);
|
|
20722
21904
|
if (this.o.mode !== "enforce") {
|
|
20723
21905
|
const mode = this.o.mode;
|
|
20724
|
-
if (
|
|
21906
|
+
if (parsed3.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
20725
21907
|
` };
|
|
20726
|
-
return
|
|
21908
|
+
return parsed3.command.length > 0 ? { run: parsed3.command } : DONE;
|
|
20727
21909
|
}
|
|
20728
21910
|
const { cred } = caller;
|
|
20729
21911
|
const policy = await this.policyRules(caller);
|
|
@@ -20736,22 +21918,22 @@ var Sudo = class {
|
|
|
20736
21918
|
};
|
|
20737
21919
|
const applies = await this.applies(self, cred);
|
|
20738
21920
|
const settings = resolveDefaults(policy.defaults, applies);
|
|
20739
|
-
if (
|
|
21921
|
+
if (parsed3.removeAll) {
|
|
20740
21922
|
this.timestamps.remove(owner.uid);
|
|
20741
21923
|
return DONE;
|
|
20742
21924
|
}
|
|
20743
|
-
if (
|
|
21925
|
+
if (parsed3.reset && !parsed3.validate && !parsed3.list && parsed3.command.length === 0) {
|
|
20744
21926
|
this.timestamps.invalidate(owner, settings.timestampType);
|
|
20745
21927
|
return DONE;
|
|
20746
21928
|
}
|
|
20747
|
-
const target = await this.lookup(
|
|
20748
|
-
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}`);
|
|
20749
21931
|
const root = cred.euid === 0;
|
|
20750
21932
|
const rules = root ? [] : policy.rules;
|
|
20751
21933
|
const mine = rules.filter((rule) => applies(rule.who));
|
|
20752
|
-
if (
|
|
20753
|
-
const session = { caller, self, target, owner, policy: policyOf(settings), parsed:
|
|
20754
|
-
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);
|
|
20755
21937
|
return this.command(session, root, mine, call);
|
|
20756
21938
|
}
|
|
20757
21939
|
async replay(value2) {
|
|
@@ -20810,9 +21992,9 @@ var Sudo = class {
|
|
|
20810
21992
|
);
|
|
20811
21993
|
}
|
|
20812
21994
|
async command(s, root, mine, call) {
|
|
20813
|
-
const { caller, self, target, parsed:
|
|
21995
|
+
const { caller, self, target, parsed: parsed3 } = s;
|
|
20814
21996
|
const { cred } = caller;
|
|
20815
|
-
const [word = "", ...args] =
|
|
21997
|
+
const [word = "", ...args] = parsed3.command;
|
|
20816
21998
|
const cwd = await this.canonical(call.cwd);
|
|
20817
21999
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
20818
22000
|
return failed2(
|
|
@@ -20827,7 +22009,7 @@ var Sudo = class {
|
|
|
20827
22009
|
const matching = mine.filter(
|
|
20828
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)
|
|
20829
22011
|
);
|
|
20830
|
-
const grant = () => this.grant(self, target, cred,
|
|
22012
|
+
const grant = () => this.grant(self, target, cred, parsed3.command, path, cwd, call.term, bound3);
|
|
20831
22013
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
20832
22014
|
const quoted = argv.map(quoteWord).join(" ");
|
|
20833
22015
|
if (matching.length === 0) {
|
|
@@ -20836,38 +22018,38 @@ var Sudo = class {
|
|
|
20836
22018
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
20837
22019
|
);
|
|
20838
22020
|
}
|
|
20839
|
-
if (!
|
|
22021
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
|
|
20840
22022
|
this.timestamps.stamp(s.owner, s.policy, bound3);
|
|
20841
22023
|
this.logged(s, cwd, argv);
|
|
20842
22024
|
return grant();
|
|
20843
22025
|
}
|
|
20844
|
-
if (
|
|
22026
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20845
22027
|
return this.pendingFor(cred.ruid, async () => {
|
|
20846
|
-
const answer2 = await this.ask(s, { argv:
|
|
22028
|
+
const answer2 = await this.ask(s, { argv: parsed3.command, path, cwd, bound: bound3 });
|
|
20847
22029
|
if ("error" in answer2) return answer2;
|
|
20848
22030
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
20849
22031
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
20850
22032
|
}
|
|
20851
22033
|
const now = await this.bound(path, cwd);
|
|
20852
22034
|
if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20853
|
-
const shown2 = recordable(cwd, [...
|
|
22035
|
+
const shown2 = recordable(cwd, [...parsed3.command, ...Object.keys(bound3)]);
|
|
20854
22036
|
const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
|
|
20855
22037
|
if (!("error" in result)) this.stamped(s, answer2, bound3);
|
|
20856
22038
|
return result;
|
|
20857
22039
|
});
|
|
20858
22040
|
}
|
|
20859
22041
|
async validate(s, root, mine, dir) {
|
|
20860
|
-
const { self, parsed:
|
|
22042
|
+
const { self, parsed: parsed3 } = s;
|
|
20861
22043
|
if (root) return DONE;
|
|
20862
22044
|
if (mine.length === 0) {
|
|
20863
22045
|
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
20864
22046
|
}
|
|
20865
22047
|
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
20866
|
-
if (!
|
|
22048
|
+
if (!parsed3.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
20867
22049
|
this.timestamps.stamp(s.owner, s.policy);
|
|
20868
22050
|
return DONE;
|
|
20869
22051
|
}
|
|
20870
|
-
if (
|
|
22052
|
+
if (parsed3.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20871
22053
|
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
20872
22054
|
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
20873
22055
|
const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
|
|
@@ -20922,7 +22104,7 @@ var Sudo = class {
|
|
|
20922
22104
|
}
|
|
20923
22105
|
const shown2 = [];
|
|
20924
22106
|
for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
|
|
20925
|
-
const
|
|
22107
|
+
const lines3 = shown2.join("\n");
|
|
20926
22108
|
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
20927
22109
|
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
20928
22110
|
${params}
|
|
@@ -20930,7 +22112,7 @@ var Sudo = class {
|
|
|
20930
22112
|
`;
|
|
20931
22113
|
return {
|
|
20932
22114
|
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
20933
|
-
${
|
|
22115
|
+
${lines3}
|
|
20934
22116
|
`
|
|
20935
22117
|
};
|
|
20936
22118
|
}
|
|
@@ -21347,7 +22529,7 @@ async function epollSyscall(req, proc) {
|
|
|
21347
22529
|
|
|
21348
22530
|
// src/kernel/locks.ts
|
|
21349
22531
|
var overlaps = (a, b) => a.start < b.end && b.start < a.end;
|
|
21350
|
-
var clashes = (lock, owner,
|
|
22532
|
+
var clashes = (lock, owner, want2) => lock.owner !== owner && overlaps(lock, want2) && (lock.kind === "write" || want2.kind === "write");
|
|
21351
22533
|
function lockRange(start2, len) {
|
|
21352
22534
|
if (!Number.isSafeInteger(start2) || !Number.isSafeInteger(len)) throw new KernelError("EINVAL");
|
|
21353
22535
|
const from = len < 0 ? start2 + len : start2;
|
|
@@ -21433,8 +22615,8 @@ var LockTable = class {
|
|
|
21433
22615
|
blocked = /* @__PURE__ */ new Map();
|
|
21434
22616
|
wakers = /* @__PURE__ */ new Map();
|
|
21435
22617
|
held = /* @__PURE__ */ new Map();
|
|
21436
|
-
holder(key, owner,
|
|
21437
|
-
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));
|
|
21438
22620
|
}
|
|
21439
22621
|
set(key, owner, kind, start2, end) {
|
|
21440
22622
|
if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
|
|
@@ -21453,10 +22635,10 @@ var LockTable = class {
|
|
|
21453
22635
|
return true;
|
|
21454
22636
|
}
|
|
21455
22637
|
async wait(key, owner, kind, range2, blocking) {
|
|
21456
|
-
const
|
|
22638
|
+
const want2 = { key, kind, ...range2 };
|
|
21457
22639
|
let signal;
|
|
21458
22640
|
for (; ; ) {
|
|
21459
|
-
const holder = this.holder(key, owner,
|
|
22641
|
+
const holder = this.holder(key, owner, want2);
|
|
21460
22642
|
if (!holder) {
|
|
21461
22643
|
this.set(key, owner, kind, range2.start, range2.end);
|
|
21462
22644
|
return;
|
|
@@ -21464,7 +22646,7 @@ var LockTable = class {
|
|
|
21464
22646
|
if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
|
|
21465
22647
|
signal ??= blocking();
|
|
21466
22648
|
if (signal.aborted) throw new KernelError("EINTR");
|
|
21467
|
-
this.blocked.set(owner,
|
|
22649
|
+
this.blocked.set(owner, want2);
|
|
21468
22650
|
try {
|
|
21469
22651
|
await this.changed(key, signal);
|
|
21470
22652
|
} finally {
|
|
@@ -21476,9 +22658,9 @@ var LockTable = class {
|
|
|
21476
22658
|
const seen = /* @__PURE__ */ new Set();
|
|
21477
22659
|
for (let at = from; at !== void 0 && !seen.has(at); ) {
|
|
21478
22660
|
seen.add(at);
|
|
21479
|
-
const
|
|
21480
|
-
if (!
|
|
21481
|
-
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);
|
|
21482
22664
|
if (!holder) return false;
|
|
21483
22665
|
if (holder.owner === target) return true;
|
|
21484
22666
|
at = holder.owner;
|
|
@@ -21843,24 +23025,24 @@ function errno2(code, message3) {
|
|
|
21843
23025
|
}
|
|
21844
23026
|
function requestHead(method, url, headers, extra, first = []) {
|
|
21845
23027
|
if (!TOKEN4.test(method)) throw errno2("EINVAL", `method ${method}`);
|
|
21846
|
-
const
|
|
21847
|
-
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}`);
|
|
21848
23030
|
for (const [name2, value2] of headers) {
|
|
21849
23031
|
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
21850
23032
|
if (!TOKEN4.test(name2) || !SAFE_VALUE.test(value2)) throw errno2("EINVAL", `header ${name2}`);
|
|
21851
|
-
|
|
23033
|
+
lines3.push(`${name2}: ${value2}`);
|
|
21852
23034
|
}
|
|
21853
|
-
for (const [name2, value2] of extra)
|
|
21854
|
-
return latin1Bytes(`${
|
|
23035
|
+
for (const [name2, value2] of extra) lines3.push(`${name2}: ${value2}`);
|
|
23036
|
+
return latin1Bytes(`${lines3.join("\r\n")}\r
|
|
21855
23037
|
\r
|
|
21856
23038
|
`);
|
|
21857
23039
|
}
|
|
21858
23040
|
function parseHead(bytes) {
|
|
21859
|
-
const [statusLine = "", ...
|
|
23041
|
+
const [statusLine = "", ...lines3] = latin1(bytes).split(/\r?\n/);
|
|
21860
23042
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
21861
23043
|
if (!match) throw errno2("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
21862
23044
|
const headers = [];
|
|
21863
|
-
for (const line of
|
|
23045
|
+
for (const line of lines3) {
|
|
21864
23046
|
const colon = line.indexOf(":");
|
|
21865
23047
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
21866
23048
|
}
|
|
@@ -21950,8 +23132,8 @@ var KernelLoopback = class {
|
|
|
21950
23132
|
);
|
|
21951
23133
|
} catch (err) {
|
|
21952
23134
|
socket.close();
|
|
21953
|
-
const
|
|
21954
|
-
throw
|
|
23135
|
+
const refused5 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
23136
|
+
throw refused5 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
21955
23137
|
}
|
|
21956
23138
|
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
21957
23139
|
return socket;
|
|
@@ -22340,9 +23522,9 @@ function pem(der2) {
|
|
|
22340
23522
|
for (let i = 0; i < der2.length; i += 32768) {
|
|
22341
23523
|
b642 += String.fromCharCode(...der2.subarray(i, i + 32768));
|
|
22342
23524
|
}
|
|
22343
|
-
const
|
|
23525
|
+
const lines3 = btoa(b642).match(/.{1,64}/g) ?? [];
|
|
22344
23526
|
return `-----BEGIN CERTIFICATE-----
|
|
22345
|
-
${
|
|
23527
|
+
${lines3.join("\n")}
|
|
22346
23528
|
-----END CERTIFICATE-----
|
|
22347
23529
|
`;
|
|
22348
23530
|
}
|
|
@@ -23126,13 +24308,13 @@ var HOSTS_PATH = "/etc/hosts";
|
|
|
23126
24308
|
function parseHosts(text) {
|
|
23127
24309
|
const table = /* @__PURE__ */ new Map();
|
|
23128
24310
|
for (const raw2 of text.split("\n")) {
|
|
23129
|
-
const [
|
|
23130
|
-
if (!
|
|
23131
|
-
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;
|
|
23132
24314
|
for (const name2 of names2) {
|
|
23133
24315
|
const key = name2.toLowerCase().replace(/\.$/, "");
|
|
23134
24316
|
const known = table.get(key) ?? [];
|
|
23135
|
-
if (!known.includes(
|
|
24317
|
+
if (!known.includes(address2)) known.push(address2);
|
|
23136
24318
|
table.set(key, known);
|
|
23137
24319
|
}
|
|
23138
24320
|
}
|
|
@@ -23332,14 +24514,14 @@ var Resolver = class {
|
|
|
23332
24514
|
if (hostsEntry(key, this.own) !== void 0) return true;
|
|
23333
24515
|
return ((await this.hosts?.().catch(() => void 0))?.get(key)?.length ?? 0) > 0;
|
|
23334
24516
|
}
|
|
23335
|
-
async reverse(
|
|
23336
|
-
const v6 = ipv6Groups(
|
|
23337
|
-
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;
|
|
23338
24520
|
const file = await this.hosts?.().catch(() => void 0);
|
|
23339
24521
|
for (const table of [file, this.builtin]) {
|
|
23340
24522
|
for (const [name2, addresses] of table ?? []) if (addresses.some(same3)) return name2;
|
|
23341
24523
|
}
|
|
23342
|
-
if (ipv4Bytes(
|
|
24524
|
+
if (ipv4Bytes(address2)?.[0] === 127) return "localhost";
|
|
23343
24525
|
return void 0;
|
|
23344
24526
|
}
|
|
23345
24527
|
async resolve(name2, family, blocking) {
|
|
@@ -23393,13 +24575,13 @@ var Resolver = class {
|
|
|
23393
24575
|
if (addresses.length > 0) this.remember(id2, addresses, answer2.ttl);
|
|
23394
24576
|
return addresses;
|
|
23395
24577
|
}
|
|
23396
|
-
usable(name2,
|
|
23397
|
-
const v4 = ipv4Bytes(
|
|
23398
|
-
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;
|
|
23399
24581
|
if (v4 && family === 6) return false;
|
|
23400
|
-
const kind = this.routes.classify(
|
|
24582
|
+
const kind = this.routes.classify(address2);
|
|
23401
24583
|
if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
|
|
23402
|
-
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}`);
|
|
23403
24585
|
return false;
|
|
23404
24586
|
}
|
|
23405
24587
|
remember(id2, addresses, ttl) {
|
|
@@ -23433,275 +24615,6 @@ function waitFor(lookup2, signal) {
|
|
|
23433
24615
|
});
|
|
23434
24616
|
}
|
|
23435
24617
|
|
|
23436
|
-
// src/kernel/tty.ts
|
|
23437
|
-
var ICRNL = 256;
|
|
23438
|
-
var IUTF8 = 16384;
|
|
23439
|
-
var OPOST = 1;
|
|
23440
|
-
var ONLCR = 4;
|
|
23441
|
-
var ISIG = 1;
|
|
23442
|
-
var ICANON = 2;
|
|
23443
|
-
var ECHO = 8;
|
|
23444
|
-
var ECHOE = 16;
|
|
23445
|
-
var ECHOCTL = 512;
|
|
23446
|
-
var NOFLSH = 128;
|
|
23447
|
-
var IEXTEN = 32768;
|
|
23448
|
-
var VINTR = 0;
|
|
23449
|
-
var VQUIT = 1;
|
|
23450
|
-
var VERASE = 2;
|
|
23451
|
-
var VKILL = 3;
|
|
23452
|
-
var VEOF = 4;
|
|
23453
|
-
var VSUSP = 10;
|
|
23454
|
-
var VWERASE = 14;
|
|
23455
|
-
function defaultTermios() {
|
|
23456
|
-
const cCc = new Array(32).fill(0);
|
|
23457
|
-
Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
|
|
23458
|
-
Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
|
|
23459
|
-
Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
|
|
23460
|
-
return {
|
|
23461
|
-
c_iflag: ICRNL | 1024 | 8192 | IUTF8,
|
|
23462
|
-
c_oflag: OPOST | ONLCR,
|
|
23463
|
-
c_cflag: 15 | 48 | 128,
|
|
23464
|
-
c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
|
|
23465
|
-
c_cc: cCc
|
|
23466
|
-
};
|
|
23467
|
-
}
|
|
23468
|
-
var encoder7 = new TextEncoder();
|
|
23469
|
-
function isContinuation(byte) {
|
|
23470
|
-
return (byte & 192) === 128;
|
|
23471
|
-
}
|
|
23472
|
-
var KernelTty = class {
|
|
23473
|
-
termios = defaultTermios();
|
|
23474
|
-
cols = 80;
|
|
23475
|
-
rows = 24;
|
|
23476
|
-
readable = [];
|
|
23477
|
-
line = [];
|
|
23478
|
-
waiters = [];
|
|
23479
|
-
hungUp = false;
|
|
23480
|
-
name;
|
|
23481
|
-
owner;
|
|
23482
|
-
onRead;
|
|
23483
|
-
readers = 0;
|
|
23484
|
-
screen;
|
|
23485
|
-
signal;
|
|
23486
|
-
constructor(screen, signal) {
|
|
23487
|
-
this.screen = screen;
|
|
23488
|
-
this.signal = signal;
|
|
23489
|
-
}
|
|
23490
|
-
receive(bytes) {
|
|
23491
|
-
for (const byte of bytes) this.key(byte);
|
|
23492
|
-
this.wake();
|
|
23493
|
-
}
|
|
23494
|
-
resize(cols, rows) {
|
|
23495
|
-
if (cols === this.cols && rows === this.rows) return;
|
|
23496
|
-
this.setSize(cols, rows);
|
|
23497
|
-
this.signal(SIG.WINCH);
|
|
23498
|
-
}
|
|
23499
|
-
hangup() {
|
|
23500
|
-
this.hungUp = true;
|
|
23501
|
-
this.wake();
|
|
23502
|
-
}
|
|
23503
|
-
signalHangup() {
|
|
23504
|
-
this.signal(SIG.HUP);
|
|
23505
|
-
}
|
|
23506
|
-
setSize(cols, rows) {
|
|
23507
|
-
this.cols = cols;
|
|
23508
|
-
this.rows = rows;
|
|
23509
|
-
}
|
|
23510
|
-
tcgets() {
|
|
23511
|
-
return { ...this.termios, c_cc: [...this.termios.c_cc] };
|
|
23512
|
-
}
|
|
23513
|
-
tcsets(termios, flush = false) {
|
|
23514
|
-
const wasCanonical = this.canonical;
|
|
23515
|
-
this.termios = { ...termios, c_cc: [...termios.c_cc] };
|
|
23516
|
-
if (flush) {
|
|
23517
|
-
this.line = [];
|
|
23518
|
-
this.readable = [];
|
|
23519
|
-
}
|
|
23520
|
-
if (wasCanonical && !this.canonical && this.line.length > 0) {
|
|
23521
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
23522
|
-
this.line = [];
|
|
23523
|
-
this.wake();
|
|
23524
|
-
}
|
|
23525
|
-
}
|
|
23526
|
-
winsize() {
|
|
23527
|
-
return [this.rows, this.cols];
|
|
23528
|
-
}
|
|
23529
|
-
file() {
|
|
23530
|
-
return new OpenFile({
|
|
23531
|
-
read: (max, signal) => this.read(max, signal),
|
|
23532
|
-
write: async (bytes) => {
|
|
23533
|
-
if (this.hungUp) throw new KernelError("EIO");
|
|
23534
|
-
this.output(bytes);
|
|
23535
|
-
return bytes.length;
|
|
23536
|
-
},
|
|
23537
|
-
poll: () => ({
|
|
23538
|
-
readable: this.readable.length > 0 || this.hungUp,
|
|
23539
|
-
writable: true,
|
|
23540
|
-
hangup: this.hungUp,
|
|
23541
|
-
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
23542
|
-
}),
|
|
23543
|
-
changed: (signal) => this.changed(signal),
|
|
23544
|
-
close: () => {
|
|
23545
|
-
},
|
|
23546
|
-
tty: this
|
|
23547
|
-
});
|
|
23548
|
-
}
|
|
23549
|
-
get canonical() {
|
|
23550
|
-
return (this.termios.c_lflag & ICANON) !== 0;
|
|
23551
|
-
}
|
|
23552
|
-
cc(slot2) {
|
|
23553
|
-
return this.termios.c_cc[slot2] ?? 0;
|
|
23554
|
-
}
|
|
23555
|
-
key(input) {
|
|
23556
|
-
const { c_iflag, c_lflag } = this.termios;
|
|
23557
|
-
const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
|
|
23558
|
-
if (c_lflag & ISIG && this.signalKey(byte)) return;
|
|
23559
|
-
if (!this.canonical) {
|
|
23560
|
-
this.readable.push(Uint8Array.of(byte));
|
|
23561
|
-
if (c_lflag & ECHO) this.echo(byte);
|
|
23562
|
-
return;
|
|
23563
|
-
}
|
|
23564
|
-
this.edit(byte);
|
|
23565
|
-
}
|
|
23566
|
-
signalKey(byte) {
|
|
23567
|
-
const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
|
|
23568
|
-
if (!sig || byte === 0) return false;
|
|
23569
|
-
if (this.termios.c_lflag & ECHO) this.echo(byte);
|
|
23570
|
-
if (!(this.termios.c_lflag & NOFLSH)) {
|
|
23571
|
-
this.line = [];
|
|
23572
|
-
this.readable = [];
|
|
23573
|
-
}
|
|
23574
|
-
this.signal(sig);
|
|
23575
|
-
return true;
|
|
23576
|
-
}
|
|
23577
|
-
edit(byte) {
|
|
23578
|
-
const echo = (this.termios.c_lflag & ECHO) !== 0;
|
|
23579
|
-
if (byte === this.cc(VERASE) || byte === 8) {
|
|
23580
|
-
this.rubOut(this.lastCharLength());
|
|
23581
|
-
} else if (byte === this.cc(VKILL)) {
|
|
23582
|
-
this.rubOut(this.line.length);
|
|
23583
|
-
} else if (byte === this.cc(VWERASE)) {
|
|
23584
|
-
this.rubOut(this.lastWordLength());
|
|
23585
|
-
} else if (byte === this.cc(VEOF)) {
|
|
23586
|
-
this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
|
|
23587
|
-
this.line = [];
|
|
23588
|
-
} else if (byte === 10) {
|
|
23589
|
-
this.line.push(byte);
|
|
23590
|
-
this.readable.push(Uint8Array.from(this.line));
|
|
23591
|
-
this.line = [];
|
|
23592
|
-
if (echo) this.output(encoder7.encode("\n"));
|
|
23593
|
-
} else {
|
|
23594
|
-
this.line.push(byte);
|
|
23595
|
-
if (echo) this.echo(byte);
|
|
23596
|
-
}
|
|
23597
|
-
}
|
|
23598
|
-
lastWordLength() {
|
|
23599
|
-
let end = this.line.length;
|
|
23600
|
-
while (end > 0 && this.line[end - 1] === 32) end--;
|
|
23601
|
-
while (end > 0 && this.line[end - 1] !== 32) end--;
|
|
23602
|
-
return this.line.length - end;
|
|
23603
|
-
}
|
|
23604
|
-
lastCharLength() {
|
|
23605
|
-
const { line } = this;
|
|
23606
|
-
let n = Math.min(1, line.length);
|
|
23607
|
-
if (this.termios.c_iflag & IUTF8) {
|
|
23608
|
-
while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
|
|
23609
|
-
}
|
|
23610
|
-
return n;
|
|
23611
|
-
}
|
|
23612
|
-
rubOut(n) {
|
|
23613
|
-
const dropped2 = this.line.splice(this.line.length - n, n);
|
|
23614
|
-
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
23615
|
-
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
23616
|
-
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
23617
|
-
this.output(encoder7.encode("\b \b".repeat(cells)), false);
|
|
23618
|
-
}
|
|
23619
|
-
}
|
|
23620
|
-
echo(byte) {
|
|
23621
|
-
if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
|
|
23622
|
-
this.output(Uint8Array.of(94, byte + 64), false);
|
|
23623
|
-
return;
|
|
23624
|
-
}
|
|
23625
|
-
this.output(Uint8Array.of(byte));
|
|
23626
|
-
}
|
|
23627
|
-
output(bytes, translate2 = true) {
|
|
23628
|
-
const { c_oflag } = this.termios;
|
|
23629
|
-
if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
|
|
23630
|
-
this.screen.write(bytes);
|
|
23631
|
-
return;
|
|
23632
|
-
}
|
|
23633
|
-
const out = [];
|
|
23634
|
-
for (const byte of bytes) {
|
|
23635
|
-
if (byte === 10) out.push(13);
|
|
23636
|
-
out.push(byte);
|
|
23637
|
-
}
|
|
23638
|
-
this.screen.write(Uint8Array.from(out));
|
|
23639
|
-
}
|
|
23640
|
-
get reading() {
|
|
23641
|
-
return this.readers > 0;
|
|
23642
|
-
}
|
|
23643
|
-
async read(max, signal) {
|
|
23644
|
-
this.onRead?.();
|
|
23645
|
-
this.readers++;
|
|
23646
|
-
try {
|
|
23647
|
-
return await this.take(max, signal);
|
|
23648
|
-
} finally {
|
|
23649
|
-
this.readers--;
|
|
23650
|
-
}
|
|
23651
|
-
}
|
|
23652
|
-
async take(max, signal) {
|
|
23653
|
-
while (this.readable.length === 0) {
|
|
23654
|
-
if (this.hungUp) return new Uint8Array(0);
|
|
23655
|
-
await this.changed(signal);
|
|
23656
|
-
}
|
|
23657
|
-
const head = this.readable[0];
|
|
23658
|
-
if (head === null) {
|
|
23659
|
-
this.readable.shift();
|
|
23660
|
-
return new Uint8Array(0);
|
|
23661
|
-
}
|
|
23662
|
-
if (head === void 0) return new Uint8Array(0);
|
|
23663
|
-
const chunks = [];
|
|
23664
|
-
let n = 0;
|
|
23665
|
-
while (this.readable.length > 0 && n < max) {
|
|
23666
|
-
const next3 = this.readable[0];
|
|
23667
|
-
if (next3 === null || next3 === void 0) break;
|
|
23668
|
-
const take = next3.subarray(0, max - n);
|
|
23669
|
-
chunks.push(take);
|
|
23670
|
-
n += take.length;
|
|
23671
|
-
if (take.length < next3.length) this.readable[0] = next3.subarray(take.length);
|
|
23672
|
-
else this.readable.shift();
|
|
23673
|
-
if (this.canonical) break;
|
|
23674
|
-
}
|
|
23675
|
-
const out = new Uint8Array(n);
|
|
23676
|
-
let at = 0;
|
|
23677
|
-
for (const chunk2 of chunks) {
|
|
23678
|
-
out.set(chunk2, at);
|
|
23679
|
-
at += chunk2.length;
|
|
23680
|
-
}
|
|
23681
|
-
return out;
|
|
23682
|
-
}
|
|
23683
|
-
changed(signal) {
|
|
23684
|
-
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
23685
|
-
return new Promise((resolve2, reject) => {
|
|
23686
|
-
const onAbort = () => {
|
|
23687
|
-
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
23688
|
-
reject(new KernelError("EINTR"));
|
|
23689
|
-
};
|
|
23690
|
-
const waiter = () => {
|
|
23691
|
-
signal?.removeEventListener("abort", onAbort);
|
|
23692
|
-
resolve2();
|
|
23693
|
-
};
|
|
23694
|
-
this.waiters.push(waiter);
|
|
23695
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
23696
|
-
});
|
|
23697
|
-
}
|
|
23698
|
-
wake() {
|
|
23699
|
-
const waiters = this.waiters;
|
|
23700
|
-
this.waiters = [];
|
|
23701
|
-
for (const resolve2 of waiters) resolve2();
|
|
23702
|
-
}
|
|
23703
|
-
};
|
|
23704
|
-
|
|
23705
24618
|
// src/kernel/pty.ts
|
|
23706
24619
|
var PtyPair = class {
|
|
23707
24620
|
slave;
|
|
@@ -23894,6 +24807,7 @@ function ptySyscall(req, ctx) {
|
|
|
23894
24807
|
return done4;
|
|
23895
24808
|
case "pty-ctty": {
|
|
23896
24809
|
const tty = terminalOf(ctx, req.fd);
|
|
24810
|
+
if (ctx.fds.get(req.fd).file.device) throw new KernelError("EPERM");
|
|
23897
24811
|
const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
|
|
23898
24812
|
if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done4;
|
|
23899
24813
|
if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
|
|
@@ -24675,10 +25589,10 @@ function basicOf(value2) {
|
|
|
24675
25589
|
function placeholdersIn(text) {
|
|
24676
25590
|
return [...new Set(text.match(ANY_PLACEHOLDER) ?? [])];
|
|
24677
25591
|
}
|
|
24678
|
-
function scan(
|
|
25592
|
+
function scan(lines3) {
|
|
24679
25593
|
const found = [];
|
|
24680
|
-
for (let i = 1; i <
|
|
24681
|
-
const m = HEADER_LINE.exec(
|
|
25594
|
+
for (let i = 1; i < lines3.length; i++) {
|
|
25595
|
+
const m = HEADER_LINE.exec(lines3[i]);
|
|
24682
25596
|
if (!m) return void 0;
|
|
24683
25597
|
const value2 = m[2];
|
|
24684
25598
|
const basic = basicOf(value2);
|
|
@@ -24701,10 +25615,10 @@ function usable2(values) {
|
|
|
24701
25615
|
}
|
|
24702
25616
|
async function substituteHead(head, ask) {
|
|
24703
25617
|
if (!head.endsWith("\r\n\r\n")) return void 0;
|
|
24704
|
-
const
|
|
24705
|
-
if (
|
|
24706
|
-
const request3 = REQUEST_LINE2.exec(
|
|
24707
|
-
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;
|
|
24708
25622
|
if (!request3 || !found || found.length !== 1) return void 0;
|
|
24709
25623
|
const at = found[0];
|
|
24710
25624
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
@@ -24716,8 +25630,8 @@ async function substituteHead(head, ask) {
|
|
|
24716
25630
|
);
|
|
24717
25631
|
if (Object.keys(values).length === 0) return void 0;
|
|
24718
25632
|
const value2 = at.basic ? `${at.basic.scheme}${btoa(replaced(at.basic.decoded, values))}` : replaced(at.value, values);
|
|
24719
|
-
const out = [...
|
|
24720
|
-
out[at.line] = `${
|
|
25633
|
+
const out = [...lines3];
|
|
25634
|
+
out[at.line] = `${lines3[at.line].split(":")[0]}: ${value2}`;
|
|
24721
25635
|
return { head: `${out.join("\r\n")}\r
|
|
24722
25636
|
\r
|
|
24723
25637
|
`, values };
|
|
@@ -25040,9 +25954,9 @@ function serverHeader(header4, server) {
|
|
|
25040
25954
|
return server.plus && server.binding ? END_POINT : "n,,";
|
|
25041
25955
|
}
|
|
25042
25956
|
function rewritten(first, header4) {
|
|
25043
|
-
const
|
|
25044
|
-
if (!
|
|
25045
|
-
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}` };
|
|
25046
25960
|
}
|
|
25047
25961
|
var PgClient = class {
|
|
25048
25962
|
clientFirst;
|
|
@@ -25062,10 +25976,10 @@ var PgClient = class {
|
|
|
25062
25976
|
}
|
|
25063
25977
|
if (server.sasl && this.clientFirst === void 0) {
|
|
25064
25978
|
const first = initial(message3);
|
|
25065
|
-
const
|
|
25066
|
-
if (!first || !
|
|
25979
|
+
const parsed3 = first && clientFirstOf(first.data);
|
|
25980
|
+
if (!first || !parsed3) return { bytes: message3, done: true };
|
|
25067
25981
|
this.clientFirst = first.data;
|
|
25068
|
-
this.header = serverHeader(
|
|
25982
|
+
this.header = serverHeader(parsed3.header, server);
|
|
25069
25983
|
const sent = rewritten(first, this.header);
|
|
25070
25984
|
if (sent === first) return { bytes: message3, done: false };
|
|
25071
25985
|
return {
|
|
@@ -25230,7 +26144,7 @@ function headEnd2(bytes) {
|
|
|
25230
26144
|
}
|
|
25231
26145
|
return -1;
|
|
25232
26146
|
}
|
|
25233
|
-
function
|
|
26147
|
+
function modeOf3(start2) {
|
|
25234
26148
|
const sniffs = [
|
|
25235
26149
|
["http1", sniffHttp1(start2)],
|
|
25236
26150
|
["smtp", sniffSmtp(start2)],
|
|
@@ -25339,7 +26253,7 @@ var SessionWriter = class {
|
|
|
25339
26253
|
joined5.set(bytes, this.buffer.length);
|
|
25340
26254
|
this.buffer.fill(0);
|
|
25341
26255
|
this.buffer = joined5;
|
|
25342
|
-
if (this.mode === "sniff") this.mode =
|
|
26256
|
+
if (this.mode === "sniff") this.mode = modeOf3(latin12(joined5.subarray(0, 8))) ?? "sniff";
|
|
25343
26257
|
if (this.mode === "pg" && !this.sawPg) {
|
|
25344
26258
|
this.sawPg = true;
|
|
25345
26259
|
if (!this.s.verified) void this.proof("pg");
|
|
@@ -25826,12 +26740,12 @@ function secure(prepared, seal) {
|
|
|
25826
26740
|
prepared.own?.release();
|
|
25827
26741
|
}
|
|
25828
26742
|
};
|
|
25829
|
-
const
|
|
26743
|
+
const abandon2 = () => {
|
|
25830
26744
|
if (ran) return;
|
|
25831
26745
|
prepared.options.session.free();
|
|
25832
26746
|
prepared.own?.release();
|
|
25833
26747
|
};
|
|
25834
|
-
return { make, abandon };
|
|
26748
|
+
return { make, abandon: abandon2 };
|
|
25835
26749
|
}
|
|
25836
26750
|
function supportOf(net) {
|
|
25837
26751
|
const support = net.tlsClient;
|
|
@@ -25848,19 +26762,19 @@ async function uplinkAddress(proc, host) {
|
|
|
25848
26762
|
async function connectSecure(proc, socket, host, port, req) {
|
|
25849
26763
|
const servername = serverName(req.servername || host);
|
|
25850
26764
|
if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
|
|
25851
|
-
const
|
|
25852
|
-
const route2 = proc.net.uplinkFor(
|
|
26765
|
+
const address2 = await uplinkAddress(proc, host);
|
|
26766
|
+
const route2 = proc.net.uplinkFor(address2);
|
|
25853
26767
|
if (!route2)
|
|
25854
26768
|
throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
|
|
25855
26769
|
const support = supportOf(proc.net);
|
|
25856
26770
|
let info;
|
|
25857
|
-
const { make, abandon } = secure(await prepare2(support, req, servername), { proc, port });
|
|
26771
|
+
const { make, abandon: abandon2 } = secure(await prepare2(support, req, servername), { proc, port });
|
|
25858
26772
|
try {
|
|
25859
|
-
await socket.connectSecure({ family: "inet", host:
|
|
26773
|
+
await socket.connectSecure({ family: "inet", host: address2, port }, route2, async (conn) => {
|
|
25860
26774
|
info = await conn.upgrade(make);
|
|
25861
26775
|
});
|
|
25862
26776
|
} finally {
|
|
25863
|
-
|
|
26777
|
+
abandon2();
|
|
25864
26778
|
}
|
|
25865
26779
|
return sessionOf(info, servername);
|
|
25866
26780
|
}
|
|
@@ -25893,14 +26807,14 @@ async function upgradeSecure(proc, socket, req) {
|
|
|
25893
26807
|
const host = req.servername || (peer?.family === "inet" ? peer.host : "");
|
|
25894
26808
|
const servername = serverName(host);
|
|
25895
26809
|
const port = peer?.family === "inet" ? peer.port : 0;
|
|
25896
|
-
const { make, abandon } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
26810
|
+
const { make, abandon: abandon2 } = secure(await prepare2(supportOf(proc.net), req, servername), {
|
|
25897
26811
|
proc,
|
|
25898
26812
|
port
|
|
25899
26813
|
});
|
|
25900
26814
|
try {
|
|
25901
26815
|
return sessionOf(await socket.upgradeSecure(make), servername);
|
|
25902
26816
|
} finally {
|
|
25903
|
-
|
|
26817
|
+
abandon2();
|
|
25904
26818
|
}
|
|
25905
26819
|
}
|
|
25906
26820
|
|
|
@@ -27824,6 +28738,10 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
27824
28738
|
"fd-open-clipboard",
|
|
27825
28739
|
"fd-clipboard-abort",
|
|
27826
28740
|
"fd-open-embedder",
|
|
28741
|
+
"fd-dev-nodes",
|
|
28742
|
+
"fd-open-dev",
|
|
28743
|
+
"fd-dev-ioctl",
|
|
28744
|
+
"fd-dev-meta",
|
|
27827
28745
|
"tty-get",
|
|
27828
28746
|
"tty-set",
|
|
27829
28747
|
"tty-winsz",
|
|
@@ -28200,7 +29118,7 @@ var WasmProcess = class {
|
|
|
28200
29118
|
async dispatch(req, cancelled) {
|
|
28201
29119
|
try {
|
|
28202
29120
|
if (isFdSyscall(req)) return await this.fdSyscall(req, cancelled);
|
|
28203
|
-
if (isTtySyscall(req)) return this.ttySyscall(req);
|
|
29121
|
+
if (isTtySyscall(req)) return await this.ttySyscall(req, cancelled);
|
|
28204
29122
|
if (isJobSyscall(req)) return this.jobSyscall(req);
|
|
28205
29123
|
if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
|
|
28206
29124
|
if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
|
|
@@ -28264,7 +29182,7 @@ var WasmProcess = class {
|
|
|
28264
29182
|
return owner;
|
|
28265
29183
|
}
|
|
28266
29184
|
pathKeys = /* @__PURE__ */ new Map();
|
|
28267
|
-
async lockPath(target,
|
|
29185
|
+
async lockPath(target, want2) {
|
|
28268
29186
|
if (target.fd !== void 0) {
|
|
28269
29187
|
const st = await this.fds.get(target.fd).file.stat?.();
|
|
28270
29188
|
if (!st) throw new KernelError("EBADF");
|
|
@@ -28274,9 +29192,9 @@ var WasmProcess = class {
|
|
|
28274
29192
|
if (!target.path.startsWith("/")) throw new KernelError("EINVAL");
|
|
28275
29193
|
const path = target.path;
|
|
28276
29194
|
const g = this.guarded();
|
|
28277
|
-
const checked = `${
|
|
28278
|
-
if (g &&
|
|
28279
|
-
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);
|
|
28280
29198
|
this.lockChecked.add(checked);
|
|
28281
29199
|
}
|
|
28282
29200
|
return path;
|
|
@@ -28289,8 +29207,8 @@ var WasmProcess = class {
|
|
|
28289
29207
|
const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
|
|
28290
29208
|
if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
|
|
28291
29209
|
if (access2 && !access2.stat) return objectKey(access2);
|
|
28292
|
-
const
|
|
28293
|
-
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);
|
|
28294
29212
|
if (known !== void 0) return known;
|
|
28295
29213
|
const st = await this.options.fs.stat?.(path).catch((err) => {
|
|
28296
29214
|
if (!file) throw err;
|
|
@@ -28376,7 +29294,7 @@ var WasmProcess = class {
|
|
|
28376
29294
|
const file = this.fds.get(req.fd).file;
|
|
28377
29295
|
const read = req.peek ? file.peek : file.read;
|
|
28378
29296
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
28379
|
-
if (file.tty) this.checkForeground(file.tty);
|
|
29297
|
+
if (file.tty && !file.device) this.checkForeground(file.tty);
|
|
28380
29298
|
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
28381
29299
|
if (file.embedder) return this.readEmbedder(file, req, cancelled);
|
|
28382
29300
|
if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
|
|
@@ -28473,6 +29391,46 @@ var WasmProcess = class {
|
|
|
28473
29391
|
throw err;
|
|
28474
29392
|
});
|
|
28475
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
|
+
}
|
|
28476
29434
|
async openEmbedder(flags2) {
|
|
28477
29435
|
const broker = this.options.embedder;
|
|
28478
29436
|
if (!broker) throw new KernelError("ENOENT");
|
|
@@ -28560,11 +29518,11 @@ var WasmProcess = class {
|
|
|
28560
29518
|
} else if (req.create) {
|
|
28561
29519
|
await nodes.onEntry(path, async () => {
|
|
28562
29520
|
const marks = this.options.owners || plain3 !== void 0;
|
|
28563
|
-
const
|
|
29521
|
+
const fresh4 = marks && !await present3(fs, path, true);
|
|
28564
29522
|
this.fds.installAt(fd, this.openVfsFile(open2, await nodes.identify(path), fs));
|
|
28565
29523
|
await this.fds.get(fd).file.stat?.();
|
|
28566
|
-
if (
|
|
28567
|
-
else if (
|
|
29524
|
+
if (fresh4 && plain3 !== void 0) await fs.chmod?.(path, plain3);
|
|
29525
|
+
else if (fresh4) {
|
|
28568
29526
|
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
28569
29527
|
}
|
|
28570
29528
|
});
|
|
@@ -28663,8 +29621,8 @@ var WasmProcess = class {
|
|
|
28663
29621
|
if (req.exclusive) return inParent(g, "open", req.path);
|
|
28664
29622
|
if (req.created && guard2.created?.take(req.path)) return;
|
|
28665
29623
|
const access2 = req.flags & WRITE_ACCESS;
|
|
28666
|
-
const
|
|
28667
|
-
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);
|
|
28668
29626
|
}
|
|
28669
29627
|
openTty(name2) {
|
|
28670
29628
|
const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
|
|
@@ -28728,7 +29686,7 @@ var WasmProcess = class {
|
|
|
28728
29686
|
}
|
|
28729
29687
|
terminalByName(name2) {
|
|
28730
29688
|
const n = ptsNumber(name2);
|
|
28731
|
-
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;
|
|
28732
29690
|
if (!tty) throw new KernelError("ENOENT");
|
|
28733
29691
|
return tty;
|
|
28734
29692
|
}
|
|
@@ -28850,6 +29808,16 @@ var WasmProcess = class {
|
|
|
28850
29808
|
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
28851
29809
|
case "fd-open-embedder":
|
|
28852
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
|
+
}
|
|
28853
29821
|
case "fd-clipboard-abort": {
|
|
28854
29822
|
const file = this.fds.get(req.fd);
|
|
28855
29823
|
file.file.clipboard?.abort();
|
|
@@ -28894,6 +29862,11 @@ var WasmProcess = class {
|
|
|
28894
29862
|
return { ok: true, kind: "void" };
|
|
28895
29863
|
}
|
|
28896
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
|
+
}
|
|
28897
29870
|
if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
|
|
28898
29871
|
switch (req.op) {
|
|
28899
29872
|
case "fd-pread":
|
|
@@ -29063,14 +30036,19 @@ var WasmProcess = class {
|
|
|
29063
30036
|
...file.fifo !== void 0 ? { fifo: file.fifo } : {}
|
|
29064
30037
|
};
|
|
29065
30038
|
}
|
|
29066
|
-
ttySyscall(req) {
|
|
30039
|
+
async ttySyscall(req, cancelled) {
|
|
29067
30040
|
const tty = this.tty(req.fd);
|
|
29068
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");
|
|
29069
30044
|
switch (req.op) {
|
|
29070
30045
|
case "tty-get":
|
|
29071
30046
|
return { ok: true, kind: "json", json: tty.tcgets() };
|
|
29072
30047
|
case "tty-set":
|
|
29073
|
-
|
|
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);
|
|
29074
30052
|
return { ok: true, kind: "void" };
|
|
29075
30053
|
case "tty-winsz": {
|
|
29076
30054
|
const size = tty.winsize();
|
|
@@ -29951,6 +30929,7 @@ function processOptions(opts, header4, hooks, created4) {
|
|
|
29951
30929
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
29952
30930
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
29953
30931
|
...embedderOptions(opts),
|
|
30932
|
+
devices: opts.devices,
|
|
29954
30933
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
29955
30934
|
...opts.source ? { source: opts.source } : {},
|
|
29956
30935
|
noLinks: opts.noLinks,
|
|
@@ -30702,14 +31681,14 @@ var LockService = class {
|
|
|
30702
31681
|
);
|
|
30703
31682
|
});
|
|
30704
31683
|
}
|
|
30705
|
-
async holder(key, owner,
|
|
31684
|
+
async holder(key, owner, want2) {
|
|
30706
31685
|
return await this.ask({
|
|
30707
31686
|
op: "holder",
|
|
30708
31687
|
key,
|
|
30709
31688
|
owner,
|
|
30710
|
-
kind:
|
|
30711
|
-
start:
|
|
30712
|
-
end:
|
|
31689
|
+
kind: want2.kind,
|
|
31690
|
+
start: want2.start,
|
|
31691
|
+
end: want2.end
|
|
30713
31692
|
}) ?? void 0;
|
|
30714
31693
|
}
|
|
30715
31694
|
async set(key, owner, kind, start2, end) {
|
|
@@ -30988,9 +31967,9 @@ function local(name2) {
|
|
|
30988
31967
|
function literal2(name2) {
|
|
30989
31968
|
return ipv4Bytes(name2) !== void 0 || ipv6Groups(name2) !== void 0;
|
|
30990
31969
|
}
|
|
30991
|
-
function addressData(
|
|
30992
|
-
if (type === TYPE_A) return ipv4Bytes(
|
|
30993
|
-
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;
|
|
30994
31973
|
return groups?.flatMap((g) => [g >> 8, g & 255]);
|
|
30995
31974
|
}
|
|
30996
31975
|
var DnsStub = class {
|
|
@@ -31003,10 +31982,10 @@ var DnsStub = class {
|
|
|
31003
31982
|
this.timeoutMs = options.timeoutMs ?? DNS_FORWARD_TIMEOUT_MS;
|
|
31004
31983
|
}
|
|
31005
31984
|
async answer(query, udp) {
|
|
31006
|
-
const
|
|
31007
|
-
if (!
|
|
31008
|
-
if (!
|
|
31009
|
-
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;
|
|
31010
31989
|
const reply = await this.reply(query, q).catch(
|
|
31011
31990
|
() => answerReply(query, q, SERVFAIL, [], false)
|
|
31012
31991
|
);
|
|
@@ -31015,8 +31994,8 @@ var DnsStub = class {
|
|
|
31015
31994
|
async reply(query, q) {
|
|
31016
31995
|
if (q.klass !== CLASS_IN || literal2(q.name)) return this.forward(query, q);
|
|
31017
31996
|
if (q.type === TYPE_PTR) {
|
|
31018
|
-
const
|
|
31019
|
-
const name2 =
|
|
31997
|
+
const address2 = reverseAddress(q.name);
|
|
31998
|
+
const name2 = address2 !== void 0 ? await this.reverse(address2) : void 0;
|
|
31020
31999
|
if (name2 !== void 0)
|
|
31021
32000
|
return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
|
|
31022
32001
|
return this.forward(query, q);
|
|
@@ -31046,8 +32025,8 @@ var DnsStub = class {
|
|
|
31046
32025
|
known(name2) {
|
|
31047
32026
|
return this.resolver.known(name2).catch(() => false);
|
|
31048
32027
|
}
|
|
31049
|
-
reverse(
|
|
31050
|
-
return this.resolver.reverse(
|
|
32028
|
+
reverse(address2) {
|
|
32029
|
+
return this.resolver.reverse(address2).catch(() => void 0);
|
|
31051
32030
|
}
|
|
31052
32031
|
async forward(query, q) {
|
|
31053
32032
|
if (!this.forwarder) return answerReply(query, q, SERVFAIL, [], false);
|
|
@@ -31226,7 +32205,7 @@ function honors(traits, redirect) {
|
|
|
31226
32205
|
const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
|
|
31227
32206
|
return redirect === "manual" && raw2;
|
|
31228
32207
|
}
|
|
31229
|
-
function
|
|
32208
|
+
function refused4(message3) {
|
|
31230
32209
|
return new TransportError(message3, 502, void 0, "transport");
|
|
31231
32210
|
}
|
|
31232
32211
|
var RemoteTransport = class {
|
|
@@ -31317,7 +32296,7 @@ var RemoteTransport = class {
|
|
|
31317
32296
|
const { signal, redirect } = request3;
|
|
31318
32297
|
signal.throwIfAborted();
|
|
31319
32298
|
if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
|
|
31320
|
-
throw
|
|
32299
|
+
throw refused4(`this transport cannot honor redirect: ${redirect}`);
|
|
31321
32300
|
}
|
|
31322
32301
|
let finished = false;
|
|
31323
32302
|
const abort = () => {
|
|
@@ -31368,7 +32347,7 @@ var RemoteTransport = class {
|
|
|
31368
32347
|
};
|
|
31369
32348
|
if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
|
|
31370
32349
|
await cancel();
|
|
31371
|
-
throw
|
|
32350
|
+
throw refused4(`this transport followed a redirect despite redirect: ${redirect}`);
|
|
31372
32351
|
}
|
|
31373
32352
|
return {
|
|
31374
32353
|
...url !== void 0 ? { url } : {},
|
|
@@ -31858,6 +32837,7 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
|
|
|
31858
32837
|
"proc-list",
|
|
31859
32838
|
"proc-identity",
|
|
31860
32839
|
"fd-info",
|
|
32840
|
+
"fd-dev-nodes",
|
|
31861
32841
|
"sig-mask",
|
|
31862
32842
|
"sig-inherited",
|
|
31863
32843
|
"proc-setpgid",
|
|
@@ -31954,8 +32934,8 @@ var WasmMemories = class {
|
|
|
31954
32934
|
const learned = this.learned;
|
|
31955
32935
|
if (!learned) return this.base;
|
|
31956
32936
|
const steps = Math.floor((this.now() - learned.at) / DECAY_MS);
|
|
31957
|
-
const
|
|
31958
|
-
if (
|
|
32937
|
+
const raised2 = learned.ceiling + steps * DECAY_STEP;
|
|
32938
|
+
if (raised2 < (this.base ?? DEFAULT_CEILING)) return raised2;
|
|
31959
32939
|
this.learned = void 0;
|
|
31960
32940
|
return this.base;
|
|
31961
32941
|
}
|
|
@@ -32098,9 +33078,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
32098
33078
|
let open2 = false;
|
|
32099
33079
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
32100
33080
|
async function hello(message3) {
|
|
32101
|
-
const
|
|
32102
|
-
if (
|
|
32103
|
-
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" } });
|
|
32104
33084
|
return;
|
|
32105
33085
|
}
|
|
32106
33086
|
try {
|
|
@@ -32406,7 +33386,7 @@ function unescape(field4) {
|
|
|
32406
33386
|
);
|
|
32407
33387
|
}
|
|
32408
33388
|
function parseFstab(text) {
|
|
32409
|
-
const
|
|
33389
|
+
const lines3 = [];
|
|
32410
33390
|
text.split("\n").forEach((raw2, index) => {
|
|
32411
33391
|
const content = raw2.replace(/#.*$/, "").trim();
|
|
32412
33392
|
if (!content) return;
|
|
@@ -32415,12 +33395,12 @@ function parseFstab(text) {
|
|
|
32415
33395
|
if (options.split(",").includes("noauto")) return;
|
|
32416
33396
|
const spec = { source, target, type, options: {} };
|
|
32417
33397
|
try {
|
|
32418
|
-
|
|
33398
|
+
lines3.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
32419
33399
|
} catch (err) {
|
|
32420
|
-
|
|
33400
|
+
lines3.push({ line: index + 1, spec, invalid: err });
|
|
32421
33401
|
}
|
|
32422
33402
|
});
|
|
32423
|
-
return
|
|
33403
|
+
return lines3;
|
|
32424
33404
|
}
|
|
32425
33405
|
function codeOf6(err) {
|
|
32426
33406
|
const code = err?.code;
|
|
@@ -32438,20 +33418,20 @@ function wait(ms, signal) {
|
|
|
32438
33418
|
signal.addEventListener("abort", done5, { once: true });
|
|
32439
33419
|
});
|
|
32440
33420
|
}
|
|
32441
|
-
async function mountFstab(
|
|
33421
|
+
async function mountFstab(lines3, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
32442
33422
|
}, settled = () => {
|
|
32443
33423
|
}) {
|
|
32444
33424
|
return Promise.all(
|
|
32445
|
-
|
|
33425
|
+
lines3.map(async (each) => {
|
|
32446
33426
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
32447
33427
|
settled(each, result);
|
|
32448
33428
|
return result;
|
|
32449
33429
|
})
|
|
32450
33430
|
);
|
|
32451
33431
|
}
|
|
32452
|
-
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
32453
|
-
let failed4 =
|
|
32454
|
-
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++) {
|
|
32455
33435
|
try {
|
|
32456
33436
|
const entry = await mount(spec);
|
|
32457
33437
|
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
@@ -32619,14 +33599,14 @@ var RESTORE_AGAIN_MS = 3e4;
|
|
|
32619
33599
|
function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
32620
33600
|
const handles = /* @__PURE__ */ new Map();
|
|
32621
33601
|
let nextFh = 0;
|
|
32622
|
-
const
|
|
33602
|
+
const fresh4 = (remote) => {
|
|
32623
33603
|
if (remote.client.token === remote.token && !remote.client.gone) return remote;
|
|
32624
33604
|
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
32625
33605
|
};
|
|
32626
33606
|
const live2 = (fh) => {
|
|
32627
33607
|
const remote = handles.get(fh);
|
|
32628
33608
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
32629
|
-
return
|
|
33609
|
+
return fresh4(remote);
|
|
32630
33610
|
};
|
|
32631
33611
|
const call = (op, body = {}) => current().call(op, body);
|
|
32632
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);
|
|
@@ -32708,7 +33688,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32708
33688
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
32709
33689
|
handles.delete(fh);
|
|
32710
33690
|
try {
|
|
32711
|
-
await
|
|
33691
|
+
await fresh4(remote).client.call("release", { fh: remote.fh }, true);
|
|
32712
33692
|
} finally {
|
|
32713
33693
|
if (remote.restore) await settle3(current(), remote.restore);
|
|
32714
33694
|
}
|
|
@@ -32758,7 +33738,7 @@ async function resize(h, path, size) {
|
|
|
32758
33738
|
await h.release(fh);
|
|
32759
33739
|
}
|
|
32760
33740
|
}
|
|
32761
|
-
function
|
|
33741
|
+
function sleep2(ms, signal) {
|
|
32762
33742
|
return new Promise((resolve2) => {
|
|
32763
33743
|
const done5 = () => {
|
|
32764
33744
|
clearTimeout(timer);
|
|
@@ -32786,22 +33766,22 @@ function keepWatching(loop, signal) {
|
|
|
32786
33766
|
const stop = () => session2.abort();
|
|
32787
33767
|
signal.addEventListener("abort", stop);
|
|
32788
33768
|
let timer;
|
|
32789
|
-
const
|
|
33769
|
+
const quiet4 = () => {
|
|
32790
33770
|
clearTimeout(timer);
|
|
32791
33771
|
timer = setTimeout(stop, silenceMs);
|
|
32792
33772
|
};
|
|
32793
33773
|
let opened2 = false;
|
|
32794
33774
|
try {
|
|
32795
33775
|
const client = await loop.connect();
|
|
32796
|
-
|
|
33776
|
+
quiet4();
|
|
32797
33777
|
await client.watch(
|
|
32798
33778
|
session2.signal,
|
|
32799
33779
|
(event) => {
|
|
32800
|
-
|
|
33780
|
+
quiet4();
|
|
32801
33781
|
if (!event.ping) loop.event(event);
|
|
32802
33782
|
},
|
|
32803
33783
|
() => {
|
|
32804
|
-
|
|
33784
|
+
quiet4();
|
|
32805
33785
|
opened2 = true;
|
|
32806
33786
|
up = true;
|
|
32807
33787
|
loop.up(client);
|
|
@@ -32830,7 +33810,7 @@ function keepWatching(loop, signal) {
|
|
|
32830
33810
|
loop.down();
|
|
32831
33811
|
}
|
|
32832
33812
|
ready2.resolve();
|
|
32833
|
-
await
|
|
33813
|
+
await sleep2(wait2, signal);
|
|
32834
33814
|
wait2 = Math.min(wait2 * 2, most);
|
|
32835
33815
|
}
|
|
32836
33816
|
})();
|
|
@@ -34314,6 +35294,7 @@ var Launcher = class {
|
|
|
34314
35294
|
scrubs = /* @__PURE__ */ new Map();
|
|
34315
35295
|
clipboard;
|
|
34316
35296
|
embedder;
|
|
35297
|
+
devices;
|
|
34317
35298
|
rootFs;
|
|
34318
35299
|
fileWatches;
|
|
34319
35300
|
nextPid = 1e3;
|
|
@@ -34338,6 +35319,7 @@ var Launcher = class {
|
|
|
34338
35319
|
);
|
|
34339
35320
|
this.embedder = this.embedderBroker(options.embedder);
|
|
34340
35321
|
this.embedder.denied = options.onDenial;
|
|
35322
|
+
this.devices = this.deviceNodes(options.devices);
|
|
34341
35323
|
this.sealed = new SealedBroker(
|
|
34342
35324
|
options.sealed,
|
|
34343
35325
|
this.usersMode,
|
|
@@ -34553,16 +35535,16 @@ var Launcher = class {
|
|
|
34553
35535
|
if (!found) {
|
|
34554
35536
|
found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
|
|
34555
35537
|
const tiers = await this.tiers(scope, appended2);
|
|
34556
|
-
const
|
|
35538
|
+
const refused5 = [];
|
|
34557
35539
|
const commands = await scanCommands(
|
|
34558
35540
|
this.scanned,
|
|
34559
35541
|
tiers,
|
|
34560
35542
|
tiers.length - appended2.length,
|
|
34561
35543
|
void 0,
|
|
34562
|
-
|
|
35544
|
+
refused5,
|
|
34563
35545
|
await this.globalRoots(scope)
|
|
34564
35546
|
);
|
|
34565
|
-
this.refusedOf.set(commands,
|
|
35547
|
+
this.refusedOf.set(commands, refused5.map(refusing));
|
|
34566
35548
|
return commands;
|
|
34567
35549
|
}).then(
|
|
34568
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)
|
|
@@ -34769,14 +35751,14 @@ var Launcher = class {
|
|
|
34769
35751
|
return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
|
|
34770
35752
|
}
|
|
34771
35753
|
async refusedAt(path, name2, scope) {
|
|
34772
|
-
const
|
|
34773
|
-
if (
|
|
35754
|
+
const refused5 = this.refusedOf.get(await this.commands(scope)) ?? [];
|
|
35755
|
+
if (refused5.length === 0) return void 0;
|
|
34774
35756
|
const st = await this.base.stat(path).catch(() => void 0);
|
|
34775
35757
|
if (!st?.isFile) return void 0;
|
|
34776
35758
|
const real = await physicalPathAsync(this.base, path).catch(() => path);
|
|
34777
35759
|
const same3 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
|
|
34778
35760
|
const owners = [];
|
|
34779
|
-
for (const c of
|
|
35761
|
+
for (const c of refused5) {
|
|
34780
35762
|
const origin = c.origin;
|
|
34781
35763
|
for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
|
|
34782
35764
|
if (!await same3(own)) continue;
|
|
@@ -34786,11 +35768,11 @@ var Launcher = class {
|
|
|
34786
35768
|
}
|
|
34787
35769
|
const stem = name2.replace(/\.wasm$/, "");
|
|
34788
35770
|
const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
|
|
34789
|
-
return named ?? owners[0] ?? await this.refusedCopy(real, name2,
|
|
35771
|
+
return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused5);
|
|
34790
35772
|
}
|
|
34791
|
-
async refusedCopy(real, name2,
|
|
35773
|
+
async refusedCopy(real, name2, refused5) {
|
|
34792
35774
|
const dir = real.slice(0, real.lastIndexOf("/"));
|
|
34793
|
-
for (const c of
|
|
35775
|
+
for (const c of refused5) {
|
|
34794
35776
|
const origin = c.origin;
|
|
34795
35777
|
if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
|
|
34796
35778
|
const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
|
|
@@ -34957,8 +35939,8 @@ var Launcher = class {
|
|
|
34957
35939
|
const via = planned?.via ?? [];
|
|
34958
35940
|
for (const [i, file] of via.entries()) {
|
|
34959
35941
|
if (!byPath(file)) continue;
|
|
34960
|
-
const
|
|
34961
|
-
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);
|
|
34962
35944
|
}
|
|
34963
35945
|
for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
|
|
34964
35946
|
if (planned?.target.copy) await need(g, "exec", planned.target.copy, R_OK | X_OK);
|
|
@@ -34998,12 +35980,12 @@ var Launcher = class {
|
|
|
34998
35980
|
return void 0;
|
|
34999
35981
|
}
|
|
35000
35982
|
async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
|
|
35001
|
-
const
|
|
35983
|
+
const refused5 = (err) => {
|
|
35002
35984
|
throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
|
|
35003
35985
|
};
|
|
35004
|
-
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);
|
|
35005
35987
|
const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
|
|
35006
|
-
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(
|
|
35988
|
+
await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused5);
|
|
35007
35989
|
if (planned) return planned;
|
|
35008
35990
|
throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
|
|
35009
35991
|
}
|
|
@@ -35639,6 +36621,7 @@ var Launcher = class {
|
|
|
35639
36621
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
35640
36622
|
clipboard: this.clipboard,
|
|
35641
36623
|
embedder: { broker: this.embedder, session: () => this.sessionOf(pid) },
|
|
36624
|
+
devices: this.devices,
|
|
35642
36625
|
...this.dac ? { dac: this.dac } : {},
|
|
35643
36626
|
program: req.program,
|
|
35644
36627
|
changes: this.changes.sab,
|
|
@@ -35822,8 +36805,8 @@ var Launcher = class {
|
|
|
35822
36805
|
this.sampledAt = Math.max(this.sampledAt, now);
|
|
35823
36806
|
this.load = this.load.map((avg, i) => {
|
|
35824
36807
|
const window = LOAD_WINDOWS_MS[i];
|
|
35825
|
-
const
|
|
35826
|
-
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;
|
|
35827
36810
|
});
|
|
35828
36811
|
}
|
|
35829
36812
|
idleSince;
|
|
@@ -36170,7 +37153,7 @@ var Launcher = class {
|
|
|
36170
37153
|
forker(ppid, parent) {
|
|
36171
37154
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
36172
37155
|
return async (state, given) => {
|
|
36173
|
-
const
|
|
37156
|
+
const fresh4 = (fds, quietOom) => this.start({
|
|
36174
37157
|
...image,
|
|
36175
37158
|
quietOom,
|
|
36176
37159
|
cwd: state.cwd ?? parent.cwd,
|
|
@@ -36186,7 +37169,7 @@ var Launcher = class {
|
|
|
36186
37169
|
tagBase: this.tagBases.get(ppid),
|
|
36187
37170
|
links: this.links.get(ppid)
|
|
36188
37171
|
});
|
|
36189
|
-
return childHandle(await this.admitted(parent.program.abi !== "js", given,
|
|
37172
|
+
return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh4));
|
|
36190
37173
|
};
|
|
36191
37174
|
}
|
|
36192
37175
|
keySignal(tty, fallback, sig, send2) {
|
|
@@ -36227,6 +37210,14 @@ var Launcher = class {
|
|
|
36227
37210
|
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
36228
37211
|
this.sealed.factsOf = (pid) => this.processFacts(pid);
|
|
36229
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
|
+
}
|
|
36230
37221
|
embedderBroker(hook) {
|
|
36231
37222
|
const broker = new EmbedderBroker(
|
|
36232
37223
|
this.usersMode,
|
|
@@ -36516,8 +37507,8 @@ var Launcher = class {
|
|
|
36516
37507
|
for (const [path, mode] of layout) {
|
|
36517
37508
|
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
36518
37509
|
const real = await this.realLayoutPath(path);
|
|
36519
|
-
const
|
|
36520
|
-
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 });
|
|
36521
37512
|
}
|
|
36522
37513
|
}
|
|
36523
37514
|
async realLayoutPath(given) {
|
|
@@ -36651,10 +37642,10 @@ var Launcher = class {
|
|
|
36651
37642
|
}
|
|
36652
37643
|
async mountFstab() {
|
|
36653
37644
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
36654
|
-
const
|
|
36655
|
-
for (const { spec } of
|
|
37645
|
+
const lines3 = parseFstab(text);
|
|
37646
|
+
for (const { spec } of lines3) this.mounts.note(spec, "pending");
|
|
36656
37647
|
return mountFstab(
|
|
36657
|
-
|
|
37648
|
+
lines3,
|
|
36658
37649
|
(spec) => this.mount(spec).catch((err) => {
|
|
36659
37650
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
36660
37651
|
throw err;
|
|
@@ -37551,7 +38542,7 @@ function isAttestRequest(req) {
|
|
|
37551
38542
|
return ATTEST_OPS.has(req.op);
|
|
37552
38543
|
}
|
|
37553
38544
|
function isUsersRequest(req) {
|
|
37554
|
-
return USERS_OPS.has(req.op);
|
|
38545
|
+
return USERS_OPS.has(req.op) || req.op === "devices-add" || req.op === "devices-remove";
|
|
37555
38546
|
}
|
|
37556
38547
|
function transferOf2(req, result) {
|
|
37557
38548
|
if (result instanceof MessagePort) return [result];
|
|
@@ -37584,7 +38575,18 @@ function failure(err) {
|
|
|
37584
38575
|
...typeof code === "string" ? { code } : {}
|
|
37585
38576
|
};
|
|
37586
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
|
+
}
|
|
37587
38588
|
function usersOp(l, req) {
|
|
38589
|
+
if (req.op === "devices-add" || req.op === "devices-remove") return devicesOp(l, req);
|
|
37588
38590
|
if (req.op === "users-add") return l.users.add(req.user);
|
|
37589
38591
|
if (req.op === "users-remove") return l.users.remove(req.name, req.options);
|
|
37590
38592
|
if (req.op === "groups-list") return l.users.groups();
|
|
@@ -37594,7 +38596,7 @@ function usersOp(l, req) {
|
|
|
37594
38596
|
return l.users.list();
|
|
37595
38597
|
}
|
|
37596
38598
|
var clientIds = 0;
|
|
37597
|
-
async function connectClient(deps, { user, tag,
|
|
38599
|
+
async function connectClient(deps, { user, tag, requests: bind2, devices }) {
|
|
37598
38600
|
const l = await deps.ready();
|
|
37599
38601
|
tagOption(tag);
|
|
37600
38602
|
if (user !== void 0 && !await l.users.lookup(user)) {
|
|
@@ -37608,6 +38610,7 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
|
|
|
37608
38610
|
signal: signalNumber,
|
|
37609
38611
|
session: client,
|
|
37610
38612
|
...bind2 === "attach" ? { attachRequests: true } : {},
|
|
38613
|
+
...devices === "attach" ? { attachDevices: true } : {},
|
|
37611
38614
|
...user !== void 0 ? { user } : {},
|
|
37612
38615
|
...tag !== void 0 ? { tag } : {},
|
|
37613
38616
|
...deps.locks ? { locks: deps.locks } : {},
|
|
@@ -37615,13 +38618,15 @@ async function connectClient(deps, { user, tag, bind: bind2 }) {
|
|
|
37615
38618
|
});
|
|
37616
38619
|
deps.clients.add(served2);
|
|
37617
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;
|
|
37618
38622
|
void served2.closed.then(() => {
|
|
37619
38623
|
deps.clients.delete(served2);
|
|
37620
38624
|
unbind?.();
|
|
38625
|
+
unbindDevices?.();
|
|
37621
38626
|
});
|
|
37622
|
-
return unbind ? { port: port2, client } : port2;
|
|
38627
|
+
return unbind || devices ? { port: port2, client } : port2;
|
|
37623
38628
|
}
|
|
37624
|
-
function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests) {
|
|
38629
|
+
function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, requests, devices) {
|
|
37625
38630
|
return {
|
|
37626
38631
|
cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
|
|
37627
38632
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
@@ -37633,7 +38638,8 @@ function hostOptions(req, port, sealed, sealedFetch, clipboard, lookup2, request
|
|
|
37633
38638
|
...req.sealed ? { sealed } : {},
|
|
37634
38639
|
...req.sealedFetch ? { sealedFetch } : {},
|
|
37635
38640
|
...req.clipboard ? { clipboard } : {},
|
|
37636
|
-
...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {}
|
|
38641
|
+
...req.requests ? { embedder: requests.hookFor(0, req.requests) } : {},
|
|
38642
|
+
...req.devices ? { devices: devices.hookFor(0) } : {}
|
|
37637
38643
|
};
|
|
37638
38644
|
}
|
|
37639
38645
|
function hostProtect(init, lookup2) {
|
|
@@ -37687,6 +38693,20 @@ function processOps(ready2, reply) {
|
|
|
37687
38693
|
}
|
|
37688
38694
|
return { terminal, sudoRun, act: onTerminal };
|
|
37689
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
|
+
}
|
|
37690
38710
|
function serveKernel(port, deps) {
|
|
37691
38711
|
let launcher;
|
|
37692
38712
|
let remote;
|
|
@@ -37696,24 +38716,14 @@ function serveKernel(port, deps) {
|
|
|
37696
38716
|
const sealed = sealedAsker(port);
|
|
37697
38717
|
const clipboard = clipboardAsker(port);
|
|
37698
38718
|
const requests = embedderAsker(port);
|
|
38719
|
+
const devices = devicesAsker(port);
|
|
37699
38720
|
const integrity = integrityAsker(port);
|
|
37700
38721
|
const approvals = sudoApprovals(port);
|
|
37701
38722
|
const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
|
|
37702
|
-
|
|
37703
|
-
|
|
37704
|
-
|
|
37705
|
-
|
|
37706
|
-
void locks2.request(name2, () => {
|
|
37707
|
-
resolve2(name2);
|
|
37708
|
-
return new Promise(() => {
|
|
37709
|
-
});
|
|
37710
|
-
});
|
|
37711
|
-
});
|
|
37712
|
-
return holding;
|
|
37713
|
-
}
|
|
37714
|
-
const connect2 = (user, tag, bind2) => connectClient(
|
|
37715
|
-
{ ready: ready2, clients, requests, held: locks ? () => holdLock(locks) : void 0, locks },
|
|
37716
|
-
{ 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
|
|
37717
38727
|
);
|
|
37718
38728
|
async function ready2() {
|
|
37719
38729
|
if (!launcher) throw new Error("the kernel is not initialized");
|
|
@@ -37744,7 +38754,8 @@ function serveKernel(port, deps) {
|
|
|
37744
38754
|
sealed.fetch,
|
|
37745
38755
|
clipboard.hook,
|
|
37746
38756
|
integrity.lookup,
|
|
37747
|
-
requests
|
|
38757
|
+
requests,
|
|
38758
|
+
devices
|
|
37748
38759
|
),
|
|
37749
38760
|
...req.approve ? { approve: approvals.ask } : {},
|
|
37750
38761
|
...net,
|
|
@@ -37776,7 +38787,7 @@ function serveKernel(port, deps) {
|
|
|
37776
38787
|
if (req.op === "sealed-placeholders") return (await ready2()).setPlaceholders(req.list);
|
|
37777
38788
|
if (req.op === "open-terminal") return processes2.terminal(req);
|
|
37778
38789
|
if (req.op === "sudo-run") return processes2.sudoRun(req);
|
|
37779
|
-
if (req.op === "connect") return connect2(req
|
|
38790
|
+
if (req.op === "connect") return connect2(req);
|
|
37780
38791
|
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
37781
38792
|
if (req.op === "routes" || req.op === "transport-traits") {
|
|
37782
38793
|
return networkOp(await ready2(), req, remote);
|
|
@@ -37813,8 +38824,7 @@ function serveKernel(port, deps) {
|
|
|
37813
38824
|
if (sealedReply) return sealed.answer(sealedReply);
|
|
37814
38825
|
const clipboardReply = event.data.clipboardReply;
|
|
37815
38826
|
if (clipboardReply) return clipboard.answer(clipboardReply);
|
|
37816
|
-
|
|
37817
|
-
if (embedderReply) return requests.answer(embedderReply);
|
|
38827
|
+
if (pageReplied(event.data, requests, devices)) return;
|
|
37818
38828
|
const integrityReply = event.data.integrityReply;
|
|
37819
38829
|
if (integrityReply) return integrity.answer(integrityReply);
|
|
37820
38830
|
const grant = event.data.hostfsGrant;
|