@ai-ecoverse/slicc-kernel 1.179.0 → 1.181.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 +41 -2
- package/dist/driver-worker.js +67 -1
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +78 -1
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +553 -93
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +67 -1
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +619 -90
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +1 -1
- package/dist/types/client/attach.d.ts +27 -3
- package/dist/types/client/protocol.d.ts +29 -4
- package/dist/types/index.d.ts +5 -3
- package/dist/types/kernel/children.d.ts +2 -1
- package/dist/types/kernel/jobs.d.ts +2 -1
- package/dist/types/kernel/trace.d.ts +59 -0
- package/dist/types/launcher.d.ts +31 -0
- package/dist/types/node.d.ts +5 -3
- package/dist/types/serve.d.ts +11 -1
- package/dist/types/sudo.d.ts +15 -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 strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
656
|
+
return { paths: strings2(notice.paths), trees: strings2(notice.trees), outside: true };
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
// src/fs/types.ts
|
|
@@ -7451,6 +7451,82 @@ var KERNEL_SIGINFO = Object.freeze({
|
|
|
7451
7451
|
status: 0
|
|
7452
7452
|
});
|
|
7453
7453
|
|
|
7454
|
+
// src/kernel/trace.ts
|
|
7455
|
+
var TRACE_LIMIT = 4096;
|
|
7456
|
+
var TRACE_LIMIT_MAX = 65536;
|
|
7457
|
+
function traceLimit(value2) {
|
|
7458
|
+
if (value2 === void 0) return TRACE_LIMIT;
|
|
7459
|
+
if (!Number.isInteger(value2) || value2 < 1 || value2 > TRACE_LIMIT_MAX) {
|
|
7460
|
+
throw Object.assign(new Error(`a trace limit is an integer from 1 to ${TRACE_LIMIT_MAX}`), {
|
|
7461
|
+
code: "EINVAL"
|
|
7462
|
+
});
|
|
7463
|
+
}
|
|
7464
|
+
return value2;
|
|
7465
|
+
}
|
|
7466
|
+
var TraceHub = class {
|
|
7467
|
+
subs = /* @__PURE__ */ new Set();
|
|
7468
|
+
seq = 0;
|
|
7469
|
+
get idle() {
|
|
7470
|
+
return this.subs.size === 0;
|
|
7471
|
+
}
|
|
7472
|
+
subscribe(tag, viewer, deliver, limit = TRACE_LIMIT) {
|
|
7473
|
+
const sub = {
|
|
7474
|
+
tag,
|
|
7475
|
+
viewer: copyCred(viewer),
|
|
7476
|
+
limit,
|
|
7477
|
+
queue: [],
|
|
7478
|
+
dropped: 0,
|
|
7479
|
+
waiting: false,
|
|
7480
|
+
scheduled: false,
|
|
7481
|
+
deliver
|
|
7482
|
+
};
|
|
7483
|
+
this.subs.add(sub);
|
|
7484
|
+
return {
|
|
7485
|
+
ack: () => {
|
|
7486
|
+
sub.waiting = false;
|
|
7487
|
+
this.schedule(sub);
|
|
7488
|
+
},
|
|
7489
|
+
close: () => {
|
|
7490
|
+
this.subs.delete(sub);
|
|
7491
|
+
sub.queue = [];
|
|
7492
|
+
}
|
|
7493
|
+
};
|
|
7494
|
+
}
|
|
7495
|
+
wants(tags, cred) {
|
|
7496
|
+
if (!tags?.length || this.subs.size === 0) return false;
|
|
7497
|
+
for (const sub of this.subs) if (this.matches(sub, tags, cred)) return true;
|
|
7498
|
+
return false;
|
|
7499
|
+
}
|
|
7500
|
+
publish(tags, cred, draft) {
|
|
7501
|
+
if (!tags?.length) return;
|
|
7502
|
+
let event;
|
|
7503
|
+
for (const sub of this.subs) {
|
|
7504
|
+
if (!this.matches(sub, tags, cred)) continue;
|
|
7505
|
+
event ??= { ...draft, seq: ++this.seq };
|
|
7506
|
+
if (sub.queue.length >= sub.limit) sub.dropped++;
|
|
7507
|
+
else sub.queue.push(event);
|
|
7508
|
+
this.schedule(sub);
|
|
7509
|
+
}
|
|
7510
|
+
}
|
|
7511
|
+
matches(sub, tags, cred) {
|
|
7512
|
+
return tags.includes(sub.tag) && maySee(sub.viewer, cred);
|
|
7513
|
+
}
|
|
7514
|
+
schedule(sub) {
|
|
7515
|
+
if (sub.scheduled || sub.waiting) return;
|
|
7516
|
+
if (sub.queue.length === 0 && sub.dropped === 0) return;
|
|
7517
|
+
sub.scheduled = true;
|
|
7518
|
+
queueMicrotask(() => {
|
|
7519
|
+
sub.scheduled = false;
|
|
7520
|
+
if (!this.subs.has(sub)) return;
|
|
7521
|
+
const batch = { events: sub.queue, dropped: sub.dropped };
|
|
7522
|
+
sub.queue = [];
|
|
7523
|
+
sub.dropped = 0;
|
|
7524
|
+
sub.waiting = true;
|
|
7525
|
+
sub.deliver(batch);
|
|
7526
|
+
});
|
|
7527
|
+
}
|
|
7528
|
+
};
|
|
7529
|
+
|
|
7454
7530
|
// src/body-idle.ts
|
|
7455
7531
|
var BODY_IDLE_MS = 5 * 60 * 1e3;
|
|
7456
7532
|
|
|
@@ -8130,7 +8206,7 @@ var UserDb = class {
|
|
|
8130
8206
|
};
|
|
8131
8207
|
|
|
8132
8208
|
// src/client/protocol.ts
|
|
8133
|
-
var PROTOCOL = [1,
|
|
8209
|
+
var PROTOCOL = [1, 16];
|
|
8134
8210
|
var FS_METHODS = [
|
|
8135
8211
|
"readFile",
|
|
8136
8212
|
"writeFile",
|
|
@@ -8286,9 +8362,9 @@ async function accountOf(l, user) {
|
|
|
8286
8362
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
8287
8363
|
return found;
|
|
8288
8364
|
}
|
|
8289
|
-
async function runAs(l, self,
|
|
8290
|
-
if (
|
|
8291
|
-
const target = await accountOf(l,
|
|
8365
|
+
async function runAs(l, self, requested2) {
|
|
8366
|
+
if (requested2 === void 0) return self;
|
|
8367
|
+
const target = await accountOf(l, requested2);
|
|
8292
8368
|
if (self.uid !== 0 && target.uid !== self.uid) {
|
|
8293
8369
|
throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
|
|
8294
8370
|
}
|
|
@@ -8298,8 +8374,8 @@ function clientCred(self) {
|
|
|
8298
8374
|
return self ? credOf(self) : copyCred(ROOT);
|
|
8299
8375
|
}
|
|
8300
8376
|
async function runTagged(l, self, options, tag, session = -1) {
|
|
8301
|
-
|
|
8302
|
-
const tags = withTag(
|
|
8377
|
+
let tags = withTag(void 0, tag);
|
|
8378
|
+
for (const added of tagList(options.tag)) tags = withTag(tags, added);
|
|
8303
8379
|
return {
|
|
8304
8380
|
user: await runAs(l, self, options.user),
|
|
8305
8381
|
tag,
|
|
@@ -8430,6 +8506,34 @@ async function watchOp(l, req, watches, send2, g) {
|
|
|
8430
8506
|
watches.set(req.id, unwatch);
|
|
8431
8507
|
return { result: req.id };
|
|
8432
8508
|
}
|
|
8509
|
+
function traceRegistry(send2) {
|
|
8510
|
+
const traces = /* @__PURE__ */ new Map();
|
|
8511
|
+
return {
|
|
8512
|
+
answer(l, self, req) {
|
|
8513
|
+
if (req.op === "untrace") {
|
|
8514
|
+
traces.get(req.trace)?.close();
|
|
8515
|
+
return { result: traces.delete(req.trace) };
|
|
8516
|
+
}
|
|
8517
|
+
if (typeof req.tag !== "string" || req.tag === "") {
|
|
8518
|
+
throw fsError("EINVAL", "trace: a tag is a non-empty string");
|
|
8519
|
+
}
|
|
8520
|
+
const limit = traceLimit(req.limit);
|
|
8521
|
+
const id2 = req.id;
|
|
8522
|
+
const deliver = ({ events, dropped: dropped2 }) => send2({ trace: id2, events, ...dropped2 > 0 ? { dropped: dropped2 } : {} });
|
|
8523
|
+
traces.set(id2, l.traces.subscribe(req.tag, credOf(self), deliver, limit));
|
|
8524
|
+
return { result: id2 };
|
|
8525
|
+
},
|
|
8526
|
+
ack(data) {
|
|
8527
|
+
if (typeof data.traceAck !== "number") return false;
|
|
8528
|
+
traces.get(data.traceAck)?.ack();
|
|
8529
|
+
return true;
|
|
8530
|
+
},
|
|
8531
|
+
close() {
|
|
8532
|
+
for (const trace of traces.values()) trace.close();
|
|
8533
|
+
traces.clear();
|
|
8534
|
+
}
|
|
8535
|
+
};
|
|
8536
|
+
}
|
|
8433
8537
|
var TRAITS_WATCH = -1;
|
|
8434
8538
|
function traitsWatch(launcher, transport, send2) {
|
|
8435
8539
|
return launcher.onTraits(() => send2({ traits: transport.traits }));
|
|
@@ -8456,6 +8560,35 @@ function requestBinding(send2, port) {
|
|
|
8456
8560
|
close: () => unbind?.()
|
|
8457
8561
|
};
|
|
8458
8562
|
}
|
|
8563
|
+
async function sudoRunAs(l, self, host, req, reply) {
|
|
8564
|
+
if (self.uid !== 0) throw sudoFailure("EPERM", "sudo: only a root client may run a sudo request");
|
|
8565
|
+
const { stdout, stderr, tag } = req.options;
|
|
8566
|
+
const running = await l.sudoRun(req.replay, {
|
|
8567
|
+
stdout,
|
|
8568
|
+
stderr,
|
|
8569
|
+
tag,
|
|
8570
|
+
base: host.tag,
|
|
8571
|
+
session: host.session
|
|
8572
|
+
});
|
|
8573
|
+
reply(req.id, { started: running.pid });
|
|
8574
|
+
const { status, signal } = await running.ended;
|
|
8575
|
+
return { result: status, ...signalField(signal) };
|
|
8576
|
+
}
|
|
8577
|
+
function answered(pending, req, reply, detach) {
|
|
8578
|
+
pending.then(
|
|
8579
|
+
({ result, signal, transfer }) => {
|
|
8580
|
+
reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
|
|
8581
|
+
if (req.op === "detach") detach(req.kill === true);
|
|
8582
|
+
},
|
|
8583
|
+
(err) => {
|
|
8584
|
+
const code = err?.code;
|
|
8585
|
+
reply(req.id, {
|
|
8586
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8587
|
+
...typeof code === "string" ? { code } : {}
|
|
8588
|
+
});
|
|
8589
|
+
}
|
|
8590
|
+
);
|
|
8591
|
+
}
|
|
8459
8592
|
function serveClient(port, host) {
|
|
8460
8593
|
let state = "new";
|
|
8461
8594
|
let self;
|
|
@@ -8471,6 +8604,7 @@ function serveClient(port, host) {
|
|
|
8471
8604
|
};
|
|
8472
8605
|
const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
|
|
8473
8606
|
const hosts = hostRegistries(send2, ended.promise, port);
|
|
8607
|
+
const traces = traceRegistry(send2);
|
|
8474
8608
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
8475
8609
|
if (state === "closed") return;
|
|
8476
8610
|
send2({ bye });
|
|
@@ -8478,6 +8612,7 @@ function serveClient(port, host) {
|
|
|
8478
8612
|
net?.close();
|
|
8479
8613
|
for (const unwatch of watches.values()) unwatch();
|
|
8480
8614
|
watches.clear();
|
|
8615
|
+
traces.close();
|
|
8481
8616
|
hosts.close();
|
|
8482
8617
|
for (const session of terminals.values()) session.close();
|
|
8483
8618
|
terminals.clear();
|
|
@@ -8585,6 +8720,11 @@ function serveClient(port, host) {
|
|
|
8585
8720
|
case "serve-facade":
|
|
8586
8721
|
case "unserve-facade":
|
|
8587
8722
|
return { result: hosts.answer(l, req) };
|
|
8723
|
+
case "trace":
|
|
8724
|
+
case "untrace":
|
|
8725
|
+
return traces.answer(l, self, req);
|
|
8726
|
+
case "sudo-run":
|
|
8727
|
+
return sudoRunAs(l, self, host, req, reply);
|
|
8588
8728
|
case "detach":
|
|
8589
8729
|
return { result: true };
|
|
8590
8730
|
default:
|
|
@@ -8608,20 +8748,9 @@ function serveClient(port, host) {
|
|
|
8608
8748
|
net?.answer(data);
|
|
8609
8749
|
return;
|
|
8610
8750
|
}
|
|
8751
|
+
if (traces.ack(data)) return;
|
|
8611
8752
|
const req = data;
|
|
8612
|
-
handle(req)
|
|
8613
|
-
({ result, signal, transfer }) => {
|
|
8614
|
-
reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
|
|
8615
|
-
if (req.op === "detach") detach(req.kill === true);
|
|
8616
|
-
},
|
|
8617
|
-
(err) => {
|
|
8618
|
-
const code = err?.code;
|
|
8619
|
-
reply(req.id, {
|
|
8620
|
-
error: err instanceof Error ? err.message : String(err),
|
|
8621
|
-
...typeof code === "string" ? { code } : {}
|
|
8622
|
-
});
|
|
8623
|
-
}
|
|
8624
|
-
);
|
|
8753
|
+
answered(handle(req), req, reply, detach);
|
|
8625
8754
|
});
|
|
8626
8755
|
port.addEventListener("close", () => detach());
|
|
8627
8756
|
port.start?.();
|
|
@@ -8779,7 +8908,7 @@ function satisfies(version, range2) {
|
|
|
8779
8908
|
}
|
|
8780
8909
|
|
|
8781
8910
|
// src/version.ts
|
|
8782
|
-
var KERNEL_VERSION = true ? "1.
|
|
8911
|
+
var KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
|
|
8783
8912
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8784
8913
|
function procVersion() {
|
|
8785
8914
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9644,27 +9773,27 @@ function hashId(text) {
|
|
|
9644
9773
|
return hex4(a) + hex4(b);
|
|
9645
9774
|
}
|
|
9646
9775
|
function split2(text, count2) {
|
|
9647
|
-
const
|
|
9776
|
+
const words3 = [];
|
|
9648
9777
|
let rest2 = text;
|
|
9649
9778
|
for (let i = 0; i < count2; i++) {
|
|
9650
9779
|
const match = /^(\S+)\s+/.exec(rest2);
|
|
9651
9780
|
if (!match) return void 0;
|
|
9652
|
-
|
|
9781
|
+
words3.push(match[1]);
|
|
9653
9782
|
rest2 = rest2.slice(match[0].length);
|
|
9654
9783
|
}
|
|
9655
|
-
return [
|
|
9784
|
+
return [words3, rest2.trim()];
|
|
9656
9785
|
}
|
|
9657
9786
|
function jobOf(content, pending, ctx, env) {
|
|
9658
9787
|
const fields = content.startsWith("@") ? 1 : 5;
|
|
9659
9788
|
const parts2 = split2(content, fields + (ctx.system ? 1 : 0));
|
|
9660
9789
|
if (!parts2 || parts2[1] === "") throw new Error("no command");
|
|
9661
|
-
const [
|
|
9662
|
-
const schedule =
|
|
9790
|
+
const [words3, command] = parts2;
|
|
9791
|
+
const schedule = words3.slice(0, fields).join(" ");
|
|
9663
9792
|
const when = parseWhen(schedule);
|
|
9664
9793
|
const zone = env.CRON_TZ;
|
|
9665
9794
|
if (zone && !when.reboot && canonicalZone(zone) === void 0)
|
|
9666
9795
|
throw new Error(`CRON_TZ: unknown time zone "${zone}"`);
|
|
9667
|
-
const user = ctx.system ?
|
|
9796
|
+
const user = ctx.system ? words3[fields] : void 0;
|
|
9668
9797
|
if (user !== void 0 && !USER_NAME.test(user)) throw new Error(`bad user name ${user}`);
|
|
9669
9798
|
if (pending !== void 0 && !JOB_NAME.test(pending))
|
|
9670
9799
|
throw new Error(`bad job name "${pending}"`);
|
|
@@ -12645,9 +12774,11 @@ var SpawnError = class extends Error {
|
|
|
12645
12774
|
};
|
|
12646
12775
|
var ChangedError = class extends SpawnError {
|
|
12647
12776
|
notice;
|
|
12648
|
-
|
|
12777
|
+
file;
|
|
12778
|
+
constructor(notice, file) {
|
|
12649
12779
|
super("EPERM");
|
|
12650
12780
|
this.notice = notice;
|
|
12781
|
+
this.file = file;
|
|
12651
12782
|
}
|
|
12652
12783
|
};
|
|
12653
12784
|
function interrupted(signal) {
|
|
@@ -19693,21 +19824,21 @@ function unescaped(double) {
|
|
|
19693
19824
|
}
|
|
19694
19825
|
function splitWords(text) {
|
|
19695
19826
|
const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
|
|
19696
|
-
const
|
|
19827
|
+
const words3 = [];
|
|
19697
19828
|
let word;
|
|
19698
19829
|
while (piece.lastIndex < text.length) {
|
|
19699
19830
|
const m = piece.exec(text);
|
|
19700
19831
|
if (!m) return void 0;
|
|
19701
19832
|
const [, single, double, escaped, plain3, space] = m;
|
|
19702
19833
|
if (space !== void 0) {
|
|
19703
|
-
if (word !== void 0)
|
|
19834
|
+
if (word !== void 0) words3.push(word);
|
|
19704
19835
|
word = void 0;
|
|
19705
19836
|
continue;
|
|
19706
19837
|
}
|
|
19707
19838
|
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
|
|
19708
19839
|
}
|
|
19709
|
-
if (word !== void 0)
|
|
19710
|
-
return
|
|
19840
|
+
if (word !== void 0) words3.push(word);
|
|
19841
|
+
return words3;
|
|
19711
19842
|
}
|
|
19712
19843
|
function quoteWord(word) {
|
|
19713
19844
|
const text = hasControl(word) ? withoutControl(word) : word;
|
|
@@ -19728,14 +19859,14 @@ function addPlain(word, plain3) {
|
|
|
19728
19859
|
}
|
|
19729
19860
|
function splitPatterns(text) {
|
|
19730
19861
|
const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
|
|
19731
|
-
const
|
|
19862
|
+
const words3 = [];
|
|
19732
19863
|
let word;
|
|
19733
19864
|
while (piece.lastIndex < text.length) {
|
|
19734
19865
|
const m = piece.exec(text);
|
|
19735
19866
|
if (!m) return void 0;
|
|
19736
19867
|
const [, single, double, escaped, plain3, space] = m;
|
|
19737
19868
|
if (space !== void 0) {
|
|
19738
|
-
if (word !== void 0)
|
|
19869
|
+
if (word !== void 0) words3.push(word);
|
|
19739
19870
|
word = void 0;
|
|
19740
19871
|
continue;
|
|
19741
19872
|
}
|
|
@@ -19743,8 +19874,8 @@ function splitPatterns(text) {
|
|
|
19743
19874
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
19744
19875
|
else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
|
|
19745
19876
|
}
|
|
19746
|
-
if (word !== void 0)
|
|
19747
|
-
return
|
|
19877
|
+
if (word !== void 0) words3.push(word);
|
|
19878
|
+
return words3;
|
|
19748
19879
|
}
|
|
19749
19880
|
var literal = (pattern) => pattern.map((p) => p ?? "*").join("");
|
|
19750
19881
|
var isAny = (pattern) => pattern?.length === 1 && pattern[0] === null;
|
|
@@ -19817,14 +19948,14 @@ function whoError(who) {
|
|
|
19817
19948
|
return `expected a user name, %group or ALL first, not ${shown(who)}`;
|
|
19818
19949
|
}
|
|
19819
19950
|
function cwdOf(word) {
|
|
19820
|
-
const
|
|
19821
|
-
const path =
|
|
19951
|
+
const words3 = splitWords(word);
|
|
19952
|
+
const path = words3?.length === 1 ? words3[0] : "";
|
|
19822
19953
|
return path.startsWith("/") ? normalizePath(path) : "";
|
|
19823
19954
|
}
|
|
19824
19955
|
function commandOf2(text, after) {
|
|
19825
|
-
const
|
|
19826
|
-
if (
|
|
19827
|
-
const [first, ...args] =
|
|
19956
|
+
const words3 = splitPatterns(text);
|
|
19957
|
+
if (words3 === void 0) return "a quote in the command is not closed, or it ends in a \\";
|
|
19958
|
+
const [first, ...args] = words3;
|
|
19828
19959
|
const path = first ? literal(first) : "";
|
|
19829
19960
|
if (path === "") return `expected a command after ${after}`;
|
|
19830
19961
|
if (!path.startsWith("/")) return `expected an absolute command path, not ${shown(path)}`;
|
|
@@ -20137,6 +20268,9 @@ function recordable(cwd, argv) {
|
|
|
20137
20268
|
function changedText(file) {
|
|
20138
20269
|
return `sudo: ${file} changed since it was approved`;
|
|
20139
20270
|
}
|
|
20271
|
+
function requestedText(file) {
|
|
20272
|
+
return `sudo: ${file} changed since it was requested`;
|
|
20273
|
+
}
|
|
20140
20274
|
function changedFile(approved, now) {
|
|
20141
20275
|
const files = /* @__PURE__ */ new Set([...Object.keys(approved), ...Object.keys(now)]);
|
|
20142
20276
|
return [...files].find((file) => approved[file] !== now[file]);
|
|
@@ -20144,6 +20278,67 @@ function changedFile(approved, now) {
|
|
|
20144
20278
|
function pinned(rule, bound3) {
|
|
20145
20279
|
return rule.sha256 === void 0 || changedFile(rule.sha256, bound3) === void 0;
|
|
20146
20280
|
}
|
|
20281
|
+
var REPLAY_ID = /^[!-~]{1,256}$/;
|
|
20282
|
+
var REPLAY_TAGS_MAX = 64;
|
|
20283
|
+
var REPLAY_ARGV_MAX = 4096;
|
|
20284
|
+
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
20285
|
+
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
20286
|
+
function strings(value2, max) {
|
|
20287
|
+
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
20288
|
+
}
|
|
20289
|
+
function words(value2) {
|
|
20290
|
+
if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
20291
|
+
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
20292
|
+
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
20293
|
+
}
|
|
20294
|
+
function filesOf(value2) {
|
|
20295
|
+
if (value2 === void 0) return {};
|
|
20296
|
+
if (!Array.isArray(value2) || value2.length > OPERANDS_MAX) return void 0;
|
|
20297
|
+
const files = {};
|
|
20298
|
+
for (const file of value2) {
|
|
20299
|
+
const { path, sha256: hex4 } = file ?? {};
|
|
20300
|
+
if (typeof path !== "string" || typeof hex4 !== "string") return void 0;
|
|
20301
|
+
files[path] = hex4;
|
|
20302
|
+
}
|
|
20303
|
+
return files;
|
|
20304
|
+
}
|
|
20305
|
+
function flagsOf(value2) {
|
|
20306
|
+
if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) return void 0;
|
|
20307
|
+
const entries = Object.entries(value2);
|
|
20308
|
+
if (entries.length > ROOT_ONLY_MAX || entries.some(([, v]) => typeof v !== "boolean")) {
|
|
20309
|
+
return void 0;
|
|
20310
|
+
}
|
|
20311
|
+
return Object.fromEntries(entries);
|
|
20312
|
+
}
|
|
20313
|
+
function replayOf(value2) {
|
|
20314
|
+
if (typeof value2 !== "object" || value2 === null) return failed2("EINVAL", NOT_A_REQUEST);
|
|
20315
|
+
const r = value2;
|
|
20316
|
+
if (r.validate === true) return failed2("EINVAL", "sudo: a sudo -v request runs no command");
|
|
20317
|
+
if (r.stdin === "pipe" || r.stdin === "file") {
|
|
20318
|
+
return failed2("EINVAL", `sudo: the request read a ${r.stdin}, and that input is gone`);
|
|
20319
|
+
}
|
|
20320
|
+
const { id: id2, user, uid, target, argv, path, cwd, stdin, tag, tags } = r;
|
|
20321
|
+
const rootOnly2 = flagsOf(r.rootOnly);
|
|
20322
|
+
const bound3 = filesOf(r.userWritable);
|
|
20323
|
+
const named = (s) => typeof s === "string" && s !== "";
|
|
20324
|
+
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
20325
|
+
const valid = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 || strings(tags, REPLAY_TAGS_MAX)) && rootOnly2 !== void 0 && bound3 !== void 0;
|
|
20326
|
+
if (!valid) return failed2("EINVAL", NOT_A_REQUEST);
|
|
20327
|
+
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
20328
|
+
return {
|
|
20329
|
+
id: id2,
|
|
20330
|
+
user,
|
|
20331
|
+
uid,
|
|
20332
|
+
target,
|
|
20333
|
+
argv,
|
|
20334
|
+
path,
|
|
20335
|
+
cwd,
|
|
20336
|
+
stdin,
|
|
20337
|
+
rootOnly: rootOnly2,
|
|
20338
|
+
bound: bound3,
|
|
20339
|
+
tags: [...new Set(all)]
|
|
20340
|
+
};
|
|
20341
|
+
}
|
|
20147
20342
|
var VISUDO_FILES_MAX = 64;
|
|
20148
20343
|
var VISUDO_TEXT_MAX = 1024 * 1024;
|
|
20149
20344
|
function visudoCall(req) {
|
|
@@ -20545,6 +20740,61 @@ var Sudo = class {
|
|
|
20545
20740
|
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
20546
20741
|
return this.command(session, root, mine, call);
|
|
20547
20742
|
}
|
|
20743
|
+
async replay(value2) {
|
|
20744
|
+
const r = replayOf(value2);
|
|
20745
|
+
if ("error" in r) return r;
|
|
20746
|
+
if (this.o.mode !== "enforce") {
|
|
20747
|
+
return failed2("EINVAL", `sudo: users are '${this.o.mode}', so no request needed approval`);
|
|
20748
|
+
}
|
|
20749
|
+
const self = await this.o.users.lookup(r.user);
|
|
20750
|
+
if (!self) return failed2("ENOENT", `sudo: unknown user ${r.user}`);
|
|
20751
|
+
if (self.uid !== r.uid) return failed2("EPERM", `sudo: ${self.name} is no longer uid ${r.uid}`);
|
|
20752
|
+
const target = await this.lookup(r.target);
|
|
20753
|
+
if (!target) return failed2("ENOENT", `sudo: unknown user ${r.target}`);
|
|
20754
|
+
const cred = credOf(self);
|
|
20755
|
+
const [word = "", ...args] = r.argv;
|
|
20756
|
+
const argv = [r.path, ...args];
|
|
20757
|
+
if (!await this.mayAsk(self, target, r, argv)) {
|
|
20758
|
+
const quoted = argv.map(quoteWord).join(" ");
|
|
20759
|
+
return failed2(
|
|
20760
|
+
"EACCES",
|
|
20761
|
+
`sudo: ${self.name} may no longer ask to run ${quoted} as ${target.name}`
|
|
20762
|
+
);
|
|
20763
|
+
}
|
|
20764
|
+
if (await this.canonical(r.cwd) !== r.cwd) {
|
|
20765
|
+
return failed2("EACCES", `sudo: ${r.cwd} changed since it was requested`);
|
|
20766
|
+
}
|
|
20767
|
+
if (!await this.reachable(cred, r.cwd)) {
|
|
20768
|
+
return failed2(
|
|
20769
|
+
"EACCES",
|
|
20770
|
+
`sudo: cannot run in ${r.cwd}: it isn't a directory ${self.name} can enter`
|
|
20771
|
+
);
|
|
20772
|
+
}
|
|
20773
|
+
const path = await this.locate(word, r.cwd);
|
|
20774
|
+
if (!path) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20775
|
+
if (path !== r.path) return failed2("EPERM", `sudo: ${word} now runs ${path}, not ${r.path}`);
|
|
20776
|
+
const bound3 = await this.bound(path, r.cwd);
|
|
20777
|
+
if (!bound3) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20778
|
+
const changed = changedFile(r.bound, bound3);
|
|
20779
|
+
if (changed !== void 0) return failed2("EPERM", requestedText(changed));
|
|
20780
|
+
const now = await this.rootOnly(r.cwd, args);
|
|
20781
|
+
const flipped = Object.keys(r.rootOnly).find((at) => r.rootOnly[at] && now[at] !== true);
|
|
20782
|
+
if (flipped !== void 0) return failed2("EPERM", `sudo: ${flipped} is no longer root-only`);
|
|
20783
|
+
const { grant } = this.grant(self, target, cred, r.argv, path, r.cwd, void 0, bound3);
|
|
20784
|
+
(this.o.warn ?? console.warn)(
|
|
20785
|
+
`sudo: ${self.name} : TTY=unknown ; PWD=${quoteWord(r.cwd)} ; USER=${target.name} ; REPLAY=${r.id} ; COMMAND=${argv.map(quoteWord).join(" ")}`
|
|
20786
|
+
);
|
|
20787
|
+
return { grant, caller: self, target, tags: r.tags };
|
|
20788
|
+
}
|
|
20789
|
+
async mayAsk(self, target, r, argv) {
|
|
20790
|
+
if (self.uid === 0) return true;
|
|
20791
|
+
const cred = credOf(self);
|
|
20792
|
+
const { rules } = await this.policyRules({ cred, pid: 0, stdin: r.stdin });
|
|
20793
|
+
const applies = await this.applies(self, cred);
|
|
20794
|
+
return rules.some(
|
|
20795
|
+
(rule) => applies(rule.who) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === r.cwd) && stdinMatches(rule, r.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, r.bound)
|
|
20796
|
+
);
|
|
20797
|
+
}
|
|
20548
20798
|
async command(s, root, mine, call) {
|
|
20549
20799
|
const { caller, self, target, parsed: parsed2 } = s;
|
|
20550
20800
|
const { cred } = caller;
|
|
@@ -20724,8 +20974,8 @@ ${lines2}
|
|
|
20724
20974
|
const st = await fs.lstat(path).catch(() => void 0);
|
|
20725
20975
|
if (st && !await this.trusted(path)) return false;
|
|
20726
20976
|
const old = st ? await fs.readFile(path) : "";
|
|
20727
|
-
const
|
|
20728
|
-
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${
|
|
20977
|
+
const words3 = recordedCommand(argv);
|
|
20978
|
+
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words3}
|
|
20729
20979
|
`;
|
|
20730
20980
|
const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
|
|
20731
20981
|
${line}`;
|
|
@@ -22030,8 +22280,8 @@ function ipv6(host) {
|
|
|
22030
22280
|
const head = groups(halves[0]);
|
|
22031
22281
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
22032
22282
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
22033
|
-
const
|
|
22034
|
-
const missing6 = 8 -
|
|
22283
|
+
const words3 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
22284
|
+
const missing6 = 8 - words3.length - tail2.length;
|
|
22035
22285
|
if (halves.length === 2 ? missing6 < 1 : missing6 !== 0) return void 0;
|
|
22036
22286
|
const all = [
|
|
22037
22287
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
@@ -25672,7 +25922,7 @@ function selectFlag(req) {
|
|
|
25672
25922
|
throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
|
|
25673
25923
|
return TLS_SERVER_SELECT;
|
|
25674
25924
|
}
|
|
25675
|
-
function
|
|
25925
|
+
function flagsOf2(req) {
|
|
25676
25926
|
return certFlags(req) | selectFlag(req) | (req.keylog ? TLS_KEYLOG : 0);
|
|
25677
25927
|
}
|
|
25678
25928
|
async function engineOf(proc, { select, keylog }) {
|
|
@@ -25723,7 +25973,7 @@ async function serverOf(proc, req) {
|
|
|
25723
25973
|
let mine;
|
|
25724
25974
|
try {
|
|
25725
25975
|
mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
|
|
25726
|
-
const flags2 =
|
|
25976
|
+
const flags2 = flagsOf2(req);
|
|
25727
25977
|
const store = mine ?? (flags2 & CERT_FLAGS ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
|
|
25728
25978
|
return {
|
|
25729
25979
|
engine,
|
|
@@ -27612,7 +27862,7 @@ var WRITE_ACCESS = 3;
|
|
|
27612
27862
|
var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
|
|
27613
27863
|
var STUCK_ESCAPES = /* @__PURE__ */ new Set([SIG.INT, SIG.TERM, SIG.QUIT, SIG.HUP]);
|
|
27614
27864
|
var DEFAULT_UMASK2 = 18;
|
|
27615
|
-
function
|
|
27865
|
+
function words2(value2) {
|
|
27616
27866
|
if (!Array.isArray(value2) || !value2.every((w) => typeof w === "string")) {
|
|
27617
27867
|
throw new KernelError("EINVAL");
|
|
27618
27868
|
}
|
|
@@ -29065,7 +29315,7 @@ var WasmProcess = class {
|
|
|
29065
29315
|
if (!sudo) throw new KernelError("ENOSYS");
|
|
29066
29316
|
const call = {
|
|
29067
29317
|
op: "proc-sudo",
|
|
29068
|
-
args:
|
|
29318
|
+
args: words2(req.args),
|
|
29069
29319
|
cwd: typeof req.cwd === "string" && req.cwd.startsWith("/") ? req.cwd : "/",
|
|
29070
29320
|
...typeof req.term === "string" ? { term: req.term } : {}
|
|
29071
29321
|
};
|
|
@@ -30069,6 +30319,9 @@ var JobTable = class {
|
|
|
30069
30319
|
idOf(pid) {
|
|
30070
30320
|
return this.members.get(pid)?.shownAs ?? pid;
|
|
30071
30321
|
}
|
|
30322
|
+
execParentOf(pid) {
|
|
30323
|
+
return this.members.get(pid)?.execParent;
|
|
30324
|
+
}
|
|
30072
30325
|
byId(id2) {
|
|
30073
30326
|
for (const member of this.members.values()) if (member.shownAs === id2) return member;
|
|
30074
30327
|
return this.member(id2);
|
|
@@ -33672,7 +33925,40 @@ function describe(req, tty) {
|
|
|
33672
33925
|
}
|
|
33673
33926
|
function refuseChanged(approved, now) {
|
|
33674
33927
|
const changed = changedFile(approved, now);
|
|
33675
|
-
if (changed !== void 0) throw new ChangedError(changedText(changed));
|
|
33928
|
+
if (changed !== void 0) throw new ChangedError(changedText(changed), changed);
|
|
33929
|
+
}
|
|
33930
|
+
var O_WRONLY2 = 1;
|
|
33931
|
+
var OUTPUT_MODE = 384;
|
|
33932
|
+
var ERRNO_TEXT = {
|
|
33933
|
+
EACCES: "Permission denied",
|
|
33934
|
+
EISDIR: "Is a directory",
|
|
33935
|
+
ENOENT: "No such file or directory",
|
|
33936
|
+
ENOTDIR: "Not a directory",
|
|
33937
|
+
EROFS: "Read-only file system"
|
|
33938
|
+
};
|
|
33939
|
+
function sudoFailure(errno3, message3) {
|
|
33940
|
+
return Object.assign(new Error(message3), { code: errno3 });
|
|
33941
|
+
}
|
|
33942
|
+
function sudoCause(err) {
|
|
33943
|
+
const code = codeOf8(err);
|
|
33944
|
+
if (typeof code !== "string") throw err;
|
|
33945
|
+
return code;
|
|
33946
|
+
}
|
|
33947
|
+
function requested(err) {
|
|
33948
|
+
if (err instanceof ChangedError) throw sudoFailure(err.code, requestedText(err.file));
|
|
33949
|
+
throw err;
|
|
33950
|
+
}
|
|
33951
|
+
var MAX_SPAWN_TAGS = 8;
|
|
33952
|
+
function tagList(value2) {
|
|
33953
|
+
if (!Array.isArray(value2)) {
|
|
33954
|
+
tagOption(value2);
|
|
33955
|
+
return value2 === void 0 ? [] : [value2];
|
|
33956
|
+
}
|
|
33957
|
+
if (value2.length > MAX_SPAWN_TAGS) {
|
|
33958
|
+
throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
|
|
33959
|
+
}
|
|
33960
|
+
for (const each of value2) tagOption(each ?? 0);
|
|
33961
|
+
return value2;
|
|
33676
33962
|
}
|
|
33677
33963
|
function linksOf2(req) {
|
|
33678
33964
|
return req.links === void 0 ? {} : { links: req.links };
|
|
@@ -33798,27 +34084,27 @@ function appendedRoots(given) {
|
|
|
33798
34084
|
}
|
|
33799
34085
|
return [...roots];
|
|
33800
34086
|
}
|
|
33801
|
-
function plainEnv(
|
|
33802
|
-
const next3 =
|
|
33803
|
-
if (baseName2(
|
|
34087
|
+
function plainEnv(words3) {
|
|
34088
|
+
const next3 = words3[1];
|
|
34089
|
+
if (baseName2(words3[0] ?? "") !== "env" || next3 === void 0) return false;
|
|
33804
34090
|
return next3 === "-S" || !next3.startsWith("-") && !next3.includes("=");
|
|
33805
34091
|
}
|
|
33806
|
-
function shebangCommand(
|
|
33807
|
-
if (!plainEnv(
|
|
33808
|
-
const arg2 =
|
|
33809
|
-
return { interp:
|
|
34092
|
+
function shebangCommand(words3) {
|
|
34093
|
+
if (!plainEnv(words3)) {
|
|
34094
|
+
const arg2 = words3.slice(1).join(" ");
|
|
34095
|
+
return { interp: words3[0], args: arg2 ? [arg2] : [] };
|
|
33810
34096
|
}
|
|
33811
|
-
const split3 =
|
|
33812
|
-
const [interp, ...rest2] =
|
|
34097
|
+
const split3 = words3[1] === "-S";
|
|
34098
|
+
const [interp, ...rest2] = words3.slice(split3 ? 2 : 1);
|
|
33813
34099
|
const arg = rest2.join(" ");
|
|
33814
34100
|
return { interp, args: split3 ? rest2 : arg ? [arg] : [] };
|
|
33815
34101
|
}
|
|
33816
34102
|
function interpreter(head) {
|
|
33817
|
-
const
|
|
33818
|
-
if (!
|
|
33819
|
-
if (baseName2(
|
|
33820
|
-
if (
|
|
33821
|
-
return baseName2(
|
|
34103
|
+
const words3 = shebang(head);
|
|
34104
|
+
if (!words3) return "node";
|
|
34105
|
+
if (baseName2(words3[0]) === "env") words3.shift();
|
|
34106
|
+
if (words3[0] === "-S") words3.shift();
|
|
34107
|
+
return baseName2(words3[0] ?? "");
|
|
33822
34108
|
}
|
|
33823
34109
|
function expandDefaults(defaults2, env) {
|
|
33824
34110
|
const out = {};
|
|
@@ -34593,9 +34879,9 @@ var Launcher = class {
|
|
|
34593
34879
|
const head = await this.head(script);
|
|
34594
34880
|
if (!head) return void 0;
|
|
34595
34881
|
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
|
|
34596
|
-
const
|
|
34597
|
-
if (!
|
|
34598
|
-
const { interp, args } = shebangCommand(
|
|
34882
|
+
const words3 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
34883
|
+
if (!words3) return void 0;
|
|
34884
|
+
const { interp, args } = shebangCommand(words3);
|
|
34599
34885
|
const passed = [...args, command ? script : file, ...argv.slice(1)];
|
|
34600
34886
|
if (!interp) return void 0;
|
|
34601
34887
|
const found = await this.resolve(interp, interp, cwd, scope);
|
|
@@ -34826,7 +35112,7 @@ var Launcher = class {
|
|
|
34826
35112
|
const hex4 = pinned2[real];
|
|
34827
35113
|
if (hex4 !== void 0) return { [real]: hex4 };
|
|
34828
35114
|
if (await this.sudoer.trusted(real)) return {};
|
|
34829
|
-
throw new ChangedError(changedText(real));
|
|
35115
|
+
throw new ChangedError(changedText(real), real);
|
|
34830
35116
|
}
|
|
34831
35117
|
async pinnedBytes(pin, bytes) {
|
|
34832
35118
|
const [real = ""] = Object.keys(pin);
|
|
@@ -35105,6 +35391,10 @@ var Launcher = class {
|
|
|
35105
35391
|
}
|
|
35106
35392
|
async launch(asked, req) {
|
|
35107
35393
|
const cwd = await physicalPathAsync(this.base, req.cwd);
|
|
35394
|
+
if (req.exactCwd && cwd !== req.cwd) {
|
|
35395
|
+
await req.fds.closeAll();
|
|
35396
|
+
throw sudoFailure("EACCES", `sudo: ${req.cwd} changed since it was requested`);
|
|
35397
|
+
}
|
|
35108
35398
|
const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
|
|
35109
35399
|
await this.pnpmReady();
|
|
35110
35400
|
let loaded;
|
|
@@ -35283,10 +35573,12 @@ var Launcher = class {
|
|
|
35283
35573
|
});
|
|
35284
35574
|
const fit = await (handle.ready ?? true);
|
|
35285
35575
|
if (fit) {
|
|
35576
|
+
this.traceAdmitted(handle.pid, true);
|
|
35286
35577
|
await fds.closeAll();
|
|
35287
35578
|
return handle;
|
|
35288
35579
|
}
|
|
35289
35580
|
await handle.exited;
|
|
35581
|
+
this.traceAdmitted(handle.pid, false);
|
|
35290
35582
|
await this.memories.refused().catch(async (err) => {
|
|
35291
35583
|
await fds.closeAll();
|
|
35292
35584
|
throw err;
|
|
@@ -35417,10 +35709,12 @@ var Launcher = class {
|
|
|
35417
35709
|
if (req.group === "lead") this.jobs.lead(pid);
|
|
35418
35710
|
if (req.group === "session") this.jobs.setsid(pid);
|
|
35419
35711
|
this.described.set(pid, describe(req, terminal?.name ?? null));
|
|
35712
|
+
this.traceStarted(pid, req);
|
|
35420
35713
|
handle.onState(
|
|
35421
35714
|
(state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
|
|
35422
35715
|
);
|
|
35423
|
-
void handle.exited.then(() => {
|
|
35716
|
+
void handle.exited.then((status) => {
|
|
35717
|
+
this.traceEnded(pid, handle, status);
|
|
35424
35718
|
this.cwds.release(this.cwdSlots.get(pid));
|
|
35425
35719
|
this.cwdSlots.delete(pid);
|
|
35426
35720
|
this.cwdPaths.delete(pid);
|
|
@@ -35535,6 +35829,71 @@ var Launcher = class {
|
|
|
35535
35829
|
imageTimes = /* @__PURE__ */ new Map();
|
|
35536
35830
|
childTimes = /* @__PURE__ */ new Map();
|
|
35537
35831
|
stopped = /* @__PURE__ */ new Set();
|
|
35832
|
+
traces = new TraceHub();
|
|
35833
|
+
traceHeld = /* @__PURE__ */ new Map();
|
|
35834
|
+
replacedImages = /* @__PURE__ */ new Set();
|
|
35835
|
+
traceStarted(pid, req) {
|
|
35836
|
+
const replaces = req.exec ? req.ppid : void 0;
|
|
35837
|
+
if (req.quietOom) this.traceHeld.set(pid, { replaces, events: [] });
|
|
35838
|
+
else if (replaces !== void 0) this.replacedImages.add(replaces);
|
|
35839
|
+
this.traceEmit(pid, () => {
|
|
35840
|
+
const { pid: shown2, ppid } = this.identityOf(pid);
|
|
35841
|
+
const described2 = this.described.get(pid);
|
|
35842
|
+
const { argv, started, tty: _tty, exeId: _exeId, ...exe } = described2;
|
|
35843
|
+
return {
|
|
35844
|
+
type: req.fork ? "fork" : "exec",
|
|
35845
|
+
pid: shown2,
|
|
35846
|
+
ppid,
|
|
35847
|
+
pgid: this.jobs.pgidOf(pid),
|
|
35848
|
+
argv: [...argv],
|
|
35849
|
+
...exe,
|
|
35850
|
+
uid: this.credOf(pid).euid,
|
|
35851
|
+
at: started,
|
|
35852
|
+
...req.exec ? { replaced: true } : {}
|
|
35853
|
+
};
|
|
35854
|
+
});
|
|
35855
|
+
}
|
|
35856
|
+
traceEnded(pid, handle, status) {
|
|
35857
|
+
if (this.replacedImages.delete(pid)) return;
|
|
35858
|
+
this.traceEmit(pid, () => ({
|
|
35859
|
+
type: "exit",
|
|
35860
|
+
pid: this.jobs.idOf(pid),
|
|
35861
|
+
status,
|
|
35862
|
+
...signalField(handle.termsig()),
|
|
35863
|
+
...this.chainTimes(pid),
|
|
35864
|
+
at: Date.now()
|
|
35865
|
+
}));
|
|
35866
|
+
}
|
|
35867
|
+
traceEmit(pid, build) {
|
|
35868
|
+
if (this.traces.idle) return;
|
|
35869
|
+
const tags = this.tagsOf(pid);
|
|
35870
|
+
const cred = this.credOf(pid);
|
|
35871
|
+
if (!this.traces.wants(tags, cred)) return;
|
|
35872
|
+
const held = this.traceHeld.get(pid);
|
|
35873
|
+
if (held) held.events.push({ tags, cred: copyCred(cred), draft: build() });
|
|
35874
|
+
else this.traces.publish(tags, cred, build());
|
|
35875
|
+
}
|
|
35876
|
+
traceAdmitted(pid, fit) {
|
|
35877
|
+
const held = this.traceHeld.get(pid);
|
|
35878
|
+
if (!held) return;
|
|
35879
|
+
this.traceHeld.delete(pid);
|
|
35880
|
+
if (!fit) return;
|
|
35881
|
+
if (held.replaces !== void 0) this.replacedImages.add(held.replaces);
|
|
35882
|
+
for (const { tags, cred, draft } of held.events) this.traces.publish(tags, cred, draft);
|
|
35883
|
+
}
|
|
35884
|
+
chainTimes(pid) {
|
|
35885
|
+
let utime = 0;
|
|
35886
|
+
let stime = 0;
|
|
35887
|
+
let timed2 = false;
|
|
35888
|
+
for (let at = pid; at !== void 0; at = this.jobs.execParentOf(at)) {
|
|
35889
|
+
const t = this.timesAt(at);
|
|
35890
|
+
if (!t) continue;
|
|
35891
|
+
timed2 = true;
|
|
35892
|
+
utime += t.utime;
|
|
35893
|
+
stime += t.stime;
|
|
35894
|
+
}
|
|
35895
|
+
return timed2 ? { utime, stime } : {};
|
|
35896
|
+
}
|
|
35538
35897
|
stateOf(pid) {
|
|
35539
35898
|
if (!this.processes.has(pid)) return "Z";
|
|
35540
35899
|
return this.stopped.has(pid) ? "T" : "S";
|
|
@@ -36731,6 +37090,96 @@ var Launcher = class {
|
|
|
36731
37090
|
const signal = handle.termsig();
|
|
36732
37091
|
return { status, ...signal ? { signal } : {}, stdout: concat7(out), stderr: concat7(err) };
|
|
36733
37092
|
}
|
|
37093
|
+
async sudoRun(replay, options = {}) {
|
|
37094
|
+
const added = tagList(options.tag);
|
|
37095
|
+
const replayed = await this.sudoer.replay(replay);
|
|
37096
|
+
if ("error" in replayed) throw sudoFailure(replayed.errno, replayed.error);
|
|
37097
|
+
const { grant, caller, target } = replayed;
|
|
37098
|
+
let tags = withTag(void 0, options.base);
|
|
37099
|
+
for (const tag of [...replayed.tags, ...added]) tags = withTag(tags, tag);
|
|
37100
|
+
const tagged = { tags, tagBase: options.base === void 0 ? 0 : 1 };
|
|
37101
|
+
await this.swaps.settled();
|
|
37102
|
+
this.resetCatalogs();
|
|
37103
|
+
const req = { file: grant.file, argv: grant.argv, env: grant.env, cwd: grant.cwd };
|
|
37104
|
+
const ctx = { ppid: 0, cred: grant.cred, scope: "" };
|
|
37105
|
+
const planned = await this.planOne(req, ctx).catch((err2) => {
|
|
37106
|
+
const code = sudoCause(err2);
|
|
37107
|
+
const word = grant.argv[0] ?? grant.file;
|
|
37108
|
+
if (code === "ENOENT") throw sudoFailure(code, `sudo: ${word}: command not found`);
|
|
37109
|
+
throw sudoFailure(code, `sudo: ${grant.file}: ${ERRNO_TEXT[code] ?? code}`);
|
|
37110
|
+
});
|
|
37111
|
+
const pin = await this.unchanged(planned, grant.file, grant.cwd, grant).catch(requested);
|
|
37112
|
+
const fds = await this.sudoOutputs(options, caller, this.stackFor(tags, tagged.tagBase));
|
|
37113
|
+
const err = fds.get(2).file;
|
|
37114
|
+
const report = (message3) => void Promise.resolve(err.write?.(encoder10.encode(`${message3}
|
|
37115
|
+
`))).catch(() => void 0);
|
|
37116
|
+
const replan = () => this.planOne(req, ctx);
|
|
37117
|
+
if (!await this.grantedCwd(grant.caller, grant.cwd)) {
|
|
37118
|
+
await fds.closeAll();
|
|
37119
|
+
throw sudoFailure("EACCES", `sudo: ${grant.cwd} changed since it was requested`);
|
|
37120
|
+
}
|
|
37121
|
+
const handle = await this.launch(
|
|
37122
|
+
{ ...planned, replan },
|
|
37123
|
+
{
|
|
37124
|
+
...pin,
|
|
37125
|
+
env: grant.env,
|
|
37126
|
+
cwd: grant.cwd,
|
|
37127
|
+
fds,
|
|
37128
|
+
report,
|
|
37129
|
+
...this.identity(target, tagged, options.session),
|
|
37130
|
+
...caller.umask !== void 0 ? { umask: caller.umask } : {},
|
|
37131
|
+
links: grant.cred.euid,
|
|
37132
|
+
exactCwd: true
|
|
37133
|
+
}
|
|
37134
|
+
).catch(requested);
|
|
37135
|
+
const ended = handle.exited.then((status) => {
|
|
37136
|
+
const signal = handle.termsig();
|
|
37137
|
+
return { status, ...signal ? { signal } : {} };
|
|
37138
|
+
});
|
|
37139
|
+
return { pid: handle.pid, ended };
|
|
37140
|
+
}
|
|
37141
|
+
async sudoOutputs(options, caller, stack) {
|
|
37142
|
+
const fds = new FdTable();
|
|
37143
|
+
const opened2 = /* @__PURE__ */ new Map();
|
|
37144
|
+
fds.installAt(0, deviceFile("null"));
|
|
37145
|
+
try {
|
|
37146
|
+
fds.installAt(1, await this.sudoOutput(options.stdout, caller, stack, opened2));
|
|
37147
|
+
fds.installAt(2, await this.sudoOutput(options.stderr, caller, stack, opened2));
|
|
37148
|
+
} catch (err) {
|
|
37149
|
+
await fds.closeAll();
|
|
37150
|
+
throw err;
|
|
37151
|
+
}
|
|
37152
|
+
return fds;
|
|
37153
|
+
}
|
|
37154
|
+
async sudoOutput(path, caller, stack, opened2) {
|
|
37155
|
+
if (path === void 0) return deviceFile("null");
|
|
37156
|
+
if (typeof path !== "string" || !path.startsWith("/")) {
|
|
37157
|
+
throw sudoFailure("EINVAL", `sudo: ${String(path)} is not an absolute path`);
|
|
37158
|
+
}
|
|
37159
|
+
const cred = credOf(caller);
|
|
37160
|
+
const walked = await walk(this.fs, cred, normalizePath(path), true, caller.uid);
|
|
37161
|
+
const real = walked.path;
|
|
37162
|
+
const same3 = opened2.get(real);
|
|
37163
|
+
if (same3) return same3.retain();
|
|
37164
|
+
try {
|
|
37165
|
+
if (walked.denied !== void 0) throw fsError("EACCES", path);
|
|
37166
|
+
await writable({ ...this.execGuard(cred, 0), links: caller.uid }, "open", real);
|
|
37167
|
+
const owner = { uid: caller.uid, gid: caller.gid, mode: OUTPUT_MODE };
|
|
37168
|
+
await stack.noLinks.writeFile(real, new Uint8Array(0), owner);
|
|
37169
|
+
} catch (err) {
|
|
37170
|
+
const code = sudoCause(err);
|
|
37171
|
+
throw sudoFailure(code, `sudo: ${path}: ${ERRNO_TEXT[code] ?? code}`);
|
|
37172
|
+
}
|
|
37173
|
+
const file = vfsFile(stack.fs, { path: real, flags: O_WRONLY2, position: 0 }, stack.nodes);
|
|
37174
|
+
const again = await walk(this.fs, cred, real, true, caller.uid);
|
|
37175
|
+
const st = await stack.noLinks.lstat(real).catch(() => void 0);
|
|
37176
|
+
if (again.path !== real || again.denied !== void 0 || !st?.isFile) {
|
|
37177
|
+
await file.release();
|
|
37178
|
+
throw sudoFailure("EACCES", `sudo: ${path} changed while it was opened`);
|
|
37179
|
+
}
|
|
37180
|
+
opened2.set(real, file);
|
|
37181
|
+
return file;
|
|
37182
|
+
}
|
|
36734
37183
|
};
|
|
36735
37184
|
function cronLocks() {
|
|
36736
37185
|
return globalThis.navigator?.locks;
|
|
@@ -37061,7 +37510,7 @@ function sudoApprovals(port) {
|
|
|
37061
37510
|
}
|
|
37062
37511
|
};
|
|
37063
37512
|
}
|
|
37064
|
-
function
|
|
37513
|
+
function answered2(data, remote, uplink, approvals) {
|
|
37065
37514
|
if (data.sudoAnswer) approvals.answer(data.sudoAnswer);
|
|
37066
37515
|
else if (data.net !== void 0) remote?.receive(data);
|
|
37067
37516
|
else if (typeof data.uplink === "string") uplink?.receive(data);
|
|
@@ -37196,11 +37645,38 @@ function policyAsker(port) {
|
|
|
37196
37645
|
}
|
|
37197
37646
|
};
|
|
37198
37647
|
}
|
|
37648
|
+
function processOps(ready2, reply) {
|
|
37649
|
+
const terminals = /* @__PURE__ */ new Map();
|
|
37650
|
+
async function terminal(req) {
|
|
37651
|
+
const onData = (bytes) => reply(req.id, { fd: 1, bytes });
|
|
37652
|
+
const session = await (await ready2()).openTerminal(req.argv, { ...req.options, onData });
|
|
37653
|
+
terminals.set(req.id, session);
|
|
37654
|
+
reply(req.id, { started: session.pid });
|
|
37655
|
+
try {
|
|
37656
|
+
const status = await session.exited;
|
|
37657
|
+
const signal = session.termsig();
|
|
37658
|
+
return signal ? { status, signal } : status;
|
|
37659
|
+
} finally {
|
|
37660
|
+
terminals.delete(req.id);
|
|
37661
|
+
}
|
|
37662
|
+
}
|
|
37663
|
+
async function sudoRun(req) {
|
|
37664
|
+
const running = await (await ready2()).sudoRun(req.replay, req.options);
|
|
37665
|
+
reply(req.id, { started: running.pid });
|
|
37666
|
+
return running.ended;
|
|
37667
|
+
}
|
|
37668
|
+
function onTerminal(req) {
|
|
37669
|
+
const session = terminals.get(req.terminal);
|
|
37670
|
+
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
37671
|
+
act2(session, req);
|
|
37672
|
+
return true;
|
|
37673
|
+
}
|
|
37674
|
+
return { terminal, sudoRun, act: onTerminal };
|
|
37675
|
+
}
|
|
37199
37676
|
function serveKernel(port, deps) {
|
|
37200
37677
|
let launcher;
|
|
37201
37678
|
let remote;
|
|
37202
37679
|
let uplink;
|
|
37203
|
-
const terminals = /* @__PURE__ */ new Map();
|
|
37204
37680
|
const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
|
|
37205
37681
|
const clients = /* @__PURE__ */ new Set();
|
|
37206
37682
|
const sealed = sealedAsker(port);
|
|
@@ -37229,19 +37705,7 @@ function serveKernel(port, deps) {
|
|
|
37229
37705
|
if (!launcher) throw new Error("the kernel is not initialized");
|
|
37230
37706
|
return launcher;
|
|
37231
37707
|
}
|
|
37232
|
-
|
|
37233
|
-
const onData = (bytes) => reply(req.id, { fd: 1, bytes });
|
|
37234
|
-
const session = await (await ready2()).openTerminal(req.argv, { ...req.options, onData });
|
|
37235
|
-
terminals.set(req.id, session);
|
|
37236
|
-
reply(req.id, { started: session.pid });
|
|
37237
|
-
try {
|
|
37238
|
-
const status = await session.exited;
|
|
37239
|
-
const signal = session.termsig();
|
|
37240
|
-
return signal ? { status, signal } : status;
|
|
37241
|
-
} finally {
|
|
37242
|
-
terminals.delete(req.id);
|
|
37243
|
-
}
|
|
37244
|
-
}
|
|
37708
|
+
const processes2 = processOps(ready2, reply);
|
|
37245
37709
|
async function init(req) {
|
|
37246
37710
|
launcher = rootOf(deps, req).then(async (root) => {
|
|
37247
37711
|
req = await rootNamed(deps, req, root);
|
|
@@ -37296,7 +37760,8 @@ function serveKernel(port, deps) {
|
|
|
37296
37760
|
if (req.op === "init") return init(req);
|
|
37297
37761
|
if (isAttestRequest(req)) return attestOp(await ready2(), req);
|
|
37298
37762
|
if (req.op === "sealed-placeholders") return (await ready2()).setPlaceholders(req.list);
|
|
37299
|
-
if (req.op === "open-terminal") return terminal(req);
|
|
37763
|
+
if (req.op === "open-terminal") return processes2.terminal(req);
|
|
37764
|
+
if (req.op === "sudo-run") return processes2.sudoRun(req);
|
|
37300
37765
|
if (req.op === "connect") return connect2(req.user, req.tag, req.requests);
|
|
37301
37766
|
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
37302
37767
|
if (req.op === "routes" || req.op === "transport-traits") {
|
|
@@ -37305,12 +37770,7 @@ function serveKernel(port, deps) {
|
|
|
37305
37770
|
if (req.op === "dial") return dial(await ready2(), req);
|
|
37306
37771
|
if (req.op === "mount" || req.op === "umount" || req.op === "mounts" || req.op === "insert")
|
|
37307
37772
|
return mountOp(req);
|
|
37308
|
-
if (req.op === "terminal")
|
|
37309
|
-
const session = terminals.get(req.terminal);
|
|
37310
|
-
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
37311
|
-
act2(session, req);
|
|
37312
|
-
return true;
|
|
37313
|
-
}
|
|
37773
|
+
if (req.op === "terminal") return processes2.act(req);
|
|
37314
37774
|
return (await ready2()).run(req.argv, {
|
|
37315
37775
|
...req.options,
|
|
37316
37776
|
onStdout: (bytes) => reply(req.id, { fd: 1, bytes }),
|
|
@@ -37346,7 +37806,7 @@ function serveKernel(port, deps) {
|
|
|
37346
37806
|
const grant = event.data.hostfsGrant;
|
|
37347
37807
|
if (grant) return granted2(grant);
|
|
37348
37808
|
if (allowed(event.data)) return;
|
|
37349
|
-
if (
|
|
37809
|
+
if (answered2(event.data, remote, uplink, approvals)) return;
|
|
37350
37810
|
const req = event.data;
|
|
37351
37811
|
handle(req).then(
|
|
37352
37812
|
(result) => reply(req.id, { result }, transferOf2(req, result)),
|