@ai-ecoverse/slicc-kernel 1.43.1 → 1.45.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 +42 -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 +783 -259
- 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 +230 -21
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +816 -300
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +230 -21
- package/dist/process-worker.js.map +4 -4
- package/package.json +2 -2
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",
|
|
@@ -1920,8 +1920,8 @@ function serveSocket(socket, port, onClose) {
|
|
|
1920
1920
|
let writes = Promise.resolve();
|
|
1921
1921
|
let credit = 0;
|
|
1922
1922
|
let granted2;
|
|
1923
|
-
const send = (
|
|
1924
|
-
if (!closed) port.postMessage(
|
|
1923
|
+
const send = (message3, transfer) => {
|
|
1924
|
+
if (!closed) port.postMessage(message3, transfer);
|
|
1925
1925
|
};
|
|
1926
1926
|
const close = (reset = false) => {
|
|
1927
1927
|
if (closed) return;
|
|
@@ -2036,8 +2036,8 @@ var BODY_IDLE_MS = 5 * 60 * 1e3;
|
|
|
2036
2036
|
var INBOUND_LIMIT = 16 * 1024 * 1024;
|
|
2037
2037
|
var MESSAGE_LIMIT = 64 * 1024 * 1024;
|
|
2038
2038
|
var TOO_BIG = 1009;
|
|
2039
|
-
function payloadSize(
|
|
2040
|
-
return typeof
|
|
2039
|
+
function payloadSize(message3) {
|
|
2040
|
+
return typeof message3 === "string" ? new TextEncoder().encode(message3).length : message3.length;
|
|
2041
2041
|
}
|
|
2042
2042
|
var MessageQueue = class {
|
|
2043
2043
|
items = [];
|
|
@@ -2057,9 +2057,9 @@ var MessageQueue = class {
|
|
|
2057
2057
|
this.settle = resolve;
|
|
2058
2058
|
});
|
|
2059
2059
|
}
|
|
2060
|
-
push(
|
|
2060
|
+
push(message3) {
|
|
2061
2061
|
if (this.ended) return;
|
|
2062
|
-
const size = payloadSize(
|
|
2062
|
+
const size = payloadSize(message3);
|
|
2063
2063
|
const huge = size > MESSAGE_LIMIT;
|
|
2064
2064
|
if (huge || this.items.length > 0 && this.size + size > this.limit) {
|
|
2065
2065
|
const reason = huge ? `a message is over ${MESSAGE_LIMIT} bytes` : `over ${this.limit} bytes of messages are waiting`;
|
|
@@ -2068,7 +2068,7 @@ var MessageQueue = class {
|
|
|
2068
2068
|
return;
|
|
2069
2069
|
}
|
|
2070
2070
|
this.size += size;
|
|
2071
|
-
this.items.push(
|
|
2071
|
+
this.items.push(message3);
|
|
2072
2072
|
this.wake();
|
|
2073
2073
|
}
|
|
2074
2074
|
end(close) {
|
|
@@ -2107,7 +2107,7 @@ var NO_WEBSOCKET = "WebSocket not supported by this transport (fetchTransport ne
|
|
|
2107
2107
|
var MAX_REQUEST_BODY = 64 * 1024 * 1024;
|
|
2108
2108
|
function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
2109
2109
|
const open = /* @__PURE__ */ new Map();
|
|
2110
|
-
const
|
|
2110
|
+
const fail2 = (nid, e) => {
|
|
2111
2111
|
open.delete(nid);
|
|
2112
2112
|
const { status, code } = e;
|
|
2113
2113
|
peer.postMessage({
|
|
@@ -2143,7 +2143,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2143
2143
|
headers: response.headers
|
|
2144
2144
|
});
|
|
2145
2145
|
} catch (e) {
|
|
2146
|
-
|
|
2146
|
+
fail2(call.nid, e);
|
|
2147
2147
|
}
|
|
2148
2148
|
};
|
|
2149
2149
|
const sockets = /* @__PURE__ */ new Map();
|
|
@@ -2177,7 +2177,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2177
2177
|
peer.postMessage({ net: "ws-opened", nid: call.nid, protocol: socket.protocol });
|
|
2178
2178
|
void relay(call.nid, socket);
|
|
2179
2179
|
} catch (e) {
|
|
2180
|
-
|
|
2180
|
+
fail2(call.nid, e);
|
|
2181
2181
|
}
|
|
2182
2182
|
};
|
|
2183
2183
|
const relay = async (nid, socket) => {
|
|
@@ -2249,7 +2249,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2249
2249
|
peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
|
|
2250
2250
|
}
|
|
2251
2251
|
} catch (e) {
|
|
2252
|
-
|
|
2252
|
+
fail2(nid, e);
|
|
2253
2253
|
}
|
|
2254
2254
|
};
|
|
2255
2255
|
const drop = (nid) => {
|
|
@@ -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,16 +2719,17 @@ 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();
|
|
2411
2726
|
const watches = /* @__PURE__ */ new Map();
|
|
2412
2727
|
let net;
|
|
2413
2728
|
const ended = Promise.withResolvers();
|
|
2414
|
-
const send = (
|
|
2415
|
-
if (state !== "closed") port.postMessage(
|
|
2729
|
+
const send = (message3, transfer = []) => {
|
|
2730
|
+
if (state !== "closed") port.postMessage(message3, 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;
|
|
@@ -2431,18 +2746,19 @@ function serveClient(port, host) {
|
|
|
2431
2746
|
port.close?.();
|
|
2432
2747
|
ended.resolve();
|
|
2433
2748
|
}
|
|
2434
|
-
async function hello(
|
|
2435
|
-
const refused = versionError(
|
|
2749
|
+
async function hello(message3) {
|
|
2750
|
+
const refused = versionError(message3.protocol);
|
|
2436
2751
|
if (refused) {
|
|
2437
2752
|
send({ hello: { protocol: PROTOCOL, error: refused } });
|
|
2438
2753
|
detach();
|
|
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";
|
|
2445
|
-
if (
|
|
2761
|
+
if (message3.lock && host.locks) void host.locks.request(message3.lock, () => detach());
|
|
2446
2762
|
send({
|
|
2447
2763
|
hello: {
|
|
2448
2764
|
protocol: PROTOCOL,
|
|
@@ -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}`);
|
|
@@ -2607,8 +2918,8 @@ function ownNames(hostname) {
|
|
|
2607
2918
|
// src/kernel/net/http1.ts
|
|
2608
2919
|
var HttpError = class extends Error {
|
|
2609
2920
|
status;
|
|
2610
|
-
constructor(status,
|
|
2611
|
-
super(
|
|
2921
|
+
constructor(status, message3) {
|
|
2922
|
+
super(message3);
|
|
2612
2923
|
this.status = status;
|
|
2613
2924
|
}
|
|
2614
2925
|
};
|
|
@@ -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");
|
|
@@ -2871,8 +3182,8 @@ var OP = {
|
|
|
2871
3182
|
};
|
|
2872
3183
|
var WsError = class extends Error {
|
|
2873
3184
|
code;
|
|
2874
|
-
constructor(code,
|
|
2875
|
-
super(
|
|
3185
|
+
constructor(code, message3) {
|
|
3186
|
+
super(message3);
|
|
2876
3187
|
this.code = code;
|
|
2877
3188
|
}
|
|
2878
3189
|
};
|
|
@@ -3238,8 +3549,8 @@ function untilAborted(response, signal) {
|
|
|
3238
3549
|
response.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
3239
3550
|
});
|
|
3240
3551
|
}
|
|
3241
|
-
function plainResponse(status,
|
|
3242
|
-
const body = latin1Bytes(`slicc realm proxy: ${
|
|
3552
|
+
function plainResponse(status, message3) {
|
|
3553
|
+
const body = latin1Bytes(`slicc realm proxy: ${message3}
|
|
3243
3554
|
`);
|
|
3244
3555
|
return {
|
|
3245
3556
|
status,
|
|
@@ -3421,10 +3732,10 @@ var RealmProxy = class {
|
|
|
3421
3732
|
return await untilAborted(fetching, signal);
|
|
3422
3733
|
} catch (e) {
|
|
3423
3734
|
if (signal.aborted) throw e;
|
|
3424
|
-
const
|
|
3735
|
+
const message3 = e instanceof Error ? e.message : String(e);
|
|
3425
3736
|
const status = e?.status;
|
|
3426
3737
|
const own = typeof status === "number" && status >= 400 && status <= 599 ? status : 502;
|
|
3427
|
-
return plainResponse(own,
|
|
3738
|
+
return plainResponse(own, message3 || "upstream request failed");
|
|
3428
3739
|
}
|
|
3429
3740
|
}
|
|
3430
3741
|
async relay(conn, method, minor, response, keepAlive) {
|
|
@@ -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(),
|
|
@@ -3929,8 +4240,8 @@ var SCRATCH = 64 * 1024;
|
|
|
3929
4240
|
var EOF = -1;
|
|
3930
4241
|
var TlsError = class extends Error {
|
|
3931
4242
|
code;
|
|
3932
|
-
constructor(code,
|
|
3933
|
-
super(
|
|
4243
|
+
constructor(code, message3) {
|
|
4244
|
+
super(message3);
|
|
3934
4245
|
this.code = code;
|
|
3935
4246
|
}
|
|
3936
4247
|
};
|
|
@@ -4319,8 +4630,8 @@ function enableNetwork(net, options) {
|
|
|
4319
4630
|
var TransportError = class extends Error {
|
|
4320
4631
|
status;
|
|
4321
4632
|
code;
|
|
4322
|
-
constructor(
|
|
4323
|
-
super(
|
|
4633
|
+
constructor(message3, status, code) {
|
|
4634
|
+
super(message3);
|
|
4324
4635
|
this.status = status;
|
|
4325
4636
|
this.code = code;
|
|
4326
4637
|
}
|
|
@@ -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) {
|
|
@@ -4778,11 +5089,11 @@ var CdpFacade = class {
|
|
|
4778
5089
|
clearTimeout(timer);
|
|
4779
5090
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
4780
5091
|
};
|
|
4781
|
-
conn.onmessage = (
|
|
4782
|
-
const
|
|
4783
|
-
if (
|
|
5092
|
+
conn.onmessage = (message3) => {
|
|
5093
|
+
const parsed2 = JSON.parse(message3);
|
|
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
|
}
|
|
@@ -4847,9 +5158,9 @@ var Relay = class {
|
|
|
4847
5158
|
void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
|
|
4848
5159
|
};
|
|
4849
5160
|
});
|
|
4850
|
-
this.host.onmessage = (
|
|
5161
|
+
this.host.onmessage = (message3) => {
|
|
4851
5162
|
if (this.ended) return;
|
|
4852
|
-
const bytes = new TextEncoder().encode(
|
|
5163
|
+
const bytes = new TextEncoder().encode(message3);
|
|
4853
5164
|
if (bytes.length > this.max)
|
|
4854
5165
|
void this.end(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
4855
5166
|
else void this.send(frame(OP_TEXT, bytes));
|
|
@@ -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);
|
|
@@ -5628,10 +5939,10 @@ var SpawnError = class extends Error {
|
|
|
5628
5939
|
};
|
|
5629
5940
|
function interrupted(signal) {
|
|
5630
5941
|
const { promise, reject } = Promise.withResolvers();
|
|
5631
|
-
const
|
|
5632
|
-
if (signal?.aborted)
|
|
5633
|
-
else signal?.addEventListener("abort",
|
|
5634
|
-
return { promise, done: () => signal?.removeEventListener("abort",
|
|
5942
|
+
const fail2 = () => reject(new KernelError("EINTR"));
|
|
5943
|
+
if (signal?.aborted) fail2();
|
|
5944
|
+
else signal?.addEventListener("abort", fail2, { once: true });
|
|
5945
|
+
return { promise, done: () => signal?.removeEventListener("abort", fail2) };
|
|
5635
5946
|
}
|
|
5636
5947
|
function stoppedStatus(sig) {
|
|
5637
5948
|
return (sig & 255) << 8 | 127;
|
|
@@ -5822,8 +6133,8 @@ function unref(timer) {
|
|
|
5822
6133
|
timer.unref?.();
|
|
5823
6134
|
return timer;
|
|
5824
6135
|
}
|
|
5825
|
-
function errnoError(code,
|
|
5826
|
-
return Object.assign(new Error(`${code}: ${
|
|
6136
|
+
function errnoError(code, message3) {
|
|
6137
|
+
return Object.assign(new Error(`${code}: ${message3}`), { code });
|
|
5827
6138
|
}
|
|
5828
6139
|
var DriverConnection = class _DriverConnection {
|
|
5829
6140
|
capabilities;
|
|
@@ -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) {
|
|
@@ -6805,12 +7116,12 @@ function syncError(code, label) {
|
|
|
6805
7116
|
|
|
6806
7117
|
// src/realm/sync-fs-dispatch.ts
|
|
6807
7118
|
function toErrno(err) {
|
|
6808
|
-
const
|
|
7119
|
+
const message3 = err instanceof Error ? err.message : String(err);
|
|
6809
7120
|
const code = err?.code;
|
|
6810
7121
|
if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
|
|
6811
|
-
return { ok: false, errno: code, message:
|
|
7122
|
+
return { ok: false, errno: code, message: message3 };
|
|
6812
7123
|
}
|
|
6813
|
-
return { ok: false, errno: "EIO", message:
|
|
7124
|
+
return { ok: false, errno: "EIO", message: message3 };
|
|
6814
7125
|
}
|
|
6815
7126
|
function statJson(s) {
|
|
6816
7127
|
return {
|
|
@@ -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
|
}
|
|
@@ -7291,10 +7602,133 @@ async function hostSyscall(req, ctx) {
|
|
|
7291
7602
|
}
|
|
7292
7603
|
}
|
|
7293
7604
|
|
|
7605
|
+
// src/kernel/net/ws-syscalls.ts
|
|
7606
|
+
var WS_OPS = [
|
|
7607
|
+
"net-ws-open",
|
|
7608
|
+
"net-ws-ready",
|
|
7609
|
+
"net-ws-recv",
|
|
7610
|
+
"net-ws-wait",
|
|
7611
|
+
"net-ws-send",
|
|
7612
|
+
"net-ws-close"
|
|
7613
|
+
];
|
|
7614
|
+
var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
7615
|
+
function message(e) {
|
|
7616
|
+
return e instanceof Error ? e.message : String(e);
|
|
7617
|
+
}
|
|
7618
|
+
function fail(errno, text) {
|
|
7619
|
+
return { ok: false, errno, message: text };
|
|
7620
|
+
}
|
|
7621
|
+
var WsHandles = class {
|
|
7622
|
+
transport;
|
|
7623
|
+
open = /* @__PURE__ */ new Map();
|
|
7624
|
+
next;
|
|
7625
|
+
constructor(transport, next2) {
|
|
7626
|
+
this.transport = transport;
|
|
7627
|
+
this.next = next2;
|
|
7628
|
+
}
|
|
7629
|
+
owns(handle) {
|
|
7630
|
+
return this.open.has(handle);
|
|
7631
|
+
}
|
|
7632
|
+
syscall(req) {
|
|
7633
|
+
if (req.op === "net-ws-open") return Promise.resolve(this.start(req));
|
|
7634
|
+
const entry = this.open.get(req.handle);
|
|
7635
|
+
if (!entry) return Promise.resolve(fail("EBADF", `no websocket ${req.handle}`));
|
|
7636
|
+
if (req.op === "net-ws-ready") return entry.ready;
|
|
7637
|
+
if (!entry.socket) return Promise.resolve(fail("ENOTCONN", "websocket not open"));
|
|
7638
|
+
if (req.op === "net-ws-recv") return this.recv(entry.socket, entry);
|
|
7639
|
+
if (req.op === "net-ws-wait") return this.closed(entry.socket);
|
|
7640
|
+
if (req.op === "net-ws-send") return Promise.resolve(this.send(entry.socket, req, entry));
|
|
7641
|
+
entry.ended = true;
|
|
7642
|
+
entry.socket.close(req.code ?? 1e3, req.reason ?? "");
|
|
7643
|
+
return Promise.resolve({ ok: true, kind: "void" });
|
|
7644
|
+
}
|
|
7645
|
+
start(req) {
|
|
7646
|
+
const handle = this.next();
|
|
7647
|
+
const entry = {
|
|
7648
|
+
abort: new AbortController(),
|
|
7649
|
+
ready: Promise.resolve({ ok: true, kind: "void" })
|
|
7650
|
+
};
|
|
7651
|
+
this.open.set(handle, entry);
|
|
7652
|
+
entry.ready = this.connect(handle, req, entry);
|
|
7653
|
+
return { ok: true, kind: "json", json: handle };
|
|
7654
|
+
}
|
|
7655
|
+
async connect(handle, req, entry) {
|
|
7656
|
+
const { transport } = this;
|
|
7657
|
+
if (!transport.traits.websocket || !transport.websocket) return fail("ENOTSUP", NO_WEBSOCKET2);
|
|
7658
|
+
let socket;
|
|
7659
|
+
try {
|
|
7660
|
+
socket = await transport.websocket({
|
|
7661
|
+
url: req.url,
|
|
7662
|
+
protocols: req.protocols,
|
|
7663
|
+
headers: req.headers,
|
|
7664
|
+
signal: entry.abort.signal
|
|
7665
|
+
});
|
|
7666
|
+
} catch (e) {
|
|
7667
|
+
return fail("ECONNREFUSED", message(e));
|
|
7668
|
+
}
|
|
7669
|
+
if (!this.open.has(handle)) {
|
|
7670
|
+
socket.close(1001, "the program went away");
|
|
7671
|
+
return fail("EBADF", `no websocket ${handle}`);
|
|
7672
|
+
}
|
|
7673
|
+
if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN3.test(socket.protocol))) {
|
|
7674
|
+
socket.close(1002, "subprotocol not offered");
|
|
7675
|
+
return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
|
|
7676
|
+
}
|
|
7677
|
+
entry.socket = socket;
|
|
7678
|
+
void socket.closed.then(() => {
|
|
7679
|
+
entry.ended = true;
|
|
7680
|
+
});
|
|
7681
|
+
entry.messages = socket.messages[Symbol.asyncIterator]();
|
|
7682
|
+
return { ok: true, kind: "json", json: { protocol: socket.protocol } };
|
|
7683
|
+
}
|
|
7684
|
+
async recv(socket, entry) {
|
|
7685
|
+
try {
|
|
7686
|
+
const next2 = await entry.messages.next();
|
|
7687
|
+
if (!next2.done) {
|
|
7688
|
+
return typeof next2.value === "string" ? { ok: true, kind: "json", json: { text: next2.value } } : { ok: true, kind: "bytes", bytes: next2.value };
|
|
7689
|
+
}
|
|
7690
|
+
const closed = await socket.closed;
|
|
7691
|
+
return { ok: true, kind: "json", json: { code: closed.code, reason: closed.reason } };
|
|
7692
|
+
} catch (e) {
|
|
7693
|
+
return fail("ECONNRESET", message(e));
|
|
7694
|
+
}
|
|
7695
|
+
}
|
|
7696
|
+
async closed(socket) {
|
|
7697
|
+
const { code, reason } = await socket.closed;
|
|
7698
|
+
return { ok: true, kind: "json", json: { code, reason } };
|
|
7699
|
+
}
|
|
7700
|
+
send(socket, req, entry) {
|
|
7701
|
+
const data = req.text ?? req.body;
|
|
7702
|
+
if (entry.ended) return fail("EPIPE", "the websocket is closed");
|
|
7703
|
+
try {
|
|
7704
|
+
if (data !== void 0) socket.send(data);
|
|
7705
|
+
} catch (e) {
|
|
7706
|
+
return fail("EPIPE", message(e));
|
|
7707
|
+
}
|
|
7708
|
+
return { ok: true, kind: "json", json: socket.buffered };
|
|
7709
|
+
}
|
|
7710
|
+
close(handle) {
|
|
7711
|
+
const entry = this.open.get(handle);
|
|
7712
|
+
if (!entry) return;
|
|
7713
|
+
this.open.delete(handle);
|
|
7714
|
+
entry.abort.abort();
|
|
7715
|
+
entry.socket?.close(1001, "the program went away");
|
|
7716
|
+
}
|
|
7717
|
+
closeAll() {
|
|
7718
|
+
for (const handle of [...this.open.keys()]) this.close(handle);
|
|
7719
|
+
}
|
|
7720
|
+
};
|
|
7721
|
+
|
|
7294
7722
|
// src/kernel/net/http-syscalls.ts
|
|
7295
|
-
var HTTP_OPS = [
|
|
7723
|
+
var HTTP_OPS = [
|
|
7724
|
+
"net-request",
|
|
7725
|
+
"net-read",
|
|
7726
|
+
"net-close",
|
|
7727
|
+
"net-traits",
|
|
7728
|
+
...WS_OPS
|
|
7729
|
+
];
|
|
7296
7730
|
var EMPTY = new Uint8Array(0);
|
|
7297
|
-
function
|
|
7731
|
+
function message2(e) {
|
|
7298
7732
|
return e instanceof Error ? e.message : String(e);
|
|
7299
7733
|
}
|
|
7300
7734
|
async function* once(bytes) {
|
|
@@ -7304,10 +7738,13 @@ var HttpHandles = class {
|
|
|
7304
7738
|
transport;
|
|
7305
7739
|
open = /* @__PURE__ */ new Map();
|
|
7306
7740
|
nextHandle = 0;
|
|
7741
|
+
ws;
|
|
7307
7742
|
constructor(transport) {
|
|
7308
7743
|
this.transport = transport;
|
|
7744
|
+
this.ws = new WsHandles(transport, () => ++this.nextHandle);
|
|
7309
7745
|
}
|
|
7310
7746
|
syscall(req) {
|
|
7747
|
+
if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
|
|
7311
7748
|
if (req.op === "net-request") return this.request(req);
|
|
7312
7749
|
if (req.op === "net-read") return this.read(req.handle, req.max);
|
|
7313
7750
|
if (req.op === "net-traits") return Promise.resolve(this.traits());
|
|
@@ -7350,7 +7787,7 @@ var HttpHandles = class {
|
|
|
7350
7787
|
};
|
|
7351
7788
|
return { ok: true, kind: "json", json: head };
|
|
7352
7789
|
} catch (e) {
|
|
7353
|
-
const text =
|
|
7790
|
+
const text = message2(e);
|
|
7354
7791
|
if (text === NO_TRANSPORT) return { ok: false, errno: "ENETUNREACH", message: text };
|
|
7355
7792
|
const status = e.status;
|
|
7356
7793
|
if (typeof status !== "number" || status === 502) {
|
|
@@ -7384,7 +7821,7 @@ var HttpHandles = class {
|
|
|
7384
7821
|
} catch (e) {
|
|
7385
7822
|
entry.done = true;
|
|
7386
7823
|
const timedOut = e?.code === "ETIMEDOUT";
|
|
7387
|
-
return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message:
|
|
7824
|
+
return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message: message2(e) };
|
|
7388
7825
|
}
|
|
7389
7826
|
}
|
|
7390
7827
|
const bytes = entry.left.subarray(0, Math.max(0, max));
|
|
@@ -7392,6 +7829,10 @@ var HttpHandles = class {
|
|
|
7392
7829
|
return { ok: true, kind: "bytes", bytes };
|
|
7393
7830
|
}
|
|
7394
7831
|
async close(handle) {
|
|
7832
|
+
if (this.ws.owns(handle)) {
|
|
7833
|
+
this.ws.close(handle);
|
|
7834
|
+
return { ok: true, kind: "void" };
|
|
7835
|
+
}
|
|
7395
7836
|
const entry = this.open.get(handle);
|
|
7396
7837
|
if (!entry) return { ok: false, errno: "EBADF", message: `no request ${handle}` };
|
|
7397
7838
|
this.open.delete(handle);
|
|
@@ -7400,6 +7841,7 @@ var HttpHandles = class {
|
|
|
7400
7841
|
return { ok: true, kind: "void" };
|
|
7401
7842
|
}
|
|
7402
7843
|
async closeAll() {
|
|
7844
|
+
this.ws.closeAll();
|
|
7403
7845
|
await Promise.all([...this.open.keys()].map((handle) => this.close(handle)));
|
|
7404
7846
|
}
|
|
7405
7847
|
};
|
|
@@ -7432,7 +7874,7 @@ var Resolver = class {
|
|
|
7432
7874
|
this.routes = options.routes;
|
|
7433
7875
|
this.now = options.now ?? Date.now;
|
|
7434
7876
|
this.timeoutMs = options.timeoutMs ?? RESOLVE_TIMEOUT_MS;
|
|
7435
|
-
this.log = options.log ?? ((
|
|
7877
|
+
this.log = options.log ?? ((message3) => console.warn(message3));
|
|
7436
7878
|
this.own = ownNames(options.hostname ?? DEFAULT_HOSTNAME);
|
|
7437
7879
|
}
|
|
7438
7880
|
async resolve(name2, family, blocking) {
|
|
@@ -7449,21 +7891,21 @@ var Resolver = class {
|
|
|
7449
7891
|
if (!this.uplink || key === "") return [];
|
|
7450
7892
|
const asked = this.uplink.traits.ipv6 ? family : 4;
|
|
7451
7893
|
if (family === 6 && asked !== 6) return [];
|
|
7452
|
-
const
|
|
7453
|
-
const hit = this.cache.get(
|
|
7894
|
+
const id2 = `${asked}:${key}`;
|
|
7895
|
+
const hit = this.cache.get(id2);
|
|
7454
7896
|
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
7455
7897
|
const signal = blocking?.();
|
|
7456
7898
|
if (signal?.aborted) throw new KernelError("EINTR");
|
|
7457
|
-
let lookup = this.pending.get(
|
|
7899
|
+
let lookup = this.pending.get(id2);
|
|
7458
7900
|
if (!lookup) {
|
|
7459
7901
|
const abort = new AbortController();
|
|
7460
|
-
lookup = { work: this.lookup(key, asked,
|
|
7461
|
-
this.pending.set(
|
|
7462
|
-
void lookup.work.then(() => this.pending.delete(
|
|
7902
|
+
lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
|
|
7903
|
+
this.pending.set(id2, lookup);
|
|
7904
|
+
void lookup.work.then(() => this.pending.delete(id2));
|
|
7463
7905
|
}
|
|
7464
7906
|
return waitFor(lookup, signal);
|
|
7465
7907
|
}
|
|
7466
|
-
async lookup(name2, family,
|
|
7908
|
+
async lookup(name2, family, id2, abandoned) {
|
|
7467
7909
|
const uplink = this.uplink;
|
|
7468
7910
|
const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
|
|
7469
7911
|
let answer;
|
|
@@ -7480,7 +7922,7 @@ var Resolver = class {
|
|
|
7480
7922
|
return [];
|
|
7481
7923
|
}
|
|
7482
7924
|
const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
|
|
7483
|
-
if (addresses.length > 0) this.remember(
|
|
7925
|
+
if (addresses.length > 0) this.remember(id2, addresses, answer.ttl);
|
|
7484
7926
|
return addresses;
|
|
7485
7927
|
}
|
|
7486
7928
|
usable(name2, address, family) {
|
|
@@ -7492,11 +7934,11 @@ var Resolver = class {
|
|
|
7492
7934
|
this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
|
|
7493
7935
|
return false;
|
|
7494
7936
|
}
|
|
7495
|
-
remember(
|
|
7937
|
+
remember(id2, addresses, ttl) {
|
|
7496
7938
|
const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
|
|
7497
7939
|
if (seconds <= 0) return;
|
|
7498
|
-
this.cache.delete(
|
|
7499
|
-
this.cache.set(
|
|
7940
|
+
this.cache.delete(id2);
|
|
7941
|
+
this.cache.set(id2, { addresses, expires: this.now() + seconds * 1e3 });
|
|
7500
7942
|
if (this.cache.size > RESOLVE_CACHE_MAX) {
|
|
7501
7943
|
this.cache.delete(this.cache.keys().next().value);
|
|
7502
7944
|
}
|
|
@@ -7992,10 +8434,10 @@ function ready(fds, read, write) {
|
|
|
7992
8434
|
}
|
|
7993
8435
|
function interruption(signal) {
|
|
7994
8436
|
const { promise, reject } = Promise.withResolvers();
|
|
7995
|
-
const
|
|
7996
|
-
if (signal.aborted)
|
|
7997
|
-
else signal.addEventListener("abort",
|
|
7998
|
-
return { promise, done: () => signal.removeEventListener("abort",
|
|
8437
|
+
const fail2 = () => reject(new KernelError("EINTR"));
|
|
8438
|
+
if (signal.aborted) fail2();
|
|
8439
|
+
else signal.addEventListener("abort", fail2, { once: true });
|
|
8440
|
+
return { promise, done: () => signal.removeEventListener("abort", fail2) };
|
|
7999
8441
|
}
|
|
8000
8442
|
async function selectFds(fds, read, write, timeoutMs, interrupt) {
|
|
8001
8443
|
const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
|
|
@@ -8938,6 +9380,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
8938
9380
|
"fd-list",
|
|
8939
9381
|
"proc-alarm",
|
|
8940
9382
|
"proc-umask",
|
|
9383
|
+
"proc-cred",
|
|
9384
|
+
"proc-setcred",
|
|
8941
9385
|
"dl-log",
|
|
8942
9386
|
"fd-renumber",
|
|
8943
9387
|
"fd-promote",
|
|
@@ -8988,6 +9432,7 @@ var WasmProcess = class {
|
|
|
8988
9432
|
interrupt = new AbortController();
|
|
8989
9433
|
dying = false;
|
|
8990
9434
|
umask = DEFAULT_UMASK;
|
|
9435
|
+
cred;
|
|
8991
9436
|
execChild;
|
|
8992
9437
|
killed = false;
|
|
8993
9438
|
execTermsig;
|
|
@@ -9007,6 +9452,7 @@ var WasmProcess = class {
|
|
|
9007
9452
|
this.options = options;
|
|
9008
9453
|
this.inherited = options.ignored ?? 0;
|
|
9009
9454
|
this.umask = options.umask ?? DEFAULT_UMASK;
|
|
9455
|
+
this.cred = copyCred(options.cred ?? ROOT);
|
|
9010
9456
|
this.children = new ChildTable(fds, options.spawner, options.forker);
|
|
9011
9457
|
this.nodes = options.nodes ?? new VfsNodes(options.fs);
|
|
9012
9458
|
if (!options.nodes) options.openFiles?.add(this.nodes);
|
|
@@ -9523,6 +9969,11 @@ var WasmProcess = class {
|
|
|
9523
9969
|
return { ok: true, kind: "void" };
|
|
9524
9970
|
case "proc-umask":
|
|
9525
9971
|
return { ok: true, kind: "json", json: this.setUmask(req.mask) };
|
|
9972
|
+
case "proc-cred":
|
|
9973
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9974
|
+
case "proc-setcred":
|
|
9975
|
+
this.cred = changeCred(this.cred, req.change);
|
|
9976
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9526
9977
|
case "dl-log":
|
|
9527
9978
|
if (req.append) this.dlLog.push(req.append);
|
|
9528
9979
|
return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
|
|
@@ -9617,9 +10068,9 @@ var KILL_GRACE_MS = 2e3;
|
|
|
9617
10068
|
var descIds = /* @__PURE__ */ new WeakMap();
|
|
9618
10069
|
var nextDescId = 1;
|
|
9619
10070
|
function descId(file) {
|
|
9620
|
-
const
|
|
9621
|
-
descIds.set(file,
|
|
9622
|
-
return
|
|
10071
|
+
const id2 = descIds.get(file) ?? nextDescId++;
|
|
10072
|
+
descIds.set(file, id2);
|
|
10073
|
+
return id2;
|
|
9623
10074
|
}
|
|
9624
10075
|
function inheritedFds(fds) {
|
|
9625
10076
|
return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
|
|
@@ -9736,7 +10187,8 @@ function processInit(opts, sab, umask, lane) {
|
|
|
9736
10187
|
function inherited(opts) {
|
|
9737
10188
|
return {
|
|
9738
10189
|
...opts.ignored ? { ignored: opts.ignored } : {},
|
|
9739
|
-
...opts.umask !== void 0 ? { umask: opts.umask } : {}
|
|
10190
|
+
...opts.umask !== void 0 ? { umask: opts.umask } : {},
|
|
10191
|
+
...opts.cred ? { cred: opts.cred } : {}
|
|
9740
10192
|
};
|
|
9741
10193
|
}
|
|
9742
10194
|
function holdingToken(opts, holds) {
|
|
@@ -9900,6 +10352,7 @@ function spawnWasmProcess(opts) {
|
|
|
9900
10352
|
ignoredSignals: (fork) => process.inheritable(fork),
|
|
9901
10353
|
signalMasks: () => process.signalMasks(),
|
|
9902
10354
|
umask: () => process.umask,
|
|
10355
|
+
cred: () => copyCred(process.cred),
|
|
9903
10356
|
writesBack: () => opts.program.abi === "wasi" && !done4
|
|
9904
10357
|
};
|
|
9905
10358
|
}
|
|
@@ -9958,6 +10411,9 @@ var JobTable = class {
|
|
|
9958
10411
|
pgidOf(pid) {
|
|
9959
10412
|
return this.members.get(pid)?.pgid;
|
|
9960
10413
|
}
|
|
10414
|
+
sidOf(pid) {
|
|
10415
|
+
return this.members.get(pid)?.sid;
|
|
10416
|
+
}
|
|
9961
10417
|
member(pid) {
|
|
9962
10418
|
const member = this.members.get(pid);
|
|
9963
10419
|
if (!member) throw new KernelError("ESRCH");
|
|
@@ -9966,27 +10422,27 @@ var JobTable = class {
|
|
|
9966
10422
|
idOf(pid) {
|
|
9967
10423
|
return this.members.get(pid)?.shownAs ?? pid;
|
|
9968
10424
|
}
|
|
9969
|
-
byId(
|
|
9970
|
-
for (const member of this.members.values()) if (member.shownAs ===
|
|
9971
|
-
return this.member(
|
|
10425
|
+
byId(id2) {
|
|
10426
|
+
for (const member of this.members.values()) if (member.shownAs === id2) return member;
|
|
10427
|
+
return this.member(id2);
|
|
9972
10428
|
}
|
|
9973
10429
|
setpgid(caller, pid, pgid) {
|
|
9974
10430
|
const me = this.idOf(caller);
|
|
9975
10431
|
const target = this.byId(pid || me);
|
|
9976
|
-
const
|
|
9977
|
-
const group = pgid ||
|
|
10432
|
+
const id2 = this.idOf(target.pid);
|
|
10433
|
+
const group = pgid || id2;
|
|
9978
10434
|
if (group < 0) throw new KernelError("EINVAL");
|
|
9979
10435
|
const self = this.member(caller);
|
|
9980
|
-
if (
|
|
10436
|
+
if (id2 !== me) {
|
|
9981
10437
|
if (target.ppid === void 0 || this.idOf(target.ppid) !== me) {
|
|
9982
10438
|
throw new KernelError("ESRCH");
|
|
9983
10439
|
}
|
|
9984
10440
|
if (target.execed) throw new KernelError("EACCES");
|
|
9985
10441
|
}
|
|
9986
10442
|
if (target.sid !== self.sid) throw new KernelError("EPERM");
|
|
9987
|
-
if (
|
|
10443
|
+
if (id2 === target.sid) throw new KernelError("EPERM");
|
|
9988
10444
|
const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
|
|
9989
|
-
if (group !==
|
|
10445
|
+
if (group !== id2 && !exists) throw new KernelError("EPERM");
|
|
9990
10446
|
target.pgid = group;
|
|
9991
10447
|
}
|
|
9992
10448
|
getpgid(caller, pid) {
|
|
@@ -9997,12 +10453,12 @@ var JobTable = class {
|
|
|
9997
10453
|
}
|
|
9998
10454
|
setsid(pid) {
|
|
9999
10455
|
const member = this.member(pid);
|
|
10000
|
-
const
|
|
10001
|
-
if (member.pgid ===
|
|
10002
|
-
member.sid =
|
|
10003
|
-
member.pgid =
|
|
10004
|
-
this.terminals.delete(
|
|
10005
|
-
return
|
|
10456
|
+
const id2 = this.idOf(pid);
|
|
10457
|
+
if (member.pgid === id2) throw new KernelError("EPERM");
|
|
10458
|
+
member.sid = id2;
|
|
10459
|
+
member.pgid = id2;
|
|
10460
|
+
this.terminals.delete(id2);
|
|
10461
|
+
return id2;
|
|
10006
10462
|
}
|
|
10007
10463
|
terminalNamed(name2) {
|
|
10008
10464
|
return this.knownTerminals().find((tty) => tty.name === name2);
|
|
@@ -10019,15 +10475,17 @@ var JobTable = class {
|
|
|
10019
10475
|
}
|
|
10020
10476
|
acquireTerminal(pid, tty) {
|
|
10021
10477
|
const member = this.members.get(pid);
|
|
10022
|
-
const
|
|
10023
|
-
if (!member || member.sid !==
|
|
10478
|
+
const id2 = this.idOf(pid);
|
|
10479
|
+
if (!member || member.sid !== id2 || this.terminals.has(id2)) return false;
|
|
10024
10480
|
if ([...this.terminals.values()].includes(tty)) return false;
|
|
10025
|
-
this.terminals.set(
|
|
10481
|
+
this.terminals.set(id2, tty);
|
|
10026
10482
|
this.foreground.set(tty, member.pgid);
|
|
10027
10483
|
return true;
|
|
10028
10484
|
}
|
|
10029
|
-
killGroup(pgid, sig) {
|
|
10030
|
-
const
|
|
10485
|
+
killGroup(pgid, sig, may = () => true) {
|
|
10486
|
+
const group = [...this.members.values()].filter((m) => m.pgid === pgid);
|
|
10487
|
+
const targets = group.filter((m) => may(m.pid));
|
|
10488
|
+
if (targets.length === 0 && group.length > 0) throw new KernelError("EPERM");
|
|
10031
10489
|
const pids = new Set(targets.map((m) => m.pid));
|
|
10032
10490
|
if (sig !== 0) {
|
|
10033
10491
|
for (const m of targets)
|
|
@@ -10128,8 +10586,8 @@ var SettlingChildren = class {
|
|
|
10128
10586
|
};
|
|
10129
10587
|
|
|
10130
10588
|
// src/mount/driver.ts
|
|
10131
|
-
function fsError2(code,
|
|
10132
|
-
return Object.assign(new Error(
|
|
10589
|
+
function fsError2(code, message3 = code) {
|
|
10590
|
+
return Object.assign(new Error(message3), { code });
|
|
10133
10591
|
}
|
|
10134
10592
|
async function dispatch(h, req) {
|
|
10135
10593
|
switch (req.op) {
|
|
@@ -10179,15 +10637,15 @@ async function dispatch(h, req) {
|
|
|
10179
10637
|
}
|
|
10180
10638
|
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
10181
10639
|
let open = false;
|
|
10182
|
-
const reply = (
|
|
10183
|
-
async function hello(
|
|
10184
|
-
const refused = driverVersionError(
|
|
10640
|
+
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
10641
|
+
async function hello(message3) {
|
|
10642
|
+
const refused = driverVersionError(message3.protocol);
|
|
10185
10643
|
if (refused) {
|
|
10186
10644
|
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: "EPROTO" } });
|
|
10187
10645
|
return;
|
|
10188
10646
|
}
|
|
10189
10647
|
try {
|
|
10190
|
-
const refined = await handlers.mount?.(
|
|
10648
|
+
const refined = await handlers.mount?.(message3.mount);
|
|
10191
10649
|
open = true;
|
|
10192
10650
|
reply({
|
|
10193
10651
|
hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } }
|
|
@@ -10489,7 +10947,7 @@ function unescape(field2) {
|
|
|
10489
10947
|
);
|
|
10490
10948
|
}
|
|
10491
10949
|
function parseFstab(text) {
|
|
10492
|
-
const
|
|
10950
|
+
const lines2 = [];
|
|
10493
10951
|
text.split("\n").forEach((raw, index) => {
|
|
10494
10952
|
const content = raw.replace(/#.*$/, "").trim();
|
|
10495
10953
|
if (!content) return;
|
|
@@ -10498,12 +10956,12 @@ function parseFstab(text) {
|
|
|
10498
10956
|
if (options.split(",").includes("noauto")) return;
|
|
10499
10957
|
const spec = { source, target, type, options: {} };
|
|
10500
10958
|
try {
|
|
10501
|
-
|
|
10959
|
+
lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
10502
10960
|
} catch (err) {
|
|
10503
|
-
|
|
10961
|
+
lines2.push({ line: index + 1, spec, invalid: err });
|
|
10504
10962
|
}
|
|
10505
10963
|
});
|
|
10506
|
-
return
|
|
10964
|
+
return lines2;
|
|
10507
10965
|
}
|
|
10508
10966
|
function codeOf3(err) {
|
|
10509
10967
|
const code = err?.code;
|
|
@@ -10521,11 +10979,11 @@ function wait(ms, signal) {
|
|
|
10521
10979
|
signal.addEventListener("abort", done4, { once: true });
|
|
10522
10980
|
});
|
|
10523
10981
|
}
|
|
10524
|
-
async function mountFstab(
|
|
10982
|
+
async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
10525
10983
|
}, settled = () => {
|
|
10526
10984
|
}) {
|
|
10527
10985
|
return Promise.all(
|
|
10528
|
-
|
|
10986
|
+
lines2.map(async (each) => {
|
|
10529
10987
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
10530
10988
|
settled(each, result);
|
|
10531
10989
|
return result;
|
|
@@ -11172,10 +11630,10 @@ function importedMemory(bytes) {
|
|
|
11172
11630
|
return { initial, maximum, shared: (flags & 2) !== 0 };
|
|
11173
11631
|
};
|
|
11174
11632
|
while (at < bytes.length) {
|
|
11175
|
-
const
|
|
11633
|
+
const id2 = bytes[at++];
|
|
11176
11634
|
const size = u32();
|
|
11177
11635
|
const end = at + size;
|
|
11178
|
-
if (
|
|
11636
|
+
if (id2 !== 2) {
|
|
11179
11637
|
at = end;
|
|
11180
11638
|
continue;
|
|
11181
11639
|
}
|
|
@@ -11295,18 +11753,17 @@ function foreignImports(bytes) {
|
|
|
11295
11753
|
const r = new Reader(bytes);
|
|
11296
11754
|
let types = [];
|
|
11297
11755
|
while (r.at < bytes.length) {
|
|
11298
|
-
const
|
|
11756
|
+
const id2 = r.byte();
|
|
11299
11757
|
const end = r.u32() + r.at;
|
|
11300
|
-
if (
|
|
11758
|
+
if (id2 === 1) types = readTypes(r);
|
|
11301
11759
|
if (!types) return void 0;
|
|
11302
|
-
if (
|
|
11760
|
+
if (id2 === 2) return readImports(r, types);
|
|
11303
11761
|
r.at = end;
|
|
11304
11762
|
}
|
|
11305
11763
|
return {};
|
|
11306
11764
|
}
|
|
11307
11765
|
|
|
11308
11766
|
// src/launcher.ts
|
|
11309
|
-
var PNPM_HOME = "/home/.local/share/pnpm";
|
|
11310
11767
|
var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
11311
11768
|
var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
11312
11769
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -11406,6 +11863,8 @@ var Launcher = class {
|
|
|
11406
11863
|
zombies = /* @__PURE__ */ new Set();
|
|
11407
11864
|
orphans = /* @__PURE__ */ new Set();
|
|
11408
11865
|
described = /* @__PURE__ */ new Map();
|
|
11866
|
+
creds = /* @__PURE__ */ new Map();
|
|
11867
|
+
users;
|
|
11409
11868
|
jobs = new JobTable();
|
|
11410
11869
|
locks = new LockTable();
|
|
11411
11870
|
settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
|
|
@@ -11457,7 +11916,7 @@ var Launcher = class {
|
|
|
11457
11916
|
this.hostfs = options;
|
|
11458
11917
|
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
11459
11918
|
this.processMounts = options.processMounts ?? true;
|
|
11460
|
-
this.pnpmHome = options.env?.PNPM_HOME ??
|
|
11919
|
+
this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
|
|
11461
11920
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
11462
11921
|
this.cdp = new CdpHosts(options.cdp);
|
|
11463
11922
|
const hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
@@ -11493,6 +11952,7 @@ var Launcher = class {
|
|
|
11493
11952
|
}
|
|
11494
11953
|
);
|
|
11495
11954
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
11955
|
+
this.users = new UserDb(this.fs);
|
|
11496
11956
|
void this.unlinked.sweep("/");
|
|
11497
11957
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
11498
11958
|
this.catalog = void 0;
|
|
@@ -11508,10 +11968,10 @@ var Launcher = class {
|
|
|
11508
11968
|
}
|
|
11509
11969
|
interpreters() {
|
|
11510
11970
|
this.binfmts ??= this.roots().then(
|
|
11511
|
-
(roots) => scanBinfmts(this.base, roots, (
|
|
11512
|
-
if (this.warned.has(
|
|
11513
|
-
this.warned.add(
|
|
11514
|
-
console.warn(
|
|
11971
|
+
(roots) => scanBinfmts(this.base, roots, (message3) => {
|
|
11972
|
+
if (this.warned.has(message3)) return;
|
|
11973
|
+
this.warned.add(message3);
|
|
11974
|
+
console.warn(message3);
|
|
11515
11975
|
})
|
|
11516
11976
|
);
|
|
11517
11977
|
return this.binfmts;
|
|
@@ -11693,6 +12153,7 @@ var Launcher = class {
|
|
|
11693
12153
|
},
|
|
11694
12154
|
...req.ignored ? { ignored: req.ignored } : {},
|
|
11695
12155
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
12156
|
+
cred: req.cred,
|
|
11696
12157
|
program: req.program,
|
|
11697
12158
|
argv0: req.argv0,
|
|
11698
12159
|
args: req.args,
|
|
@@ -11704,9 +12165,9 @@ var Launcher = class {
|
|
|
11704
12165
|
onError: req.report,
|
|
11705
12166
|
spawner: this.spawner(pid, req.report),
|
|
11706
12167
|
forker: this.forker(pid, req),
|
|
11707
|
-
kill: (target, sig) => this.kill(target, sig),
|
|
12168
|
+
kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
|
|
11708
12169
|
writesBack: (target) => this.processes.get(target)?.writesBack() === true,
|
|
11709
|
-
processes: () => ({ boot: this.boot, processes: this.list() }),
|
|
12170
|
+
processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
|
|
11710
12171
|
openFiles: this.openFiles,
|
|
11711
12172
|
nodes: this.nodes,
|
|
11712
12173
|
held: this.held,
|
|
@@ -11726,6 +12187,8 @@ var Launcher = class {
|
|
|
11726
12187
|
...this.shownIds(req)
|
|
11727
12188
|
});
|
|
11728
12189
|
this.processes.set(pid, handle);
|
|
12190
|
+
const { cred } = req;
|
|
12191
|
+
this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
|
|
11729
12192
|
if (req.ppid !== void 0) this.settling.started(pid, req.ppid);
|
|
11730
12193
|
this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
|
|
11731
12194
|
if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
|
|
@@ -11752,12 +12215,13 @@ var Launcher = class {
|
|
|
11752
12215
|
forget(pid) {
|
|
11753
12216
|
this.jobs.remove(pid);
|
|
11754
12217
|
this.described.delete(pid);
|
|
12218
|
+
this.creds.delete(pid);
|
|
11755
12219
|
}
|
|
11756
12220
|
reaped(pid) {
|
|
11757
12221
|
if (this.zombies.delete(pid)) this.forget(pid);
|
|
11758
12222
|
else this.orphans.add(pid);
|
|
11759
12223
|
}
|
|
11760
|
-
list() {
|
|
12224
|
+
list(viewer = ROOT) {
|
|
11761
12225
|
const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
|
|
11762
12226
|
const rootOf = (member) => {
|
|
11763
12227
|
let root = member;
|
|
@@ -11775,6 +12239,8 @@ var Launcher = class {
|
|
|
11775
12239
|
for (const member of members.values()) {
|
|
11776
12240
|
const described = this.described.get(member.pid);
|
|
11777
12241
|
if (member.execed || !described) continue;
|
|
12242
|
+
const cred = this.credOf(member.pid);
|
|
12243
|
+
if (!maySee(viewer, cred)) continue;
|
|
11778
12244
|
const root = rootOf(member);
|
|
11779
12245
|
listed.push({
|
|
11780
12246
|
pid: root.pid,
|
|
@@ -11787,6 +12253,7 @@ var Launcher = class {
|
|
|
11787
12253
|
state: this.processes.has(member.pid) ? "S" : "Z",
|
|
11788
12254
|
memory: this.processes.get(member.pid)?.memory() ?? 0,
|
|
11789
12255
|
umask: this.umaskOf(member.pid),
|
|
12256
|
+
cred,
|
|
11790
12257
|
...this.signalMasksOf(member.pid)
|
|
11791
12258
|
});
|
|
11792
12259
|
}
|
|
@@ -11810,6 +12277,7 @@ var Launcher = class {
|
|
|
11810
12277
|
ppid,
|
|
11811
12278
|
ignored: this.ignoredBy(ppid),
|
|
11812
12279
|
umask: this.umaskOf(ppid),
|
|
12280
|
+
cred: this.credOf(ppid),
|
|
11813
12281
|
...exec
|
|
11814
12282
|
});
|
|
11815
12283
|
deciding?.attach();
|
|
@@ -11855,7 +12323,8 @@ var Launcher = class {
|
|
|
11855
12323
|
ppid,
|
|
11856
12324
|
fork: state,
|
|
11857
12325
|
ignored: this.ignoredBy(ppid, true),
|
|
11858
|
-
umask: this.umaskOf(ppid)
|
|
12326
|
+
umask: this.umaskOf(ppid),
|
|
12327
|
+
cred: this.credOf(ppid)
|
|
11859
12328
|
})
|
|
11860
12329
|
);
|
|
11861
12330
|
}
|
|
@@ -11867,6 +12336,18 @@ var Launcher = class {
|
|
|
11867
12336
|
umaskOf(pid) {
|
|
11868
12337
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
11869
12338
|
}
|
|
12339
|
+
imageOf(pid) {
|
|
12340
|
+
const members = this.jobs.list();
|
|
12341
|
+
let image = pid;
|
|
12342
|
+
for (; ; ) {
|
|
12343
|
+
const next2 = members.find((m) => m.execParent === image);
|
|
12344
|
+
if (!next2) return image;
|
|
12345
|
+
image = next2.pid;
|
|
12346
|
+
}
|
|
12347
|
+
}
|
|
12348
|
+
credOf(pid) {
|
|
12349
|
+
return this.creds.get(pid)?.() ?? copyCred(ROOT);
|
|
12350
|
+
}
|
|
11870
12351
|
released(path) {
|
|
11871
12352
|
const at = normalizePath(path);
|
|
11872
12353
|
this.unlinked.closed(at);
|
|
@@ -11880,10 +12361,13 @@ var Launcher = class {
|
|
|
11880
12361
|
ignoredBy(pid, fork = false) {
|
|
11881
12362
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
11882
12363
|
}
|
|
11883
|
-
kill(pid, sig) {
|
|
11884
|
-
|
|
12364
|
+
kill(pid, sig, sender, from) {
|
|
12365
|
+
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
12366
|
+
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);
|
|
12367
|
+
if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
|
|
11885
12368
|
const handle = this.processes.get(pid);
|
|
11886
12369
|
if (!handle) return false;
|
|
12370
|
+
if (!may(this.imageOf(pid))) throw fsError("EPERM", `pid ${pid}`);
|
|
11887
12371
|
if (sig !== 0) handle.signal(sig);
|
|
11888
12372
|
return true;
|
|
11889
12373
|
}
|
|
@@ -11954,9 +12438,9 @@ var Launcher = class {
|
|
|
11954
12438
|
ask(handle);
|
|
11955
12439
|
});
|
|
11956
12440
|
const served = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
|
|
11957
|
-
const
|
|
11958
|
-
this.removable.set(target, { id, medium, served });
|
|
11959
|
-
const stored = this.inserted.get(
|
|
12441
|
+
const id2 = source.slice("fsa:".length);
|
|
12442
|
+
this.removable.set(target, { id: id2, medium, served });
|
|
12443
|
+
const stored = this.inserted.get(id2) ?? await this.media.get(id2);
|
|
11960
12444
|
if (stored && await granted(stored)) medium.insert(stored);
|
|
11961
12445
|
else ask(stored);
|
|
11962
12446
|
return {
|
|
@@ -12040,15 +12524,16 @@ var Launcher = class {
|
|
|
12040
12524
|
}
|
|
12041
12525
|
async prepare() {
|
|
12042
12526
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
12527
|
+
await this.users.ensure();
|
|
12043
12528
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
12044
12529
|
this.fstab = this.mountFstab();
|
|
12045
12530
|
}
|
|
12046
12531
|
async mountFstab() {
|
|
12047
12532
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
12048
|
-
const
|
|
12049
|
-
for (const { spec } of
|
|
12533
|
+
const lines2 = parseFstab(text);
|
|
12534
|
+
for (const { spec } of lines2) this.mounts.note(spec, "pending");
|
|
12050
12535
|
return mountFstab(
|
|
12051
|
-
|
|
12536
|
+
lines2,
|
|
12052
12537
|
(spec) => this.mount(spec).catch((err) => {
|
|
12053
12538
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
12054
12539
|
throw err;
|
|
@@ -12063,14 +12548,19 @@ var Launcher = class {
|
|
|
12063
12548
|
}
|
|
12064
12549
|
);
|
|
12065
12550
|
}
|
|
12066
|
-
environment(cwd, extra) {
|
|
12551
|
+
environment(cwd, extra, account) {
|
|
12552
|
+
return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
|
|
12553
|
+
}
|
|
12554
|
+
async account(user) {
|
|
12555
|
+
if (typeof user === "object") return user;
|
|
12556
|
+
const found = await this.users.lookup(user ?? 0);
|
|
12557
|
+
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
12558
|
+
return found;
|
|
12559
|
+
}
|
|
12560
|
+
identity(account) {
|
|
12067
12561
|
return {
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
PNPM_HOME: this.pnpmHome,
|
|
12071
|
-
...this.env,
|
|
12072
|
-
PWD: cwd,
|
|
12073
|
-
...extra
|
|
12562
|
+
cred: credOf(account),
|
|
12563
|
+
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
12074
12564
|
};
|
|
12075
12565
|
}
|
|
12076
12566
|
async starting(argv, dir) {
|
|
@@ -12083,6 +12573,7 @@ var Launcher = class {
|
|
|
12083
12573
|
return { cwd, file, planned: await this.plan(file, argv, cwd) };
|
|
12084
12574
|
}
|
|
12085
12575
|
async openTerminal(argv, options) {
|
|
12576
|
+
const account = await this.account(options.user);
|
|
12086
12577
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
12087
12578
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
12088
12579
|
let leader = 0;
|
|
@@ -12098,14 +12589,18 @@ var Launcher = class {
|
|
|
12098
12589
|
fds.installAt(0, stdio);
|
|
12099
12590
|
fds.installAt(1, stdio.retain());
|
|
12100
12591
|
fds.installAt(2, stdio.retain());
|
|
12101
|
-
const env = this.environment(
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12592
|
+
const env = this.environment(
|
|
12593
|
+
cwd,
|
|
12594
|
+
{
|
|
12595
|
+
TERM: "xterm-256color",
|
|
12596
|
+
COLORTERM: "truecolor",
|
|
12597
|
+
...options.env
|
|
12598
|
+
},
|
|
12599
|
+
account
|
|
12600
|
+
);
|
|
12601
|
+
const report = (message3) => options.onData(encoder3.encode(`${message3}\r
|
|
12107
12602
|
`));
|
|
12108
|
-
const handle = await this.launch(planned, { env, cwd, fds, report });
|
|
12603
|
+
const handle = await this.launch(planned, { env, cwd, fds, report, ...this.identity(account) });
|
|
12109
12604
|
leader = handle.pid;
|
|
12110
12605
|
void handle.exited.then(() => tty.hangup());
|
|
12111
12606
|
return {
|
|
@@ -12121,8 +12616,9 @@ var Launcher = class {
|
|
|
12121
12616
|
};
|
|
12122
12617
|
}
|
|
12123
12618
|
async run(argv, options = {}) {
|
|
12619
|
+
const account = await this.account(options.user);
|
|
12124
12620
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
12125
|
-
const env = this.environment(cwd, options.env);
|
|
12621
|
+
const env = this.environment(cwd, options.env, account);
|
|
12126
12622
|
const out = [];
|
|
12127
12623
|
const err = [];
|
|
12128
12624
|
const keep = options.collect !== false;
|
|
@@ -12132,7 +12628,7 @@ var Launcher = class {
|
|
|
12132
12628
|
};
|
|
12133
12629
|
const stdout = collect(out, options.onStdout);
|
|
12134
12630
|
const stderr = collect(err, options.onStderr);
|
|
12135
|
-
const report = (
|
|
12631
|
+
const report = (message3) => stderr(encoder3.encode(`${message3}
|
|
12136
12632
|
`));
|
|
12137
12633
|
if (!planned) {
|
|
12138
12634
|
report(`${file}: command not found`);
|
|
@@ -12143,7 +12639,14 @@ var Launcher = class {
|
|
|
12143
12639
|
fds.installAt(1, sinkFile(stdout));
|
|
12144
12640
|
fds.installAt(2, sinkFile(stderr));
|
|
12145
12641
|
const group = options.pgid !== void 0 ? { pgid: options.pgid } : {};
|
|
12146
|
-
const handle = await this.launch(planned, {
|
|
12642
|
+
const handle = await this.launch(planned, {
|
|
12643
|
+
env,
|
|
12644
|
+
cwd,
|
|
12645
|
+
fds,
|
|
12646
|
+
report,
|
|
12647
|
+
...group,
|
|
12648
|
+
...this.identity(account)
|
|
12649
|
+
});
|
|
12147
12650
|
options.onStarted?.(handle.pid);
|
|
12148
12651
|
const status = await handle.exited;
|
|
12149
12652
|
return { status, stdout: concat5(out), stderr: concat5(err) };
|
|
@@ -12209,12 +12712,28 @@ function answered(data, remote, uplink) {
|
|
|
12209
12712
|
else return false;
|
|
12210
12713
|
return true;
|
|
12211
12714
|
}
|
|
12715
|
+
var USERS_OPS = /* @__PURE__ */ new Set(["users-add", "users-remove", "users-list"]);
|
|
12716
|
+
function isUsersRequest(req) {
|
|
12717
|
+
return USERS_OPS.has(req.op);
|
|
12718
|
+
}
|
|
12719
|
+
function failure(err) {
|
|
12720
|
+
const code = err?.code;
|
|
12721
|
+
return {
|
|
12722
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12723
|
+
...typeof code === "string" ? { code } : {}
|
|
12724
|
+
};
|
|
12725
|
+
}
|
|
12726
|
+
function usersOp(l, req) {
|
|
12727
|
+
if (req.op === "users-add") return l.users.add(req.user);
|
|
12728
|
+
if (req.op === "users-remove") return l.users.remove(req.name, req.options);
|
|
12729
|
+
return l.users.list();
|
|
12730
|
+
}
|
|
12212
12731
|
function serveKernel(port, deps) {
|
|
12213
12732
|
let launcher;
|
|
12214
12733
|
let remote;
|
|
12215
12734
|
let uplink;
|
|
12216
12735
|
const terminals = /* @__PURE__ */ new Map();
|
|
12217
|
-
const reply = (
|
|
12736
|
+
const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
|
|
12218
12737
|
const clients = /* @__PURE__ */ new Set();
|
|
12219
12738
|
const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
|
|
12220
12739
|
let holding;
|
|
@@ -12229,13 +12748,17 @@ function serveKernel(port, deps) {
|
|
|
12229
12748
|
});
|
|
12230
12749
|
return holding;
|
|
12231
12750
|
}
|
|
12232
|
-
async function connect() {
|
|
12233
|
-
await ready2();
|
|
12751
|
+
async function connect(user) {
|
|
12752
|
+
const l = await ready2();
|
|
12753
|
+
if (user !== void 0 && !await l.users.lookup(user)) {
|
|
12754
|
+
throw fsError("ENOENT", `no user ${String(user)}`);
|
|
12755
|
+
}
|
|
12234
12756
|
const { port1, port2 } = new MessageChannel();
|
|
12235
12757
|
const held = locks ? await holdLock(locks) : void 0;
|
|
12236
12758
|
const served = serveClient(port1, {
|
|
12237
12759
|
launcher: ready2,
|
|
12238
12760
|
signal: signalNumber,
|
|
12761
|
+
...user !== void 0 ? { user } : {},
|
|
12239
12762
|
...locks ? { locks } : {},
|
|
12240
12763
|
...held ? { lock: held } : {}
|
|
12241
12764
|
});
|
|
@@ -12304,7 +12827,8 @@ function serveKernel(port, deps) {
|
|
|
12304
12827
|
async function handle(req) {
|
|
12305
12828
|
if (req.op === "init") return init(req);
|
|
12306
12829
|
if (req.op === "open-terminal") return terminal(req);
|
|
12307
|
-
if (req.op === "connect") return connect();
|
|
12830
|
+
if (req.op === "connect") return connect(req.user);
|
|
12831
|
+
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
12308
12832
|
if (req.op === "routes") {
|
|
12309
12833
|
(await ready2()).setRoutes(req.routes);
|
|
12310
12834
|
return true;
|
|
@@ -12327,25 +12851,25 @@ function serveKernel(port, deps) {
|
|
|
12327
12851
|
const grants = /* @__PURE__ */ new Map();
|
|
12328
12852
|
let nextGrant = 0;
|
|
12329
12853
|
const askGrant = (source, options) => {
|
|
12330
|
-
const
|
|
12854
|
+
const id2 = ++nextGrant;
|
|
12331
12855
|
const waiting = Promise.withResolvers();
|
|
12332
|
-
grants.set(
|
|
12333
|
-
port.postMessage({ hostfs: { id, source, ...options } });
|
|
12856
|
+
grants.set(id2, waiting);
|
|
12857
|
+
port.postMessage({ hostfs: { id: id2, source, ...options } });
|
|
12334
12858
|
return waiting.promise;
|
|
12335
12859
|
};
|
|
12336
|
-
function granted2({ id, grant, error }) {
|
|
12337
|
-
const waiting = grants.get(
|
|
12860
|
+
function granted2({ id: id2, grant, error }) {
|
|
12861
|
+
const waiting = grants.get(id2);
|
|
12338
12862
|
if (!waiting) return;
|
|
12339
|
-
grants.delete(
|
|
12863
|
+
grants.delete(id2);
|
|
12340
12864
|
if (grant) waiting.resolve(grant);
|
|
12341
12865
|
else waiting.reject(Object.assign(new Error(error ?? "no grant"), { code: "EACCES" }));
|
|
12342
12866
|
}
|
|
12343
12867
|
const policies = /* @__PURE__ */ new Map();
|
|
12344
12868
|
let nextPolicy = 0;
|
|
12345
12869
|
const askPolicy = (req) => new Promise((resolve) => {
|
|
12346
|
-
const
|
|
12347
|
-
policies.set(
|
|
12348
|
-
port.postMessage({ mountPolicy: { id, req } });
|
|
12870
|
+
const id2 = ++nextPolicy;
|
|
12871
|
+
policies.set(id2, resolve);
|
|
12872
|
+
port.postMessage({ mountPolicy: { id: id2, req } });
|
|
12349
12873
|
});
|
|
12350
12874
|
const mountPolicy = (policy) => policy === void 0 ? {} : { processMounts: policy === "ask" ? askPolicy : policy };
|
|
12351
12875
|
port.addEventListener("message", (event) => {
|
|
@@ -12361,7 +12885,7 @@ function serveKernel(port, deps) {
|
|
|
12361
12885
|
const req = event.data;
|
|
12362
12886
|
handle(req).then(
|
|
12363
12887
|
(result) => reply(req.id, { result }, result instanceof MessagePort ? [result] : void 0),
|
|
12364
|
-
(err) => reply(req.id,
|
|
12888
|
+
(err) => reply(req.id, failure(err))
|
|
12365
12889
|
);
|
|
12366
12890
|
});
|
|
12367
12891
|
}
|