@ai-ecoverse/slicc-kernel 1.43.1 → 1.44.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 -13
- package/dist/driver-worker.js +5 -1
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +18 -3
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +591 -198
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +5 -1
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.js +62 -5
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +594 -209
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +62 -5
- package/dist/process-worker.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -873,8 +873,8 @@ var MEDIA_STORE = "handles";
|
|
|
873
873
|
function memoryMedia() {
|
|
874
874
|
const handles = /* @__PURE__ */ new Map();
|
|
875
875
|
return {
|
|
876
|
-
get: async (
|
|
877
|
-
put: async (
|
|
876
|
+
get: async (id2) => handles.get(id2),
|
|
877
|
+
put: async (id2, handle) => void handles.set(id2, handle)
|
|
878
878
|
};
|
|
879
879
|
}
|
|
880
880
|
function settle2(request2, value) {
|
|
@@ -890,11 +890,11 @@ function openMedia(name2, factory) {
|
|
|
890
890
|
const db = request2.result;
|
|
891
891
|
const store = (mode) => db.transaction(MEDIA_STORE, mode).objectStore(MEDIA_STORE);
|
|
892
892
|
return {
|
|
893
|
-
get: (
|
|
894
|
-
const got = store("readonly").get(
|
|
893
|
+
get: (id2) => {
|
|
894
|
+
const got = store("readonly").get(id2);
|
|
895
895
|
return settle2(got, () => got.result);
|
|
896
896
|
},
|
|
897
|
-
put: (
|
|
897
|
+
put: (id2, handle) => settle2(store("readwrite").put(handle, id2), () => void 0)
|
|
898
898
|
};
|
|
899
899
|
});
|
|
900
900
|
}
|
|
@@ -959,7 +959,7 @@ function portCdpHook(ask, gone) {
|
|
|
959
959
|
}
|
|
960
960
|
|
|
961
961
|
// src/client/protocol.ts
|
|
962
|
-
var PROTOCOL = [1,
|
|
962
|
+
var PROTOCOL = [1, 8];
|
|
963
963
|
var FS_METHODS = [
|
|
964
964
|
"readFile",
|
|
965
965
|
"writeFile",
|
|
@@ -2278,6 +2278,287 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2278
2278
|
};
|
|
2279
2279
|
}
|
|
2280
2280
|
|
|
2281
|
+
// src/kernel/cred.ts
|
|
2282
|
+
var ROOT = Object.freeze({
|
|
2283
|
+
ruid: 0,
|
|
2284
|
+
euid: 0,
|
|
2285
|
+
suid: 0,
|
|
2286
|
+
rgid: 0,
|
|
2287
|
+
egid: 0,
|
|
2288
|
+
sgid: 0,
|
|
2289
|
+
groups: [0]
|
|
2290
|
+
});
|
|
2291
|
+
var MAX_ID = 4294967294;
|
|
2292
|
+
var MAX_GROUPS = 65536;
|
|
2293
|
+
var UIDS = ["ruid", "euid", "suid"];
|
|
2294
|
+
var GIDS = ["rgid", "egid", "sgid"];
|
|
2295
|
+
function userCred(uid, gid, groups = [gid]) {
|
|
2296
|
+
return { ruid: uid, euid: uid, suid: uid, rgid: gid, egid: gid, sgid: gid, groups: [...groups] };
|
|
2297
|
+
}
|
|
2298
|
+
function copyCred(cred) {
|
|
2299
|
+
return { ...cred, groups: [...cred.groups] };
|
|
2300
|
+
}
|
|
2301
|
+
function id(value) {
|
|
2302
|
+
if (value === void 0 || value === -1 || value === 4294967295) return void 0;
|
|
2303
|
+
if (!Number.isInteger(value) || value < 0 || value > MAX_ID) {
|
|
2304
|
+
throw fsError("EINVAL", `not a user or group id: ${String(value)}`);
|
|
2305
|
+
}
|
|
2306
|
+
return value;
|
|
2307
|
+
}
|
|
2308
|
+
function changeCred(cur, change) {
|
|
2309
|
+
const privileged = cur.euid === 0;
|
|
2310
|
+
const next2 = copyCred(cur);
|
|
2311
|
+
for (const keys of [UIDS, GIDS]) {
|
|
2312
|
+
const own = keys.map((key) => cur[key]);
|
|
2313
|
+
for (const key of keys) {
|
|
2314
|
+
const value = id(change[key]);
|
|
2315
|
+
if (value === void 0) continue;
|
|
2316
|
+
if (!privileged && !own.includes(value)) throw fsError("EPERM", `${key} ${value}`);
|
|
2317
|
+
next2[key] = value;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
if (change.groups !== void 0) {
|
|
2321
|
+
if (!Array.isArray(change.groups) || change.groups.length > MAX_GROUPS) {
|
|
2322
|
+
throw fsError("EINVAL", "not a group list");
|
|
2323
|
+
}
|
|
2324
|
+
const groups = change.groups.map((g) => id(g) ?? Number.NaN);
|
|
2325
|
+
if (groups.some(Number.isNaN)) throw fsError("EINVAL", "not a group list");
|
|
2326
|
+
if (!privileged) throw fsError("EPERM", "setgroups");
|
|
2327
|
+
next2.groups = groups;
|
|
2328
|
+
}
|
|
2329
|
+
return next2;
|
|
2330
|
+
}
|
|
2331
|
+
function maySignal(sender, target) {
|
|
2332
|
+
if (sender.euid === 0) return true;
|
|
2333
|
+
return [sender.ruid, sender.euid].some((uid) => uid === target.ruid || uid === target.suid);
|
|
2334
|
+
}
|
|
2335
|
+
function maySee(viewer, target) {
|
|
2336
|
+
return viewer.euid === 0 || target.ruid === viewer.euid || target.euid === viewer.euid;
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// src/users.ts
|
|
2340
|
+
var PASSWD = "/etc/passwd";
|
|
2341
|
+
var GROUP = "/etc/group";
|
|
2342
|
+
var DEFAULT_PASSWD = "root:x:0:0:root:/root:/bin/bash\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n";
|
|
2343
|
+
var DEFAULT_GROUP = "root:x:0:\nsudo:x:27:\nusers:x:100:\nnogroup:x:65534:\n";
|
|
2344
|
+
var NAME = /^[a-z_][a-z0-9_.-]*$/;
|
|
2345
|
+
var MAX_NAME = 255;
|
|
2346
|
+
var FIRST_UID = 1e3;
|
|
2347
|
+
var LAST_UID = 6e4;
|
|
2348
|
+
var USERS_GROUP = "users";
|
|
2349
|
+
var HOME_DIR = /^\/home\/[^/]+$/;
|
|
2350
|
+
var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
|
|
2351
|
+
var isId = (text) => text !== void 0 && /^\d+$/.test(text);
|
|
2352
|
+
function lines(text) {
|
|
2353
|
+
return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
|
|
2354
|
+
}
|
|
2355
|
+
function parsePasswd(text) {
|
|
2356
|
+
return lines(text).map((line) => {
|
|
2357
|
+
const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
|
|
2358
|
+
if (!name2 || !isId(uid) || !isId(gid)) return line;
|
|
2359
|
+
return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
function parseGroup(text) {
|
|
2363
|
+
return lines(text).map((line) => {
|
|
2364
|
+
const [name2, , gid, members = ""] = line.split(":");
|
|
2365
|
+
if (!name2 || !isId(gid)) return line;
|
|
2366
|
+
return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
function formatPasswd(rows) {
|
|
2370
|
+
const line = (r) => `${r.name}:x:${r.uid}:${r.gid}:${r.gecos}:${r.home}:${r.shell}`;
|
|
2371
|
+
return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
|
|
2372
|
+
`;
|
|
2373
|
+
}
|
|
2374
|
+
function formatGroup(rows) {
|
|
2375
|
+
const line = (r) => `${r.name}:x:${r.gid}:${r.members.join(",")}`;
|
|
2376
|
+
return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
|
|
2377
|
+
`;
|
|
2378
|
+
}
|
|
2379
|
+
var parsed = (rows) => rows.filter((r) => typeof r !== "string");
|
|
2380
|
+
function homeOf(home) {
|
|
2381
|
+
if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
|
|
2382
|
+
throw fsError("EINVAL", `not a home directory: ${String(home)}`);
|
|
2383
|
+
}
|
|
2384
|
+
return home;
|
|
2385
|
+
}
|
|
2386
|
+
function freeId(users, groups) {
|
|
2387
|
+
const taken = /* @__PURE__ */ new Set([...users.map((r) => r.uid), ...groups.map((r) => r.gid)]);
|
|
2388
|
+
let uid = FIRST_UID;
|
|
2389
|
+
while (taken.has(uid)) uid++;
|
|
2390
|
+
if (uid > LAST_UID) throw fsError("ENOSPC", "no free user id");
|
|
2391
|
+
return uid;
|
|
2392
|
+
}
|
|
2393
|
+
function credOf(account) {
|
|
2394
|
+
return userCred(account.uid, account.gid, account.groups);
|
|
2395
|
+
}
|
|
2396
|
+
function pnpmHomeOf(account, system = SYSTEM_PNPM_HOME) {
|
|
2397
|
+
return account.uid === 0 ? system : `${account.home}/.local/share/pnpm`;
|
|
2398
|
+
}
|
|
2399
|
+
function userEnv(account, system) {
|
|
2400
|
+
const pnpm = pnpmHomeOf(account, system);
|
|
2401
|
+
return {
|
|
2402
|
+
HOME: account.home,
|
|
2403
|
+
USER: account.name,
|
|
2404
|
+
LOGNAME: account.name,
|
|
2405
|
+
PNPM_HOME: pnpm,
|
|
2406
|
+
PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
var UserDb = class {
|
|
2410
|
+
fs;
|
|
2411
|
+
umasks = /* @__PURE__ */ new Map();
|
|
2412
|
+
writing = Promise.resolve();
|
|
2413
|
+
constructor(fs) {
|
|
2414
|
+
this.fs = fs;
|
|
2415
|
+
}
|
|
2416
|
+
async read(path, fallback) {
|
|
2417
|
+
try {
|
|
2418
|
+
return await this.fs.readFile(path);
|
|
2419
|
+
} catch (err) {
|
|
2420
|
+
if (err?.code === "ENOENT") return fallback;
|
|
2421
|
+
throw err;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
async tables() {
|
|
2425
|
+
return {
|
|
2426
|
+
passwd: parsePasswd(await this.read(PASSWD, DEFAULT_PASSWD)),
|
|
2427
|
+
group: parseGroup(await this.read(GROUP, DEFAULT_GROUP))
|
|
2428
|
+
};
|
|
2429
|
+
}
|
|
2430
|
+
async commit(passwd, group, then) {
|
|
2431
|
+
const before = [await this.read(PASSWD, DEFAULT_PASSWD), await this.read(GROUP, DEFAULT_GROUP)];
|
|
2432
|
+
try {
|
|
2433
|
+
await this.fs.writeFile(GROUP, group);
|
|
2434
|
+
await this.fs.writeFile(PASSWD, passwd);
|
|
2435
|
+
await then?.();
|
|
2436
|
+
} catch (err) {
|
|
2437
|
+
await this.fs.writeFile(GROUP, before[1]);
|
|
2438
|
+
await this.fs.writeFile(PASSWD, before[0]);
|
|
2439
|
+
throw err;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
serial(work) {
|
|
2443
|
+
const next2 = this.writing.then(work);
|
|
2444
|
+
this.writing = next2.catch(() => void 0);
|
|
2445
|
+
return next2;
|
|
2446
|
+
}
|
|
2447
|
+
async ensure() {
|
|
2448
|
+
await this.fs.mkdir("/etc", { recursive: true });
|
|
2449
|
+
for (const [path, text] of [
|
|
2450
|
+
[PASSWD, DEFAULT_PASSWD],
|
|
2451
|
+
[GROUP, DEFAULT_GROUP]
|
|
2452
|
+
]) {
|
|
2453
|
+
if (!await this.fs.exists(path)) await this.fs.writeFile(path, text);
|
|
2454
|
+
}
|
|
2455
|
+
if (!await this.fs.exists("/root")) {
|
|
2456
|
+
await this.fs.mkdir("/root", { recursive: true });
|
|
2457
|
+
try {
|
|
2458
|
+
await this.fs.chmod("/root", 448);
|
|
2459
|
+
} catch (err) {
|
|
2460
|
+
await this.fs.rm("/root", { recursive: true, force: true });
|
|
2461
|
+
throw err;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
account(row, groups) {
|
|
2466
|
+
const extra = groups.filter((g) => g.members.includes(row.name) && g.gid !== row.gid);
|
|
2467
|
+
const umask = this.umasks.get(row.name);
|
|
2468
|
+
return {
|
|
2469
|
+
name: row.name,
|
|
2470
|
+
uid: row.uid,
|
|
2471
|
+
gid: row.gid,
|
|
2472
|
+
home: row.home,
|
|
2473
|
+
shell: row.shell,
|
|
2474
|
+
groups: [row.gid, ...extra.map((g) => g.gid)],
|
|
2475
|
+
...umask !== void 0 ? { umask } : {}
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
async lookup(user) {
|
|
2479
|
+
const { passwd, group } = await this.tables();
|
|
2480
|
+
const key = typeof user === "number" ? "uid" : "name";
|
|
2481
|
+
const row = parsed(passwd).find((r) => r[key] === user);
|
|
2482
|
+
return row && this.account(row, parsed(group));
|
|
2483
|
+
}
|
|
2484
|
+
async list() {
|
|
2485
|
+
const { passwd, group } = await this.tables();
|
|
2486
|
+
const groups = parsed(group);
|
|
2487
|
+
return parsed(passwd).map((row) => this.account(row, groups));
|
|
2488
|
+
}
|
|
2489
|
+
add(options) {
|
|
2490
|
+
return this.serial(async () => {
|
|
2491
|
+
const { name: name2 } = options;
|
|
2492
|
+
if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME.test(name2)) {
|
|
2493
|
+
throw fsError("EINVAL", `not a user name: ${String(name2)}`);
|
|
2494
|
+
}
|
|
2495
|
+
const remember = () => {
|
|
2496
|
+
if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
|
|
2497
|
+
};
|
|
2498
|
+
const { passwd, group } = await this.tables();
|
|
2499
|
+
const users = parsed(passwd);
|
|
2500
|
+
const groups = parsed(group);
|
|
2501
|
+
const found = users.find((r) => r.name === name2);
|
|
2502
|
+
if (found) {
|
|
2503
|
+
remember();
|
|
2504
|
+
return this.account(found, groups);
|
|
2505
|
+
}
|
|
2506
|
+
const home = homeOf(options.home ?? `/home/${name2}`);
|
|
2507
|
+
const extra = [USERS_GROUP, ...options.groups ?? []].map((g) => {
|
|
2508
|
+
const row2 = groups.find((r) => r.name === g);
|
|
2509
|
+
if (!row2) throw fsError("ENOENT", `no group ${g}`);
|
|
2510
|
+
return row2;
|
|
2511
|
+
});
|
|
2512
|
+
if (groups.some((r) => r.name === name2)) throw fsError("EEXIST", `group ${name2} exists`);
|
|
2513
|
+
const uid = freeId(users, groups);
|
|
2514
|
+
const row = { name: name2, uid, gid: uid, gecos: name2, home, shell: "/bin/bash" };
|
|
2515
|
+
for (const g of extra) if (!g.members.includes(name2)) g.members.push(name2);
|
|
2516
|
+
group.push({ name: name2, gid: uid, members: [] });
|
|
2517
|
+
passwd.push(row);
|
|
2518
|
+
const fresh = !await this.fs.exists(home);
|
|
2519
|
+
await this.fs.mkdir(home, { recursive: true });
|
|
2520
|
+
try {
|
|
2521
|
+
await this.fs.mkdir("/etc", { recursive: true });
|
|
2522
|
+
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
2523
|
+
} catch (err) {
|
|
2524
|
+
if (fresh) await this.fs.rm(home, { recursive: true, force: true });
|
|
2525
|
+
throw err;
|
|
2526
|
+
}
|
|
2527
|
+
remember();
|
|
2528
|
+
return this.account(row, parsed(group));
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
remove(name2, options = {}) {
|
|
2532
|
+
return this.serial(async () => {
|
|
2533
|
+
const { passwd, group } = await this.tables();
|
|
2534
|
+
const row = parsed(passwd).find((r) => r.name === name2);
|
|
2535
|
+
if (!row) return false;
|
|
2536
|
+
if (row.uid < FIRST_UID || row.uid > LAST_UID) {
|
|
2537
|
+
throw fsError("EPERM", `${name2} is a system account`);
|
|
2538
|
+
}
|
|
2539
|
+
const keptGroups = group.filter(
|
|
2540
|
+
(r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
|
|
2541
|
+
);
|
|
2542
|
+
for (const r of parsed(keptGroups)) r.members = r.members.filter((m) => m !== name2);
|
|
2543
|
+
const home = this.fs.resolvePath("/", row.home);
|
|
2544
|
+
const shared = parsed(passwd).some(
|
|
2545
|
+
(r) => r !== row && this.fs.resolvePath("/", r.home) === home
|
|
2546
|
+
);
|
|
2547
|
+
const removeHome = !options.keepHome && !shared && HOME_DIR.test(home) && await this.fs.exists(home);
|
|
2548
|
+
const staged = `/home/.${name2}.removed-${Date.now()}`;
|
|
2549
|
+
await this.commit(
|
|
2550
|
+
formatPasswd(passwd.filter((r) => r !== row)),
|
|
2551
|
+
formatGroup(keptGroups),
|
|
2552
|
+
removeHome ? () => this.fs.rename(home, staged) : void 0
|
|
2553
|
+
);
|
|
2554
|
+
this.umasks.delete(name2);
|
|
2555
|
+
if (removeHome)
|
|
2556
|
+
await this.fs.rm(staged, { recursive: true, force: true }).catch(() => void 0);
|
|
2557
|
+
return true;
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
};
|
|
2561
|
+
|
|
2281
2562
|
// src/client/serve-client.ts
|
|
2282
2563
|
var MAX_LINKS2 = 40;
|
|
2283
2564
|
function act(session, req, signal) {
|
|
@@ -2355,6 +2636,39 @@ function dialFor(l, req, open) {
|
|
|
2355
2636
|
);
|
|
2356
2637
|
return { result: port2, transfer: [port2] };
|
|
2357
2638
|
}
|
|
2639
|
+
async function accountOf(l, user) {
|
|
2640
|
+
const found = await l.users.lookup(user);
|
|
2641
|
+
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
2642
|
+
return found;
|
|
2643
|
+
}
|
|
2644
|
+
async function runAs(l, self, requested) {
|
|
2645
|
+
if (requested === void 0) return self;
|
|
2646
|
+
const target = await accountOf(l, requested);
|
|
2647
|
+
if (self.uid !== 0 && target.uid !== self.uid) {
|
|
2648
|
+
throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
|
|
2649
|
+
}
|
|
2650
|
+
return target;
|
|
2651
|
+
}
|
|
2652
|
+
function processes(l, self) {
|
|
2653
|
+
const viewer = credOf(self);
|
|
2654
|
+
return l.list(viewer).map(({ tid: _, cred = viewer, umask: _umask, ...entry }) => ({
|
|
2655
|
+
...entry,
|
|
2656
|
+
uid: cred.euid,
|
|
2657
|
+
gid: cred.egid
|
|
2658
|
+
}));
|
|
2659
|
+
}
|
|
2660
|
+
function killAs(l, host, self, req) {
|
|
2661
|
+
if (!l.kill(req.pid, signalOf(host)(req.signal), credOf(self))) {
|
|
2662
|
+
throw fsError("ESRCH", `pid ${req.pid}`);
|
|
2663
|
+
}
|
|
2664
|
+
return { result: true };
|
|
2665
|
+
}
|
|
2666
|
+
function terminalAction(terminals, req, host) {
|
|
2667
|
+
const session = terminals.get(req.terminal);
|
|
2668
|
+
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
2669
|
+
act(session, req, signalOf(host));
|
|
2670
|
+
return { result: true };
|
|
2671
|
+
}
|
|
2358
2672
|
function killGroups(host, pgids) {
|
|
2359
2673
|
if (pgids.length === 0) return;
|
|
2360
2674
|
void host.launcher().then((launcher) => {
|
|
@@ -2368,9 +2682,9 @@ function joinable(l, pgid, ours) {
|
|
|
2368
2682
|
}
|
|
2369
2683
|
function cdpRegistry(send, gone) {
|
|
2370
2684
|
const registered = /* @__PURE__ */ new Map();
|
|
2371
|
-
const drop = (
|
|
2372
|
-
registered.get(
|
|
2373
|
-
return registered.delete(
|
|
2685
|
+
const drop = (id2) => {
|
|
2686
|
+
registered.get(id2)?.();
|
|
2687
|
+
return registered.delete(id2);
|
|
2374
2688
|
};
|
|
2375
2689
|
return {
|
|
2376
2690
|
serve(l, req) {
|
|
@@ -2385,7 +2699,7 @@ function cdpRegistry(send, gone) {
|
|
|
2385
2699
|
},
|
|
2386
2700
|
drop,
|
|
2387
2701
|
close: () => {
|
|
2388
|
-
for (const
|
|
2702
|
+
for (const id2 of [...registered.keys()]) drop(id2);
|
|
2389
2703
|
}
|
|
2390
2704
|
};
|
|
2391
2705
|
}
|
|
@@ -2405,6 +2719,7 @@ function watchOp(l, req, watches, send) {
|
|
|
2405
2719
|
}
|
|
2406
2720
|
function serveClient(port, host) {
|
|
2407
2721
|
let state = "new";
|
|
2722
|
+
let self;
|
|
2408
2723
|
const terminals = /* @__PURE__ */ new Map();
|
|
2409
2724
|
const groups = /* @__PURE__ */ new Set();
|
|
2410
2725
|
const sessions = /* @__PURE__ */ new Set();
|
|
@@ -2414,7 +2729,7 @@ function serveClient(port, host) {
|
|
|
2414
2729
|
const send = (message2, transfer = []) => {
|
|
2415
2730
|
if (state !== "closed") port.postMessage(message2, transfer);
|
|
2416
2731
|
};
|
|
2417
|
-
const reply = (
|
|
2732
|
+
const reply = (id2, body, transfer) => send({ id: id2, ...body }, transfer);
|
|
2418
2733
|
const cdps = cdpRegistry(send, ended.promise);
|
|
2419
2734
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
2420
2735
|
if (state === "closed") return;
|
|
@@ -2439,6 +2754,7 @@ function serveClient(port, host) {
|
|
|
2439
2754
|
return;
|
|
2440
2755
|
}
|
|
2441
2756
|
const launcher = await host.launcher();
|
|
2757
|
+
if (host.scope !== "transport") self = await accountOf(launcher, host.user ?? 0);
|
|
2442
2758
|
const transport = host.transport?.() ?? launcher.transport;
|
|
2443
2759
|
net = serveTransport({ postMessage: (m, t) => send(m, t) }, transport);
|
|
2444
2760
|
state = "open";
|
|
@@ -2454,6 +2770,7 @@ function serveClient(port, host) {
|
|
|
2454
2770
|
async function terminal(req, l) {
|
|
2455
2771
|
const session = await l.openTerminal(req.argv, {
|
|
2456
2772
|
...req.options,
|
|
2773
|
+
user: await runAs(l, self, req.options.user),
|
|
2457
2774
|
onData: (bytes) => reply(req.id, { fd: 1, bytes: bytes.slice() })
|
|
2458
2775
|
});
|
|
2459
2776
|
if (state === "closed") {
|
|
@@ -2475,6 +2792,7 @@ function serveClient(port, host) {
|
|
|
2475
2792
|
let leader;
|
|
2476
2793
|
const result = await l.run(req.argv, {
|
|
2477
2794
|
...req.options,
|
|
2795
|
+
user: await runAs(l, self, req.options.user),
|
|
2478
2796
|
collect: false,
|
|
2479
2797
|
onStdout: out(1),
|
|
2480
2798
|
onStderr: out(2),
|
|
@@ -2500,19 +2818,12 @@ function serveClient(port, host) {
|
|
|
2500
2818
|
return spawn(req, l);
|
|
2501
2819
|
case "open-terminal":
|
|
2502
2820
|
return terminal(req, l);
|
|
2503
|
-
case "terminal":
|
|
2504
|
-
|
|
2505
|
-
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
2506
|
-
act(session, req, signalOf(host));
|
|
2507
|
-
return { result: true };
|
|
2508
|
-
}
|
|
2821
|
+
case "terminal":
|
|
2822
|
+
return terminalAction(terminals, req, host);
|
|
2509
2823
|
case "kill":
|
|
2510
|
-
|
|
2511
|
-
return { result: true };
|
|
2824
|
+
return killAs(l, host, self, req);
|
|
2512
2825
|
case "ps":
|
|
2513
|
-
return {
|
|
2514
|
-
result: l.list().map(({ tid: _, ...entry }) => entry)
|
|
2515
|
-
};
|
|
2826
|
+
return { result: processes(l, self) };
|
|
2516
2827
|
case "fs":
|
|
2517
2828
|
if (!FS_METHODS.includes(req.method)) {
|
|
2518
2829
|
throw new Error(`unknown file system call ${req.method}`);
|
|
@@ -2732,21 +3043,21 @@ function latin1Bytes(text) {
|
|
|
2732
3043
|
return out;
|
|
2733
3044
|
}
|
|
2734
3045
|
function parseRequestHead(bytes) {
|
|
2735
|
-
const
|
|
2736
|
-
while (
|
|
2737
|
-
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(
|
|
3046
|
+
const lines2 = latin1(bytes).split(/\r?\n/);
|
|
3047
|
+
while (lines2.length > 0 && lines2[0] === "") lines2.shift();
|
|
3048
|
+
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines2.shift() ?? "");
|
|
2738
3049
|
if (!match) throw new HttpError(400, "malformed request line");
|
|
2739
3050
|
if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
|
|
2740
3051
|
return {
|
|
2741
3052
|
method: match[1],
|
|
2742
3053
|
target: match[2],
|
|
2743
3054
|
minor: Number(match[4]),
|
|
2744
|
-
headers: parseFields(
|
|
3055
|
+
headers: parseFields(lines2)
|
|
2745
3056
|
};
|
|
2746
3057
|
}
|
|
2747
|
-
function parseFields(
|
|
3058
|
+
function parseFields(lines2) {
|
|
2748
3059
|
const headers = [];
|
|
2749
|
-
for (const line of
|
|
3060
|
+
for (const line of lines2) {
|
|
2750
3061
|
if (line === "") continue;
|
|
2751
3062
|
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
2752
3063
|
throw new HttpError(400, "obsolete line folding");
|
|
@@ -3661,8 +3972,8 @@ function name(dn) {
|
|
|
3661
3972
|
rdns.push(set(seq(oid("2.5.4.3"), utf8(dn.commonName))));
|
|
3662
3973
|
return seq(...rdns);
|
|
3663
3974
|
}
|
|
3664
|
-
function extension(
|
|
3665
|
-
return critical ? seq(oid(
|
|
3975
|
+
function extension(id2, critical, value) {
|
|
3976
|
+
return critical ? seq(oid(id2), TRUE, octets(value)) : seq(oid(id2), octets(value));
|
|
3666
3977
|
}
|
|
3667
3978
|
function children(der) {
|
|
3668
3979
|
const out = [];
|
|
@@ -3778,9 +4089,9 @@ function pem(der) {
|
|
|
3778
4089
|
for (let i = 0; i < der.length; i += 32768) {
|
|
3779
4090
|
b64 += String.fromCharCode(...der.subarray(i, i + 32768));
|
|
3780
4091
|
}
|
|
3781
|
-
const
|
|
4092
|
+
const lines2 = btoa(b64).match(/.{1,64}/g) ?? [];
|
|
3782
4093
|
return `-----BEGIN CERTIFICATE-----
|
|
3783
|
-
${
|
|
4094
|
+
${lines2.join("\n")}
|
|
3784
4095
|
-----END CERTIFICATE-----
|
|
3785
4096
|
`;
|
|
3786
4097
|
}
|
|
@@ -3864,11 +4175,11 @@ var RealmCa = class _RealmCa {
|
|
|
3864
4175
|
static async create(owner, now) {
|
|
3865
4176
|
const pair = await crypto.subtle.generateKey(ECDSA, false, ["sign", "verify"]);
|
|
3866
4177
|
const spki = new Uint8Array(await crypto.subtle.exportKey("spki", pair.publicKey));
|
|
3867
|
-
const
|
|
4178
|
+
const id2 = Array.from(
|
|
3868
4179
|
crypto.getRandomValues(new Uint8Array(4)),
|
|
3869
4180
|
(b) => b.toString(16).padStart(2, "0")
|
|
3870
4181
|
).join("");
|
|
3871
|
-
const name2 = { organization: "SLICC", commonName: `SLICC realm CA ${owner} ${
|
|
4182
|
+
const name2 = { organization: "SLICC", commonName: `SLICC realm CA ${owner} ${id2}` };
|
|
3872
4183
|
const notAfter = now + CA_LIFETIME;
|
|
3873
4184
|
const cert = await certificate({
|
|
3874
4185
|
serial: randomSerial(),
|
|
@@ -4517,12 +4828,12 @@ function parsePrefix(text) {
|
|
|
4517
4828
|
const parts = text.trim().split("/");
|
|
4518
4829
|
if (parts.length > 2) return void 0;
|
|
4519
4830
|
const [address = "", size] = parts;
|
|
4520
|
-
const
|
|
4521
|
-
if (!
|
|
4522
|
-
const max =
|
|
4831
|
+
const parsed2 = addressBits(address);
|
|
4832
|
+
if (!parsed2) return void 0;
|
|
4833
|
+
const max = parsed2.v6 ? 128 : 32;
|
|
4523
4834
|
const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
4524
4835
|
if (length2 < 0 || length2 > max) return void 0;
|
|
4525
|
-
return { ...
|
|
4836
|
+
return { ...parsed2, length: length2 };
|
|
4526
4837
|
}
|
|
4527
4838
|
function within2(prefix, addr) {
|
|
4528
4839
|
if (prefix.v6 !== addr.v6) return false;
|
|
@@ -4548,10 +4859,10 @@ var Routes = class {
|
|
|
4548
4859
|
this.ipv6 = ipv62;
|
|
4549
4860
|
}
|
|
4550
4861
|
set(table) {
|
|
4551
|
-
const
|
|
4552
|
-
const bad = (table.prefixes ?? []).find((_, i) => !
|
|
4862
|
+
const parsed2 = (table.prefixes ?? []).map(parsePrefix);
|
|
4863
|
+
const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
|
|
4553
4864
|
if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
|
|
4554
|
-
this.table =
|
|
4865
|
+
this.table = parsed2;
|
|
4555
4866
|
this.exit = table.exit === true;
|
|
4556
4867
|
}
|
|
4557
4868
|
classify(host) {
|
|
@@ -4779,10 +5090,10 @@ var CdpFacade = class {
|
|
|
4779
5090
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
4780
5091
|
};
|
|
4781
5092
|
conn.onmessage = (message2) => {
|
|
4782
|
-
const
|
|
4783
|
-
if (
|
|
5093
|
+
const parsed2 = JSON.parse(message2);
|
|
5094
|
+
if (parsed2.id !== 1) return;
|
|
4784
5095
|
clearTimeout(timer);
|
|
4785
|
-
resolve(
|
|
5096
|
+
resolve(parsed2);
|
|
4786
5097
|
};
|
|
4787
5098
|
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
4788
5099
|
}
|
|
@@ -4955,7 +5266,7 @@ function closeAll(entry) {
|
|
|
4955
5266
|
}
|
|
4956
5267
|
|
|
4957
5268
|
// src/commands.ts
|
|
4958
|
-
var
|
|
5269
|
+
var NAME2 = /^[A-Za-z0-9._+-]+$/;
|
|
4959
5270
|
var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4960
5271
|
var PACKAGE = /\$\{package\}/g;
|
|
4961
5272
|
function inside(pkg, rel2) {
|
|
@@ -5044,7 +5355,7 @@ function commandsOf(pkg, manifest) {
|
|
|
5044
5355
|
const shared = envOf(pkg, slicc.env);
|
|
5045
5356
|
const out = [];
|
|
5046
5357
|
for (const [name2, raw] of Object.entries(entries)) {
|
|
5047
|
-
if (!
|
|
5358
|
+
if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
|
|
5048
5359
|
continue;
|
|
5049
5360
|
}
|
|
5050
5361
|
const command = commandOf(pkg, name2, raw, packageAbi, shared);
|
|
@@ -5128,7 +5439,7 @@ async function scanFilesystems(fs, modules) {
|
|
|
5128
5439
|
if (!declared || typeof declared !== "object") continue;
|
|
5129
5440
|
for (const [type, entry] of Object.entries(declared)) {
|
|
5130
5441
|
const module = inside(pkg, entry?.module);
|
|
5131
|
-
if (
|
|
5442
|
+
if (NAME2.test(type) && module && !found.has(type)) found.set(type, module);
|
|
5132
5443
|
}
|
|
5133
5444
|
}
|
|
5134
5445
|
return found;
|
|
@@ -5148,7 +5459,7 @@ async function scanBinfmts(fs, modules, warn = () => {
|
|
|
5148
5459
|
const declared = manifest.slicc?.binfmt;
|
|
5149
5460
|
if (!declared || typeof declared !== "object") continue;
|
|
5150
5461
|
for (const [suffix, command] of Object.entries(declared)) {
|
|
5151
|
-
if (!SUFFIX.test(suffix) || typeof command !== "string" || !
|
|
5462
|
+
if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME2.test(command)) continue;
|
|
5152
5463
|
const owner = owners.get(suffix);
|
|
5153
5464
|
if (owner === void 0) {
|
|
5154
5465
|
found.set(suffix, command);
|
|
@@ -5516,9 +5827,9 @@ var UnlinkedKeeper = class {
|
|
|
5516
5827
|
if (!names) return;
|
|
5517
5828
|
const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
5518
5829
|
for (const name2 of names) {
|
|
5519
|
-
const
|
|
5520
|
-
const at =
|
|
5521
|
-
const dead =
|
|
5830
|
+
const id2 = name2.slice(0, name2.lastIndexOf("."));
|
|
5831
|
+
const at = id2.indexOf("~");
|
|
5832
|
+
const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
|
|
5522
5833
|
if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
5523
5834
|
}
|
|
5524
5835
|
await this.tidy(dir);
|
|
@@ -5876,11 +6187,11 @@ var DriverConnection = class _DriverConnection {
|
|
|
5876
6187
|
}
|
|
5877
6188
|
call(call, transfer = []) {
|
|
5878
6189
|
if (this.failure) return Promise.reject(this.failure);
|
|
5879
|
-
const
|
|
6190
|
+
const id2 = ++this.nextId;
|
|
5880
6191
|
return new Promise((resolve, reject) => {
|
|
5881
6192
|
const timer = unref(
|
|
5882
6193
|
setTimeout(() => {
|
|
5883
|
-
this.waiting.delete(
|
|
6194
|
+
this.waiting.delete(id2);
|
|
5884
6195
|
const error = errnoError(
|
|
5885
6196
|
"EIO",
|
|
5886
6197
|
`the driver did not answer ${call.op} within ${this.timeoutMs} ms`
|
|
@@ -5890,8 +6201,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
5890
6201
|
this.onFail?.(error);
|
|
5891
6202
|
}, this.timeoutMs)
|
|
5892
6203
|
);
|
|
5893
|
-
this.waiting.set(
|
|
5894
|
-
this.port.postMessage({ ...call, id }, transfer);
|
|
6204
|
+
this.waiting.set(id2, { resolve, reject, timer });
|
|
6205
|
+
this.port.postMessage({ ...call, id: id2 }, transfer);
|
|
5895
6206
|
});
|
|
5896
6207
|
}
|
|
5897
6208
|
fail(error) {
|
|
@@ -5966,22 +6277,22 @@ var HeldPaths = class extends Map {
|
|
|
5966
6277
|
}
|
|
5967
6278
|
isKept(path) {
|
|
5968
6279
|
const ids = this.handlesAt(path);
|
|
5969
|
-
return ids.length === 0 ? this.unlinked.has(path) : ids.every((
|
|
6280
|
+
return ids.length === 0 ? this.unlinked.has(path) : ids.every((id2) => this.keptHandles.has(id2));
|
|
5970
6281
|
}
|
|
5971
6282
|
handlesAt(path) {
|
|
5972
|
-
return [...this.handles].filter(([, key]) => key === path).map(([
|
|
6283
|
+
return [...this.handles].filter(([, key]) => key === path).map(([id2]) => id2);
|
|
5973
6284
|
}
|
|
5974
6285
|
keptFor(handle) {
|
|
5975
6286
|
return this.keptHandles.get(handle);
|
|
5976
6287
|
}
|
|
5977
6288
|
keep(path, file) {
|
|
5978
|
-
const fresh = this.handlesAt(path).filter((
|
|
5979
|
-
for (const
|
|
6289
|
+
const fresh = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
6290
|
+
for (const id2 of fresh) this.keptHandles.set(id2, file);
|
|
5980
6291
|
this.uses.set(file, Math.max(fresh.length, 1));
|
|
5981
6292
|
this.unlinked.set(path, file);
|
|
5982
6293
|
}
|
|
5983
6294
|
unkeep(path, file) {
|
|
5984
|
-
for (const [
|
|
6295
|
+
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
5985
6296
|
this.uses.delete(file);
|
|
5986
6297
|
if (this.unlinked.get(path) === file) this.unlinked.delete(path);
|
|
5987
6298
|
}
|
|
@@ -6018,7 +6329,7 @@ var HeldPaths = class extends Map {
|
|
|
6018
6329
|
if (count > 0) this.opens.set(key, count);
|
|
6019
6330
|
else {
|
|
6020
6331
|
this.opens.delete(key);
|
|
6021
|
-
for (const
|
|
6332
|
+
for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
|
|
6022
6333
|
const kept = this.unlinked.get(key);
|
|
6023
6334
|
if (kept) this.letGo(kept, true);
|
|
6024
6335
|
for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
|
|
@@ -6065,8 +6376,8 @@ var HeldPaths = class extends Map {
|
|
|
6065
6376
|
this.opens.delete(path);
|
|
6066
6377
|
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
6067
6378
|
}
|
|
6068
|
-
for (const [
|
|
6069
|
-
if (within4(key, from)) this.handles.set(
|
|
6379
|
+
for (const [id2, key] of this.handles) {
|
|
6380
|
+
if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
6070
6381
|
}
|
|
6071
6382
|
}
|
|
6072
6383
|
isRevoked(path) {
|
|
@@ -7035,15 +7346,15 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7035
7346
|
const pending = /* @__PURE__ */ new Map();
|
|
7036
7347
|
let disposed = false;
|
|
7037
7348
|
const { dispatch: dispatch2 } = opts;
|
|
7038
|
-
function drop(
|
|
7039
|
-
const entry = pending.get(
|
|
7349
|
+
function drop(id2) {
|
|
7350
|
+
const entry = pending.get(id2);
|
|
7040
7351
|
if (!entry) return;
|
|
7041
7352
|
clearTimeout(entry.timer);
|
|
7042
|
-
pending.delete(
|
|
7353
|
+
pending.delete(id2);
|
|
7043
7354
|
}
|
|
7044
|
-
function publish(
|
|
7045
|
-
if (Atomics.load(header2, SAB_I_REQ) !==
|
|
7046
|
-
drop(
|
|
7355
|
+
function publish(id2, entry, offset) {
|
|
7356
|
+
if (Atomics.load(header2, SAB_I_REQ) !== id2) {
|
|
7357
|
+
drop(id2);
|
|
7047
7358
|
return;
|
|
7048
7359
|
}
|
|
7049
7360
|
const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
|
|
@@ -7052,23 +7363,23 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7052
7363
|
storeU53(header2, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
|
|
7053
7364
|
Atomics.store(header2, SAB_I_CHUNK, chunk2);
|
|
7054
7365
|
storeU53(header2, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
|
|
7055
|
-
Atomics.store(header2, SAB_I_SEQ,
|
|
7366
|
+
Atomics.store(header2, SAB_I_SEQ, id2);
|
|
7056
7367
|
Atomics.store(header2, SAB_I_STATE, SAB_STATE_READY);
|
|
7057
7368
|
Atomics.add(header2, SAB_I_PUB, 1);
|
|
7058
7369
|
Atomics.notify(header2, SAB_I_PUB);
|
|
7059
|
-
if (offset + chunk2 >= entry.payload.byteLength) drop(
|
|
7370
|
+
if (offset + chunk2 >= entry.payload.byteLength) drop(id2);
|
|
7060
7371
|
}
|
|
7061
|
-
function settle3(
|
|
7372
|
+
function settle3(id2, result) {
|
|
7062
7373
|
if (disposed) return;
|
|
7063
7374
|
const { status, payload } = encodeSabResult(result);
|
|
7064
|
-
drop(
|
|
7375
|
+
drop(id2);
|
|
7065
7376
|
const entry = {
|
|
7066
7377
|
status,
|
|
7067
7378
|
payload,
|
|
7068
|
-
timer: setTimeout(() => drop(
|
|
7379
|
+
timer: setTimeout(() => drop(id2), PENDING_TTL_MS)
|
|
7069
7380
|
};
|
|
7070
|
-
pending.set(
|
|
7071
|
-
publish(
|
|
7381
|
+
pending.set(id2, entry);
|
|
7382
|
+
publish(id2, entry, 0);
|
|
7072
7383
|
}
|
|
7073
7384
|
const handler = (event) => {
|
|
7074
7385
|
const data = event.data;
|
|
@@ -7084,14 +7395,14 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7084
7395
|
return;
|
|
7085
7396
|
}
|
|
7086
7397
|
if (data.type !== SYNC_SAB_REQ_MSG || !data.req || typeof data.req !== "object") return;
|
|
7087
|
-
const
|
|
7398
|
+
const id2 = data.id;
|
|
7088
7399
|
const body = data.req;
|
|
7089
7400
|
const req = { ...body, token };
|
|
7090
7401
|
let dispatched;
|
|
7091
7402
|
try {
|
|
7092
7403
|
dispatched = dispatch2(req);
|
|
7093
7404
|
} catch (err) {
|
|
7094
|
-
settle3(
|
|
7405
|
+
settle3(id2, {
|
|
7095
7406
|
ok: false,
|
|
7096
7407
|
errno: "EIO",
|
|
7097
7408
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -7099,8 +7410,8 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7099
7410
|
return;
|
|
7100
7411
|
}
|
|
7101
7412
|
void dispatched.then(
|
|
7102
|
-
(result) => settle3(
|
|
7103
|
-
(err) => settle3(
|
|
7413
|
+
(result) => settle3(id2, result),
|
|
7414
|
+
(err) => settle3(id2, {
|
|
7104
7415
|
ok: false,
|
|
7105
7416
|
errno: "EIO",
|
|
7106
7417
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -7113,7 +7424,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7113
7424
|
if (disposed) return;
|
|
7114
7425
|
disposed = true;
|
|
7115
7426
|
port.removeEventListener("message", handler);
|
|
7116
|
-
for (const
|
|
7427
|
+
for (const id2 of [...pending.keys()]) drop(id2);
|
|
7117
7428
|
}
|
|
7118
7429
|
};
|
|
7119
7430
|
}
|
|
@@ -7173,47 +7484,47 @@ var AsyncOps = class {
|
|
|
7173
7484
|
this.onReady = onReady;
|
|
7174
7485
|
}
|
|
7175
7486
|
submit(req) {
|
|
7176
|
-
const
|
|
7487
|
+
const id2 = this.hold();
|
|
7177
7488
|
const running = new AbortController();
|
|
7178
|
-
this.running.set(
|
|
7489
|
+
this.running.set(id2, running);
|
|
7179
7490
|
this.run(req, running.signal).then(
|
|
7180
|
-
(result) => this.complete(
|
|
7181
|
-
(err) => this.complete(
|
|
7491
|
+
(result) => this.complete(id2, result),
|
|
7492
|
+
(err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
|
|
7182
7493
|
);
|
|
7183
|
-
return
|
|
7494
|
+
return id2;
|
|
7184
7495
|
}
|
|
7185
7496
|
resolve(value) {
|
|
7186
|
-
const
|
|
7187
|
-
this.complete(
|
|
7188
|
-
return
|
|
7497
|
+
const id2 = this.hold();
|
|
7498
|
+
this.complete(id2, json3(value));
|
|
7499
|
+
return id2;
|
|
7189
7500
|
}
|
|
7190
7501
|
hold() {
|
|
7191
|
-
const
|
|
7192
|
-
this.results.set(
|
|
7193
|
-
return
|
|
7502
|
+
const id2 = this.next++;
|
|
7503
|
+
this.results.set(id2, void 0);
|
|
7504
|
+
return id2;
|
|
7194
7505
|
}
|
|
7195
|
-
take(
|
|
7196
|
-
if (!this.results.has(
|
|
7197
|
-
const result = this.results.get(
|
|
7506
|
+
take(id2) {
|
|
7507
|
+
if (!this.results.has(id2)) throw new KernelError("EINVAL");
|
|
7508
|
+
const result = this.results.get(id2);
|
|
7198
7509
|
if (!result) throw new KernelError("EAGAIN");
|
|
7199
|
-
this.forget(
|
|
7510
|
+
this.forget(id2);
|
|
7200
7511
|
return result;
|
|
7201
7512
|
}
|
|
7202
|
-
cancel(
|
|
7203
|
-
this.running.get(
|
|
7204
|
-
this.forget(
|
|
7513
|
+
cancel(id2) {
|
|
7514
|
+
this.running.get(id2)?.abort();
|
|
7515
|
+
this.forget(id2);
|
|
7205
7516
|
}
|
|
7206
|
-
forget(
|
|
7207
|
-
this.running.delete(
|
|
7208
|
-
this.results.delete(
|
|
7209
|
-
const at = this.ready.indexOf(
|
|
7517
|
+
forget(id2) {
|
|
7518
|
+
this.running.delete(id2);
|
|
7519
|
+
this.results.delete(id2);
|
|
7520
|
+
const at = this.ready.indexOf(id2);
|
|
7210
7521
|
if (at >= 0) this.ready.splice(at, 1);
|
|
7211
7522
|
}
|
|
7212
7523
|
async wait(timeoutMs, signal) {
|
|
7213
7524
|
for (; ; ) {
|
|
7214
7525
|
if (this.closed) return -1;
|
|
7215
|
-
const
|
|
7216
|
-
if (
|
|
7526
|
+
const id2 = this.ready.shift();
|
|
7527
|
+
if (id2 !== void 0) return id2;
|
|
7217
7528
|
if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
|
|
7218
7529
|
const woke = await this.sleep(timeoutMs, signal);
|
|
7219
7530
|
if (!woke) return 0;
|
|
@@ -7249,11 +7560,11 @@ var AsyncOps = class {
|
|
|
7249
7560
|
}
|
|
7250
7561
|
});
|
|
7251
7562
|
}
|
|
7252
|
-
complete(
|
|
7253
|
-
this.running.delete(
|
|
7254
|
-
if (!this.results.has(
|
|
7255
|
-
this.results.set(
|
|
7256
|
-
this.ready.push(
|
|
7563
|
+
complete(id2, result) {
|
|
7564
|
+
this.running.delete(id2);
|
|
7565
|
+
if (!this.results.has(id2)) return;
|
|
7566
|
+
this.results.set(id2, result);
|
|
7567
|
+
this.ready.push(id2);
|
|
7257
7568
|
this.wake();
|
|
7258
7569
|
this.onReady?.();
|
|
7259
7570
|
}
|
|
@@ -7449,21 +7760,21 @@ var Resolver = class {
|
|
|
7449
7760
|
if (!this.uplink || key === "") return [];
|
|
7450
7761
|
const asked = this.uplink.traits.ipv6 ? family : 4;
|
|
7451
7762
|
if (family === 6 && asked !== 6) return [];
|
|
7452
|
-
const
|
|
7453
|
-
const hit = this.cache.get(
|
|
7763
|
+
const id2 = `${asked}:${key}`;
|
|
7764
|
+
const hit = this.cache.get(id2);
|
|
7454
7765
|
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
7455
7766
|
const signal = blocking?.();
|
|
7456
7767
|
if (signal?.aborted) throw new KernelError("EINTR");
|
|
7457
|
-
let lookup = this.pending.get(
|
|
7768
|
+
let lookup = this.pending.get(id2);
|
|
7458
7769
|
if (!lookup) {
|
|
7459
7770
|
const abort = new AbortController();
|
|
7460
|
-
lookup = { work: this.lookup(key, asked,
|
|
7461
|
-
this.pending.set(
|
|
7462
|
-
void lookup.work.then(() => this.pending.delete(
|
|
7771
|
+
lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
|
|
7772
|
+
this.pending.set(id2, lookup);
|
|
7773
|
+
void lookup.work.then(() => this.pending.delete(id2));
|
|
7463
7774
|
}
|
|
7464
7775
|
return waitFor(lookup, signal);
|
|
7465
7776
|
}
|
|
7466
|
-
async lookup(name2, family,
|
|
7777
|
+
async lookup(name2, family, id2, abandoned) {
|
|
7467
7778
|
const uplink = this.uplink;
|
|
7468
7779
|
const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
|
|
7469
7780
|
let answer;
|
|
@@ -7480,7 +7791,7 @@ var Resolver = class {
|
|
|
7480
7791
|
return [];
|
|
7481
7792
|
}
|
|
7482
7793
|
const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
|
|
7483
|
-
if (addresses.length > 0) this.remember(
|
|
7794
|
+
if (addresses.length > 0) this.remember(id2, addresses, answer.ttl);
|
|
7484
7795
|
return addresses;
|
|
7485
7796
|
}
|
|
7486
7797
|
usable(name2, address, family) {
|
|
@@ -7492,11 +7803,11 @@ var Resolver = class {
|
|
|
7492
7803
|
this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
|
|
7493
7804
|
return false;
|
|
7494
7805
|
}
|
|
7495
|
-
remember(
|
|
7806
|
+
remember(id2, addresses, ttl) {
|
|
7496
7807
|
const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
|
|
7497
7808
|
if (seconds <= 0) return;
|
|
7498
|
-
this.cache.delete(
|
|
7499
|
-
this.cache.set(
|
|
7809
|
+
this.cache.delete(id2);
|
|
7810
|
+
this.cache.set(id2, { addresses, expires: this.now() + seconds * 1e3 });
|
|
7500
7811
|
if (this.cache.size > RESOLVE_CACHE_MAX) {
|
|
7501
7812
|
this.cache.delete(this.cache.keys().next().value);
|
|
7502
7813
|
}
|
|
@@ -8938,6 +9249,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
8938
9249
|
"fd-list",
|
|
8939
9250
|
"proc-alarm",
|
|
8940
9251
|
"proc-umask",
|
|
9252
|
+
"proc-cred",
|
|
9253
|
+
"proc-setcred",
|
|
8941
9254
|
"dl-log",
|
|
8942
9255
|
"fd-renumber",
|
|
8943
9256
|
"fd-promote",
|
|
@@ -8988,6 +9301,7 @@ var WasmProcess = class {
|
|
|
8988
9301
|
interrupt = new AbortController();
|
|
8989
9302
|
dying = false;
|
|
8990
9303
|
umask = DEFAULT_UMASK;
|
|
9304
|
+
cred;
|
|
8991
9305
|
execChild;
|
|
8992
9306
|
killed = false;
|
|
8993
9307
|
execTermsig;
|
|
@@ -9007,6 +9321,7 @@ var WasmProcess = class {
|
|
|
9007
9321
|
this.options = options;
|
|
9008
9322
|
this.inherited = options.ignored ?? 0;
|
|
9009
9323
|
this.umask = options.umask ?? DEFAULT_UMASK;
|
|
9324
|
+
this.cred = copyCred(options.cred ?? ROOT);
|
|
9010
9325
|
this.children = new ChildTable(fds, options.spawner, options.forker);
|
|
9011
9326
|
this.nodes = options.nodes ?? new VfsNodes(options.fs);
|
|
9012
9327
|
if (!options.nodes) options.openFiles?.add(this.nodes);
|
|
@@ -9523,6 +9838,11 @@ var WasmProcess = class {
|
|
|
9523
9838
|
return { ok: true, kind: "void" };
|
|
9524
9839
|
case "proc-umask":
|
|
9525
9840
|
return { ok: true, kind: "json", json: this.setUmask(req.mask) };
|
|
9841
|
+
case "proc-cred":
|
|
9842
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9843
|
+
case "proc-setcred":
|
|
9844
|
+
this.cred = changeCred(this.cred, req.change);
|
|
9845
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9526
9846
|
case "dl-log":
|
|
9527
9847
|
if (req.append) this.dlLog.push(req.append);
|
|
9528
9848
|
return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
|
|
@@ -9617,9 +9937,9 @@ var KILL_GRACE_MS = 2e3;
|
|
|
9617
9937
|
var descIds = /* @__PURE__ */ new WeakMap();
|
|
9618
9938
|
var nextDescId = 1;
|
|
9619
9939
|
function descId(file) {
|
|
9620
|
-
const
|
|
9621
|
-
descIds.set(file,
|
|
9622
|
-
return
|
|
9940
|
+
const id2 = descIds.get(file) ?? nextDescId++;
|
|
9941
|
+
descIds.set(file, id2);
|
|
9942
|
+
return id2;
|
|
9623
9943
|
}
|
|
9624
9944
|
function inheritedFds(fds) {
|
|
9625
9945
|
return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
|
|
@@ -9736,7 +10056,8 @@ function processInit(opts, sab, umask, lane) {
|
|
|
9736
10056
|
function inherited(opts) {
|
|
9737
10057
|
return {
|
|
9738
10058
|
...opts.ignored ? { ignored: opts.ignored } : {},
|
|
9739
|
-
...opts.umask !== void 0 ? { umask: opts.umask } : {}
|
|
10059
|
+
...opts.umask !== void 0 ? { umask: opts.umask } : {},
|
|
10060
|
+
...opts.cred ? { cred: opts.cred } : {}
|
|
9740
10061
|
};
|
|
9741
10062
|
}
|
|
9742
10063
|
function holdingToken(opts, holds) {
|
|
@@ -9900,6 +10221,7 @@ function spawnWasmProcess(opts) {
|
|
|
9900
10221
|
ignoredSignals: (fork) => process.inheritable(fork),
|
|
9901
10222
|
signalMasks: () => process.signalMasks(),
|
|
9902
10223
|
umask: () => process.umask,
|
|
10224
|
+
cred: () => copyCred(process.cred),
|
|
9903
10225
|
writesBack: () => opts.program.abi === "wasi" && !done4
|
|
9904
10226
|
};
|
|
9905
10227
|
}
|
|
@@ -9958,6 +10280,9 @@ var JobTable = class {
|
|
|
9958
10280
|
pgidOf(pid) {
|
|
9959
10281
|
return this.members.get(pid)?.pgid;
|
|
9960
10282
|
}
|
|
10283
|
+
sidOf(pid) {
|
|
10284
|
+
return this.members.get(pid)?.sid;
|
|
10285
|
+
}
|
|
9961
10286
|
member(pid) {
|
|
9962
10287
|
const member = this.members.get(pid);
|
|
9963
10288
|
if (!member) throw new KernelError("ESRCH");
|
|
@@ -9966,27 +10291,27 @@ var JobTable = class {
|
|
|
9966
10291
|
idOf(pid) {
|
|
9967
10292
|
return this.members.get(pid)?.shownAs ?? pid;
|
|
9968
10293
|
}
|
|
9969
|
-
byId(
|
|
9970
|
-
for (const member of this.members.values()) if (member.shownAs ===
|
|
9971
|
-
return this.member(
|
|
10294
|
+
byId(id2) {
|
|
10295
|
+
for (const member of this.members.values()) if (member.shownAs === id2) return member;
|
|
10296
|
+
return this.member(id2);
|
|
9972
10297
|
}
|
|
9973
10298
|
setpgid(caller, pid, pgid) {
|
|
9974
10299
|
const me = this.idOf(caller);
|
|
9975
10300
|
const target = this.byId(pid || me);
|
|
9976
|
-
const
|
|
9977
|
-
const group = pgid ||
|
|
10301
|
+
const id2 = this.idOf(target.pid);
|
|
10302
|
+
const group = pgid || id2;
|
|
9978
10303
|
if (group < 0) throw new KernelError("EINVAL");
|
|
9979
10304
|
const self = this.member(caller);
|
|
9980
|
-
if (
|
|
10305
|
+
if (id2 !== me) {
|
|
9981
10306
|
if (target.ppid === void 0 || this.idOf(target.ppid) !== me) {
|
|
9982
10307
|
throw new KernelError("ESRCH");
|
|
9983
10308
|
}
|
|
9984
10309
|
if (target.execed) throw new KernelError("EACCES");
|
|
9985
10310
|
}
|
|
9986
10311
|
if (target.sid !== self.sid) throw new KernelError("EPERM");
|
|
9987
|
-
if (
|
|
10312
|
+
if (id2 === target.sid) throw new KernelError("EPERM");
|
|
9988
10313
|
const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
|
|
9989
|
-
if (group !==
|
|
10314
|
+
if (group !== id2 && !exists) throw new KernelError("EPERM");
|
|
9990
10315
|
target.pgid = group;
|
|
9991
10316
|
}
|
|
9992
10317
|
getpgid(caller, pid) {
|
|
@@ -9997,12 +10322,12 @@ var JobTable = class {
|
|
|
9997
10322
|
}
|
|
9998
10323
|
setsid(pid) {
|
|
9999
10324
|
const member = this.member(pid);
|
|
10000
|
-
const
|
|
10001
|
-
if (member.pgid ===
|
|
10002
|
-
member.sid =
|
|
10003
|
-
member.pgid =
|
|
10004
|
-
this.terminals.delete(
|
|
10005
|
-
return
|
|
10325
|
+
const id2 = this.idOf(pid);
|
|
10326
|
+
if (member.pgid === id2) throw new KernelError("EPERM");
|
|
10327
|
+
member.sid = id2;
|
|
10328
|
+
member.pgid = id2;
|
|
10329
|
+
this.terminals.delete(id2);
|
|
10330
|
+
return id2;
|
|
10006
10331
|
}
|
|
10007
10332
|
terminalNamed(name2) {
|
|
10008
10333
|
return this.knownTerminals().find((tty) => tty.name === name2);
|
|
@@ -10019,15 +10344,17 @@ var JobTable = class {
|
|
|
10019
10344
|
}
|
|
10020
10345
|
acquireTerminal(pid, tty) {
|
|
10021
10346
|
const member = this.members.get(pid);
|
|
10022
|
-
const
|
|
10023
|
-
if (!member || member.sid !==
|
|
10347
|
+
const id2 = this.idOf(pid);
|
|
10348
|
+
if (!member || member.sid !== id2 || this.terminals.has(id2)) return false;
|
|
10024
10349
|
if ([...this.terminals.values()].includes(tty)) return false;
|
|
10025
|
-
this.terminals.set(
|
|
10350
|
+
this.terminals.set(id2, tty);
|
|
10026
10351
|
this.foreground.set(tty, member.pgid);
|
|
10027
10352
|
return true;
|
|
10028
10353
|
}
|
|
10029
|
-
killGroup(pgid, sig) {
|
|
10030
|
-
const
|
|
10354
|
+
killGroup(pgid, sig, may = () => true) {
|
|
10355
|
+
const group = [...this.members.values()].filter((m) => m.pgid === pgid);
|
|
10356
|
+
const targets = group.filter((m) => may(m.pid));
|
|
10357
|
+
if (targets.length === 0 && group.length > 0) throw new KernelError("EPERM");
|
|
10031
10358
|
const pids = new Set(targets.map((m) => m.pid));
|
|
10032
10359
|
if (sig !== 0) {
|
|
10033
10360
|
for (const m of targets)
|
|
@@ -10489,7 +10816,7 @@ function unescape(field2) {
|
|
|
10489
10816
|
);
|
|
10490
10817
|
}
|
|
10491
10818
|
function parseFstab(text) {
|
|
10492
|
-
const
|
|
10819
|
+
const lines2 = [];
|
|
10493
10820
|
text.split("\n").forEach((raw, index) => {
|
|
10494
10821
|
const content = raw.replace(/#.*$/, "").trim();
|
|
10495
10822
|
if (!content) return;
|
|
@@ -10498,12 +10825,12 @@ function parseFstab(text) {
|
|
|
10498
10825
|
if (options.split(",").includes("noauto")) return;
|
|
10499
10826
|
const spec = { source, target, type, options: {} };
|
|
10500
10827
|
try {
|
|
10501
|
-
|
|
10828
|
+
lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
10502
10829
|
} catch (err) {
|
|
10503
|
-
|
|
10830
|
+
lines2.push({ line: index + 1, spec, invalid: err });
|
|
10504
10831
|
}
|
|
10505
10832
|
});
|
|
10506
|
-
return
|
|
10833
|
+
return lines2;
|
|
10507
10834
|
}
|
|
10508
10835
|
function codeOf3(err) {
|
|
10509
10836
|
const code = err?.code;
|
|
@@ -10521,11 +10848,11 @@ function wait(ms, signal) {
|
|
|
10521
10848
|
signal.addEventListener("abort", done4, { once: true });
|
|
10522
10849
|
});
|
|
10523
10850
|
}
|
|
10524
|
-
async function mountFstab(
|
|
10851
|
+
async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
10525
10852
|
}, settled = () => {
|
|
10526
10853
|
}) {
|
|
10527
10854
|
return Promise.all(
|
|
10528
|
-
|
|
10855
|
+
lines2.map(async (each) => {
|
|
10529
10856
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
10530
10857
|
settled(each, result);
|
|
10531
10858
|
return result;
|
|
@@ -11172,10 +11499,10 @@ function importedMemory(bytes) {
|
|
|
11172
11499
|
return { initial, maximum, shared: (flags & 2) !== 0 };
|
|
11173
11500
|
};
|
|
11174
11501
|
while (at < bytes.length) {
|
|
11175
|
-
const
|
|
11502
|
+
const id2 = bytes[at++];
|
|
11176
11503
|
const size = u32();
|
|
11177
11504
|
const end = at + size;
|
|
11178
|
-
if (
|
|
11505
|
+
if (id2 !== 2) {
|
|
11179
11506
|
at = end;
|
|
11180
11507
|
continue;
|
|
11181
11508
|
}
|
|
@@ -11295,18 +11622,17 @@ function foreignImports(bytes) {
|
|
|
11295
11622
|
const r = new Reader(bytes);
|
|
11296
11623
|
let types = [];
|
|
11297
11624
|
while (r.at < bytes.length) {
|
|
11298
|
-
const
|
|
11625
|
+
const id2 = r.byte();
|
|
11299
11626
|
const end = r.u32() + r.at;
|
|
11300
|
-
if (
|
|
11627
|
+
if (id2 === 1) types = readTypes(r);
|
|
11301
11628
|
if (!types) return void 0;
|
|
11302
|
-
if (
|
|
11629
|
+
if (id2 === 2) return readImports(r, types);
|
|
11303
11630
|
r.at = end;
|
|
11304
11631
|
}
|
|
11305
11632
|
return {};
|
|
11306
11633
|
}
|
|
11307
11634
|
|
|
11308
11635
|
// src/launcher.ts
|
|
11309
|
-
var PNPM_HOME = "/home/.local/share/pnpm";
|
|
11310
11636
|
var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
11311
11637
|
var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
11312
11638
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -11406,6 +11732,8 @@ var Launcher = class {
|
|
|
11406
11732
|
zombies = /* @__PURE__ */ new Set();
|
|
11407
11733
|
orphans = /* @__PURE__ */ new Set();
|
|
11408
11734
|
described = /* @__PURE__ */ new Map();
|
|
11735
|
+
creds = /* @__PURE__ */ new Map();
|
|
11736
|
+
users;
|
|
11409
11737
|
jobs = new JobTable();
|
|
11410
11738
|
locks = new LockTable();
|
|
11411
11739
|
settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
|
|
@@ -11457,7 +11785,7 @@ var Launcher = class {
|
|
|
11457
11785
|
this.hostfs = options;
|
|
11458
11786
|
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
11459
11787
|
this.processMounts = options.processMounts ?? true;
|
|
11460
|
-
this.pnpmHome = options.env?.PNPM_HOME ??
|
|
11788
|
+
this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
|
|
11461
11789
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
11462
11790
|
this.cdp = new CdpHosts(options.cdp);
|
|
11463
11791
|
const hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
@@ -11493,6 +11821,7 @@ var Launcher = class {
|
|
|
11493
11821
|
}
|
|
11494
11822
|
);
|
|
11495
11823
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
11824
|
+
this.users = new UserDb(this.fs);
|
|
11496
11825
|
void this.unlinked.sweep("/");
|
|
11497
11826
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
11498
11827
|
this.catalog = void 0;
|
|
@@ -11693,6 +12022,7 @@ var Launcher = class {
|
|
|
11693
12022
|
},
|
|
11694
12023
|
...req.ignored ? { ignored: req.ignored } : {},
|
|
11695
12024
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
12025
|
+
cred: req.cred,
|
|
11696
12026
|
program: req.program,
|
|
11697
12027
|
argv0: req.argv0,
|
|
11698
12028
|
args: req.args,
|
|
@@ -11704,9 +12034,9 @@ var Launcher = class {
|
|
|
11704
12034
|
onError: req.report,
|
|
11705
12035
|
spawner: this.spawner(pid, req.report),
|
|
11706
12036
|
forker: this.forker(pid, req),
|
|
11707
|
-
kill: (target, sig) => this.kill(target, sig),
|
|
12037
|
+
kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
|
|
11708
12038
|
writesBack: (target) => this.processes.get(target)?.writesBack() === true,
|
|
11709
|
-
processes: () => ({ boot: this.boot, processes: this.list() }),
|
|
12039
|
+
processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
|
|
11710
12040
|
openFiles: this.openFiles,
|
|
11711
12041
|
nodes: this.nodes,
|
|
11712
12042
|
held: this.held,
|
|
@@ -11726,6 +12056,8 @@ var Launcher = class {
|
|
|
11726
12056
|
...this.shownIds(req)
|
|
11727
12057
|
});
|
|
11728
12058
|
this.processes.set(pid, handle);
|
|
12059
|
+
const { cred } = req;
|
|
12060
|
+
this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
|
|
11729
12061
|
if (req.ppid !== void 0) this.settling.started(pid, req.ppid);
|
|
11730
12062
|
this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
|
|
11731
12063
|
if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
|
|
@@ -11752,12 +12084,13 @@ var Launcher = class {
|
|
|
11752
12084
|
forget(pid) {
|
|
11753
12085
|
this.jobs.remove(pid);
|
|
11754
12086
|
this.described.delete(pid);
|
|
12087
|
+
this.creds.delete(pid);
|
|
11755
12088
|
}
|
|
11756
12089
|
reaped(pid) {
|
|
11757
12090
|
if (this.zombies.delete(pid)) this.forget(pid);
|
|
11758
12091
|
else this.orphans.add(pid);
|
|
11759
12092
|
}
|
|
11760
|
-
list() {
|
|
12093
|
+
list(viewer = ROOT) {
|
|
11761
12094
|
const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
|
|
11762
12095
|
const rootOf = (member) => {
|
|
11763
12096
|
let root = member;
|
|
@@ -11775,6 +12108,8 @@ var Launcher = class {
|
|
|
11775
12108
|
for (const member of members.values()) {
|
|
11776
12109
|
const described = this.described.get(member.pid);
|
|
11777
12110
|
if (member.execed || !described) continue;
|
|
12111
|
+
const cred = this.credOf(member.pid);
|
|
12112
|
+
if (!maySee(viewer, cred)) continue;
|
|
11778
12113
|
const root = rootOf(member);
|
|
11779
12114
|
listed.push({
|
|
11780
12115
|
pid: root.pid,
|
|
@@ -11787,6 +12122,7 @@ var Launcher = class {
|
|
|
11787
12122
|
state: this.processes.has(member.pid) ? "S" : "Z",
|
|
11788
12123
|
memory: this.processes.get(member.pid)?.memory() ?? 0,
|
|
11789
12124
|
umask: this.umaskOf(member.pid),
|
|
12125
|
+
cred,
|
|
11790
12126
|
...this.signalMasksOf(member.pid)
|
|
11791
12127
|
});
|
|
11792
12128
|
}
|
|
@@ -11810,6 +12146,7 @@ var Launcher = class {
|
|
|
11810
12146
|
ppid,
|
|
11811
12147
|
ignored: this.ignoredBy(ppid),
|
|
11812
12148
|
umask: this.umaskOf(ppid),
|
|
12149
|
+
cred: this.credOf(ppid),
|
|
11813
12150
|
...exec
|
|
11814
12151
|
});
|
|
11815
12152
|
deciding?.attach();
|
|
@@ -11855,7 +12192,8 @@ var Launcher = class {
|
|
|
11855
12192
|
ppid,
|
|
11856
12193
|
fork: state,
|
|
11857
12194
|
ignored: this.ignoredBy(ppid, true),
|
|
11858
|
-
umask: this.umaskOf(ppid)
|
|
12195
|
+
umask: this.umaskOf(ppid),
|
|
12196
|
+
cred: this.credOf(ppid)
|
|
11859
12197
|
})
|
|
11860
12198
|
);
|
|
11861
12199
|
}
|
|
@@ -11867,6 +12205,18 @@ var Launcher = class {
|
|
|
11867
12205
|
umaskOf(pid) {
|
|
11868
12206
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
11869
12207
|
}
|
|
12208
|
+
imageOf(pid) {
|
|
12209
|
+
const members = this.jobs.list();
|
|
12210
|
+
let image = pid;
|
|
12211
|
+
for (; ; ) {
|
|
12212
|
+
const next2 = members.find((m) => m.execParent === image);
|
|
12213
|
+
if (!next2) return image;
|
|
12214
|
+
image = next2.pid;
|
|
12215
|
+
}
|
|
12216
|
+
}
|
|
12217
|
+
credOf(pid) {
|
|
12218
|
+
return this.creds.get(pid)?.() ?? copyCred(ROOT);
|
|
12219
|
+
}
|
|
11870
12220
|
released(path) {
|
|
11871
12221
|
const at = normalizePath(path);
|
|
11872
12222
|
this.unlinked.closed(at);
|
|
@@ -11880,10 +12230,13 @@ var Launcher = class {
|
|
|
11880
12230
|
ignoredBy(pid, fork = false) {
|
|
11881
12231
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
11882
12232
|
}
|
|
11883
|
-
kill(pid, sig) {
|
|
11884
|
-
|
|
12233
|
+
kill(pid, sig, sender, from) {
|
|
12234
|
+
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
12235
|
+
const may = (target) => !sender || maySignal(sender, this.credOf(target)) || sig === SIG.CONT && session !== void 0 && this.jobs.sidOf(target) === session || from !== void 0 && this.imageOf(from) === this.imageOf(target);
|
|
12236
|
+
if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
|
|
11885
12237
|
const handle = this.processes.get(pid);
|
|
11886
12238
|
if (!handle) return false;
|
|
12239
|
+
if (!may(this.imageOf(pid))) throw fsError("EPERM", `pid ${pid}`);
|
|
11887
12240
|
if (sig !== 0) handle.signal(sig);
|
|
11888
12241
|
return true;
|
|
11889
12242
|
}
|
|
@@ -11954,9 +12307,9 @@ var Launcher = class {
|
|
|
11954
12307
|
ask(handle);
|
|
11955
12308
|
});
|
|
11956
12309
|
const served = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
|
|
11957
|
-
const
|
|
11958
|
-
this.removable.set(target, { id, medium, served });
|
|
11959
|
-
const stored = this.inserted.get(
|
|
12310
|
+
const id2 = source.slice("fsa:".length);
|
|
12311
|
+
this.removable.set(target, { id: id2, medium, served });
|
|
12312
|
+
const stored = this.inserted.get(id2) ?? await this.media.get(id2);
|
|
11960
12313
|
if (stored && await granted(stored)) medium.insert(stored);
|
|
11961
12314
|
else ask(stored);
|
|
11962
12315
|
return {
|
|
@@ -12040,15 +12393,16 @@ var Launcher = class {
|
|
|
12040
12393
|
}
|
|
12041
12394
|
async prepare() {
|
|
12042
12395
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
12396
|
+
await this.users.ensure();
|
|
12043
12397
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
12044
12398
|
this.fstab = this.mountFstab();
|
|
12045
12399
|
}
|
|
12046
12400
|
async mountFstab() {
|
|
12047
12401
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
12048
|
-
const
|
|
12049
|
-
for (const { spec } of
|
|
12402
|
+
const lines2 = parseFstab(text);
|
|
12403
|
+
for (const { spec } of lines2) this.mounts.note(spec, "pending");
|
|
12050
12404
|
return mountFstab(
|
|
12051
|
-
|
|
12405
|
+
lines2,
|
|
12052
12406
|
(spec) => this.mount(spec).catch((err) => {
|
|
12053
12407
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
12054
12408
|
throw err;
|
|
@@ -12063,14 +12417,19 @@ var Launcher = class {
|
|
|
12063
12417
|
}
|
|
12064
12418
|
);
|
|
12065
12419
|
}
|
|
12066
|
-
environment(cwd, extra) {
|
|
12420
|
+
environment(cwd, extra, account) {
|
|
12421
|
+
return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
|
|
12422
|
+
}
|
|
12423
|
+
async account(user) {
|
|
12424
|
+
if (typeof user === "object") return user;
|
|
12425
|
+
const found = await this.users.lookup(user ?? 0);
|
|
12426
|
+
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
12427
|
+
return found;
|
|
12428
|
+
}
|
|
12429
|
+
identity(account) {
|
|
12067
12430
|
return {
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
PNPM_HOME: this.pnpmHome,
|
|
12071
|
-
...this.env,
|
|
12072
|
-
PWD: cwd,
|
|
12073
|
-
...extra
|
|
12431
|
+
cred: credOf(account),
|
|
12432
|
+
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
12074
12433
|
};
|
|
12075
12434
|
}
|
|
12076
12435
|
async starting(argv, dir) {
|
|
@@ -12083,6 +12442,7 @@ var Launcher = class {
|
|
|
12083
12442
|
return { cwd, file, planned: await this.plan(file, argv, cwd) };
|
|
12084
12443
|
}
|
|
12085
12444
|
async openTerminal(argv, options) {
|
|
12445
|
+
const account = await this.account(options.user);
|
|
12086
12446
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
12087
12447
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
12088
12448
|
let leader = 0;
|
|
@@ -12098,14 +12458,18 @@ var Launcher = class {
|
|
|
12098
12458
|
fds.installAt(0, stdio);
|
|
12099
12459
|
fds.installAt(1, stdio.retain());
|
|
12100
12460
|
fds.installAt(2, stdio.retain());
|
|
12101
|
-
const env = this.environment(
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12461
|
+
const env = this.environment(
|
|
12462
|
+
cwd,
|
|
12463
|
+
{
|
|
12464
|
+
TERM: "xterm-256color",
|
|
12465
|
+
COLORTERM: "truecolor",
|
|
12466
|
+
...options.env
|
|
12467
|
+
},
|
|
12468
|
+
account
|
|
12469
|
+
);
|
|
12106
12470
|
const report = (message2) => options.onData(encoder3.encode(`${message2}\r
|
|
12107
12471
|
`));
|
|
12108
|
-
const handle = await this.launch(planned, { env, cwd, fds, report });
|
|
12472
|
+
const handle = await this.launch(planned, { env, cwd, fds, report, ...this.identity(account) });
|
|
12109
12473
|
leader = handle.pid;
|
|
12110
12474
|
void handle.exited.then(() => tty.hangup());
|
|
12111
12475
|
return {
|
|
@@ -12121,8 +12485,9 @@ var Launcher = class {
|
|
|
12121
12485
|
};
|
|
12122
12486
|
}
|
|
12123
12487
|
async run(argv, options = {}) {
|
|
12488
|
+
const account = await this.account(options.user);
|
|
12124
12489
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
12125
|
-
const env = this.environment(cwd, options.env);
|
|
12490
|
+
const env = this.environment(cwd, options.env, account);
|
|
12126
12491
|
const out = [];
|
|
12127
12492
|
const err = [];
|
|
12128
12493
|
const keep = options.collect !== false;
|
|
@@ -12143,7 +12508,14 @@ var Launcher = class {
|
|
|
12143
12508
|
fds.installAt(1, sinkFile(stdout));
|
|
12144
12509
|
fds.installAt(2, sinkFile(stderr));
|
|
12145
12510
|
const group = options.pgid !== void 0 ? { pgid: options.pgid } : {};
|
|
12146
|
-
const handle = await this.launch(planned, {
|
|
12511
|
+
const handle = await this.launch(planned, {
|
|
12512
|
+
env,
|
|
12513
|
+
cwd,
|
|
12514
|
+
fds,
|
|
12515
|
+
report,
|
|
12516
|
+
...group,
|
|
12517
|
+
...this.identity(account)
|
|
12518
|
+
});
|
|
12147
12519
|
options.onStarted?.(handle.pid);
|
|
12148
12520
|
const status = await handle.exited;
|
|
12149
12521
|
return { status, stdout: concat5(out), stderr: concat5(err) };
|
|
@@ -12209,12 +12581,28 @@ function answered(data, remote, uplink) {
|
|
|
12209
12581
|
else return false;
|
|
12210
12582
|
return true;
|
|
12211
12583
|
}
|
|
12584
|
+
var USERS_OPS = /* @__PURE__ */ new Set(["users-add", "users-remove", "users-list"]);
|
|
12585
|
+
function isUsersRequest(req) {
|
|
12586
|
+
return USERS_OPS.has(req.op);
|
|
12587
|
+
}
|
|
12588
|
+
function failure(err) {
|
|
12589
|
+
const code = err?.code;
|
|
12590
|
+
return {
|
|
12591
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12592
|
+
...typeof code === "string" ? { code } : {}
|
|
12593
|
+
};
|
|
12594
|
+
}
|
|
12595
|
+
function usersOp(l, req) {
|
|
12596
|
+
if (req.op === "users-add") return l.users.add(req.user);
|
|
12597
|
+
if (req.op === "users-remove") return l.users.remove(req.name, req.options);
|
|
12598
|
+
return l.users.list();
|
|
12599
|
+
}
|
|
12212
12600
|
function serveKernel(port, deps) {
|
|
12213
12601
|
let launcher;
|
|
12214
12602
|
let remote;
|
|
12215
12603
|
let uplink;
|
|
12216
12604
|
const terminals = /* @__PURE__ */ new Map();
|
|
12217
|
-
const reply = (
|
|
12605
|
+
const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
|
|
12218
12606
|
const clients = /* @__PURE__ */ new Set();
|
|
12219
12607
|
const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
|
|
12220
12608
|
let holding;
|
|
@@ -12229,13 +12617,17 @@ function serveKernel(port, deps) {
|
|
|
12229
12617
|
});
|
|
12230
12618
|
return holding;
|
|
12231
12619
|
}
|
|
12232
|
-
async function connect() {
|
|
12233
|
-
await ready2();
|
|
12620
|
+
async function connect(user) {
|
|
12621
|
+
const l = await ready2();
|
|
12622
|
+
if (user !== void 0 && !await l.users.lookup(user)) {
|
|
12623
|
+
throw fsError("ENOENT", `no user ${String(user)}`);
|
|
12624
|
+
}
|
|
12234
12625
|
const { port1, port2 } = new MessageChannel();
|
|
12235
12626
|
const held = locks ? await holdLock(locks) : void 0;
|
|
12236
12627
|
const served = serveClient(port1, {
|
|
12237
12628
|
launcher: ready2,
|
|
12238
12629
|
signal: signalNumber,
|
|
12630
|
+
...user !== void 0 ? { user } : {},
|
|
12239
12631
|
...locks ? { locks } : {},
|
|
12240
12632
|
...held ? { lock: held } : {}
|
|
12241
12633
|
});
|
|
@@ -12304,7 +12696,8 @@ function serveKernel(port, deps) {
|
|
|
12304
12696
|
async function handle(req) {
|
|
12305
12697
|
if (req.op === "init") return init(req);
|
|
12306
12698
|
if (req.op === "open-terminal") return terminal(req);
|
|
12307
|
-
if (req.op === "connect") return connect();
|
|
12699
|
+
if (req.op === "connect") return connect(req.user);
|
|
12700
|
+
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
12308
12701
|
if (req.op === "routes") {
|
|
12309
12702
|
(await ready2()).setRoutes(req.routes);
|
|
12310
12703
|
return true;
|
|
@@ -12327,25 +12720,25 @@ function serveKernel(port, deps) {
|
|
|
12327
12720
|
const grants = /* @__PURE__ */ new Map();
|
|
12328
12721
|
let nextGrant = 0;
|
|
12329
12722
|
const askGrant = (source, options) => {
|
|
12330
|
-
const
|
|
12723
|
+
const id2 = ++nextGrant;
|
|
12331
12724
|
const waiting = Promise.withResolvers();
|
|
12332
|
-
grants.set(
|
|
12333
|
-
port.postMessage({ hostfs: { id, source, ...options } });
|
|
12725
|
+
grants.set(id2, waiting);
|
|
12726
|
+
port.postMessage({ hostfs: { id: id2, source, ...options } });
|
|
12334
12727
|
return waiting.promise;
|
|
12335
12728
|
};
|
|
12336
|
-
function granted2({ id, grant, error }) {
|
|
12337
|
-
const waiting = grants.get(
|
|
12729
|
+
function granted2({ id: id2, grant, error }) {
|
|
12730
|
+
const waiting = grants.get(id2);
|
|
12338
12731
|
if (!waiting) return;
|
|
12339
|
-
grants.delete(
|
|
12732
|
+
grants.delete(id2);
|
|
12340
12733
|
if (grant) waiting.resolve(grant);
|
|
12341
12734
|
else waiting.reject(Object.assign(new Error(error ?? "no grant"), { code: "EACCES" }));
|
|
12342
12735
|
}
|
|
12343
12736
|
const policies = /* @__PURE__ */ new Map();
|
|
12344
12737
|
let nextPolicy = 0;
|
|
12345
12738
|
const askPolicy = (req) => new Promise((resolve) => {
|
|
12346
|
-
const
|
|
12347
|
-
policies.set(
|
|
12348
|
-
port.postMessage({ mountPolicy: { id, req } });
|
|
12739
|
+
const id2 = ++nextPolicy;
|
|
12740
|
+
policies.set(id2, resolve);
|
|
12741
|
+
port.postMessage({ mountPolicy: { id: id2, req } });
|
|
12349
12742
|
});
|
|
12350
12743
|
const mountPolicy = (policy) => policy === void 0 ? {} : { processMounts: policy === "ask" ? askPolicy : policy };
|
|
12351
12744
|
port.addEventListener("message", (event) => {
|
|
@@ -12361,7 +12754,7 @@ function serveKernel(port, deps) {
|
|
|
12361
12754
|
const req = event.data;
|
|
12362
12755
|
handle(req).then(
|
|
12363
12756
|
(result) => reply(req.id, { result }, result instanceof MessagePort ? [result] : void 0),
|
|
12364
|
-
(err) => reply(req.id,
|
|
12757
|
+
(err) => reply(req.id, failure(err))
|
|
12365
12758
|
);
|
|
12366
12759
|
});
|
|
12367
12760
|
}
|