@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/node.js
CHANGED
|
@@ -1061,8 +1061,8 @@ function serveSocket(socket, port, onClose) {
|
|
|
1061
1061
|
let writes = Promise.resolve();
|
|
1062
1062
|
let credit = 0;
|
|
1063
1063
|
let granted2;
|
|
1064
|
-
const send = (
|
|
1065
|
-
if (!closed) port.postMessage(
|
|
1064
|
+
const send = (message3, transfer) => {
|
|
1065
|
+
if (!closed) port.postMessage(message3, transfer);
|
|
1066
1066
|
};
|
|
1067
1067
|
const close = (reset = false) => {
|
|
1068
1068
|
if (closed) return;
|
|
@@ -1194,8 +1194,8 @@ async function readWithin(reader, ms) {
|
|
|
1194
1194
|
var INBOUND_LIMIT = 16 * 1024 * 1024;
|
|
1195
1195
|
var MESSAGE_LIMIT = 64 * 1024 * 1024;
|
|
1196
1196
|
var TOO_BIG = 1009;
|
|
1197
|
-
function payloadSize(
|
|
1198
|
-
return typeof
|
|
1197
|
+
function payloadSize(message3) {
|
|
1198
|
+
return typeof message3 === "string" ? new TextEncoder().encode(message3).length : message3.length;
|
|
1199
1199
|
}
|
|
1200
1200
|
var MessageQueue = class {
|
|
1201
1201
|
items = [];
|
|
@@ -1215,9 +1215,9 @@ var MessageQueue = class {
|
|
|
1215
1215
|
this.settle = resolve;
|
|
1216
1216
|
});
|
|
1217
1217
|
}
|
|
1218
|
-
push(
|
|
1218
|
+
push(message3) {
|
|
1219
1219
|
if (this.ended) return;
|
|
1220
|
-
const size = payloadSize(
|
|
1220
|
+
const size = payloadSize(message3);
|
|
1221
1221
|
const huge = size > MESSAGE_LIMIT;
|
|
1222
1222
|
if (huge || this.items.length > 0 && this.size + size > this.limit) {
|
|
1223
1223
|
const reason = huge ? `a message is over ${MESSAGE_LIMIT} bytes` : `over ${this.limit} bytes of messages are waiting`;
|
|
@@ -1226,7 +1226,7 @@ var MessageQueue = class {
|
|
|
1226
1226
|
return;
|
|
1227
1227
|
}
|
|
1228
1228
|
this.size += size;
|
|
1229
|
-
this.items.push(
|
|
1229
|
+
this.items.push(message3);
|
|
1230
1230
|
this.wake();
|
|
1231
1231
|
}
|
|
1232
1232
|
end(close) {
|
|
@@ -1413,7 +1413,7 @@ function fetchTransport(options = {}) {
|
|
|
1413
1413
|
}
|
|
1414
1414
|
function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
1415
1415
|
const open = /* @__PURE__ */ new Map();
|
|
1416
|
-
const
|
|
1416
|
+
const fail3 = (nid, e) => {
|
|
1417
1417
|
open.delete(nid);
|
|
1418
1418
|
const { status, code } = e;
|
|
1419
1419
|
peer.postMessage({
|
|
@@ -1449,7 +1449,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
1449
1449
|
headers: response.headers
|
|
1450
1450
|
});
|
|
1451
1451
|
} catch (e) {
|
|
1452
|
-
|
|
1452
|
+
fail3(call.nid, e);
|
|
1453
1453
|
}
|
|
1454
1454
|
};
|
|
1455
1455
|
const sockets = /* @__PURE__ */ new Map();
|
|
@@ -1483,7 +1483,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
1483
1483
|
peer.postMessage({ net: "ws-opened", nid: call.nid, protocol: socket.protocol });
|
|
1484
1484
|
void relay(call.nid, socket);
|
|
1485
1485
|
} catch (e) {
|
|
1486
|
-
|
|
1486
|
+
fail3(call.nid, e);
|
|
1487
1487
|
}
|
|
1488
1488
|
};
|
|
1489
1489
|
const relay = async (nid, socket) => {
|
|
@@ -1555,7 +1555,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
1555
1555
|
peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
|
|
1556
1556
|
}
|
|
1557
1557
|
} catch (e) {
|
|
1558
|
-
|
|
1558
|
+
fail3(nid, e);
|
|
1559
1559
|
}
|
|
1560
1560
|
};
|
|
1561
1561
|
const drop = (nid) => {
|
|
@@ -1584,8 +1584,289 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
1584
1584
|
};
|
|
1585
1585
|
}
|
|
1586
1586
|
|
|
1587
|
+
// src/kernel/cred.ts
|
|
1588
|
+
var ROOT = Object.freeze({
|
|
1589
|
+
ruid: 0,
|
|
1590
|
+
euid: 0,
|
|
1591
|
+
suid: 0,
|
|
1592
|
+
rgid: 0,
|
|
1593
|
+
egid: 0,
|
|
1594
|
+
sgid: 0,
|
|
1595
|
+
groups: [0]
|
|
1596
|
+
});
|
|
1597
|
+
var MAX_ID = 4294967294;
|
|
1598
|
+
var MAX_GROUPS = 65536;
|
|
1599
|
+
var UIDS = ["ruid", "euid", "suid"];
|
|
1600
|
+
var GIDS = ["rgid", "egid", "sgid"];
|
|
1601
|
+
function userCred(uid, gid, groups = [gid]) {
|
|
1602
|
+
return { ruid: uid, euid: uid, suid: uid, rgid: gid, egid: gid, sgid: gid, groups: [...groups] };
|
|
1603
|
+
}
|
|
1604
|
+
function copyCred(cred) {
|
|
1605
|
+
return { ...cred, groups: [...cred.groups] };
|
|
1606
|
+
}
|
|
1607
|
+
function id(value) {
|
|
1608
|
+
if (value === void 0 || value === -1 || value === 4294967295) return void 0;
|
|
1609
|
+
if (!Number.isInteger(value) || value < 0 || value > MAX_ID) {
|
|
1610
|
+
throw fsError("EINVAL", `not a user or group id: ${String(value)}`);
|
|
1611
|
+
}
|
|
1612
|
+
return value;
|
|
1613
|
+
}
|
|
1614
|
+
function changeCred(cur, change) {
|
|
1615
|
+
const privileged = cur.euid === 0;
|
|
1616
|
+
const next2 = copyCred(cur);
|
|
1617
|
+
for (const keys of [UIDS, GIDS]) {
|
|
1618
|
+
const own = keys.map((key) => cur[key]);
|
|
1619
|
+
for (const key of keys) {
|
|
1620
|
+
const value = id(change[key]);
|
|
1621
|
+
if (value === void 0) continue;
|
|
1622
|
+
if (!privileged && !own.includes(value)) throw fsError("EPERM", `${key} ${value}`);
|
|
1623
|
+
next2[key] = value;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (change.groups !== void 0) {
|
|
1627
|
+
if (!Array.isArray(change.groups) || change.groups.length > MAX_GROUPS) {
|
|
1628
|
+
throw fsError("EINVAL", "not a group list");
|
|
1629
|
+
}
|
|
1630
|
+
const groups = change.groups.map((g) => id(g) ?? Number.NaN);
|
|
1631
|
+
if (groups.some(Number.isNaN)) throw fsError("EINVAL", "not a group list");
|
|
1632
|
+
if (!privileged) throw fsError("EPERM", "setgroups");
|
|
1633
|
+
next2.groups = groups;
|
|
1634
|
+
}
|
|
1635
|
+
return next2;
|
|
1636
|
+
}
|
|
1637
|
+
function maySignal(sender, target) {
|
|
1638
|
+
if (sender.euid === 0) return true;
|
|
1639
|
+
return [sender.ruid, sender.euid].some((uid) => uid === target.ruid || uid === target.suid);
|
|
1640
|
+
}
|
|
1641
|
+
function maySee(viewer, target) {
|
|
1642
|
+
return viewer.euid === 0 || target.ruid === viewer.euid || target.euid === viewer.euid;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// src/users.ts
|
|
1646
|
+
var PASSWD = "/etc/passwd";
|
|
1647
|
+
var GROUP = "/etc/group";
|
|
1648
|
+
var DEFAULT_PASSWD = "root:x:0:0:root:/root:/bin/bash\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n";
|
|
1649
|
+
var DEFAULT_GROUP = "root:x:0:\nsudo:x:27:\nusers:x:100:\nnogroup:x:65534:\n";
|
|
1650
|
+
var NAME = /^[a-z_][a-z0-9_.-]*$/;
|
|
1651
|
+
var MAX_NAME = 255;
|
|
1652
|
+
var FIRST_UID = 1e3;
|
|
1653
|
+
var LAST_UID = 6e4;
|
|
1654
|
+
var USERS_GROUP = "users";
|
|
1655
|
+
var HOME_DIR = /^\/home\/[^/]+$/;
|
|
1656
|
+
var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
|
|
1657
|
+
var isId = (text) => text !== void 0 && /^\d+$/.test(text);
|
|
1658
|
+
function lines(text) {
|
|
1659
|
+
return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
|
|
1660
|
+
}
|
|
1661
|
+
function parsePasswd(text) {
|
|
1662
|
+
return lines(text).map((line) => {
|
|
1663
|
+
const [name2, , uid, gid, gecos = "", home = "/", shell = "/bin/sh"] = line.split(":");
|
|
1664
|
+
if (!name2 || !isId(uid) || !isId(gid)) return line;
|
|
1665
|
+
return { name: name2, uid: Number(uid), gid: Number(gid), gecos, home, shell };
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
function parseGroup(text) {
|
|
1669
|
+
return lines(text).map((line) => {
|
|
1670
|
+
const [name2, , gid, members = ""] = line.split(":");
|
|
1671
|
+
if (!name2 || !isId(gid)) return line;
|
|
1672
|
+
return { name: name2, gid: Number(gid), members: members.split(",").filter(Boolean) };
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
function formatPasswd(rows) {
|
|
1676
|
+
const line = (r) => `${r.name}:x:${r.uid}:${r.gid}:${r.gecos}:${r.home}:${r.shell}`;
|
|
1677
|
+
return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
|
|
1678
|
+
`;
|
|
1679
|
+
}
|
|
1680
|
+
function formatGroup(rows) {
|
|
1681
|
+
const line = (r) => `${r.name}:x:${r.gid}:${r.members.join(",")}`;
|
|
1682
|
+
return `${rows.map((r) => typeof r === "string" ? r : line(r)).join("\n")}
|
|
1683
|
+
`;
|
|
1684
|
+
}
|
|
1685
|
+
var parsed = (rows) => rows.filter((r) => typeof r !== "string");
|
|
1686
|
+
function homeOf(home) {
|
|
1687
|
+
if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
|
|
1688
|
+
throw fsError("EINVAL", `not a home directory: ${String(home)}`);
|
|
1689
|
+
}
|
|
1690
|
+
return home;
|
|
1691
|
+
}
|
|
1692
|
+
function freeId(users, groups) {
|
|
1693
|
+
const taken = /* @__PURE__ */ new Set([...users.map((r) => r.uid), ...groups.map((r) => r.gid)]);
|
|
1694
|
+
let uid = FIRST_UID;
|
|
1695
|
+
while (taken.has(uid)) uid++;
|
|
1696
|
+
if (uid > LAST_UID) throw fsError("ENOSPC", "no free user id");
|
|
1697
|
+
return uid;
|
|
1698
|
+
}
|
|
1699
|
+
function credOf(account) {
|
|
1700
|
+
return userCred(account.uid, account.gid, account.groups);
|
|
1701
|
+
}
|
|
1702
|
+
function pnpmHomeOf(account, system = SYSTEM_PNPM_HOME) {
|
|
1703
|
+
return account.uid === 0 ? system : `${account.home}/.local/share/pnpm`;
|
|
1704
|
+
}
|
|
1705
|
+
function userEnv(account, system) {
|
|
1706
|
+
const pnpm = pnpmHomeOf(account, system);
|
|
1707
|
+
return {
|
|
1708
|
+
HOME: account.home,
|
|
1709
|
+
USER: account.name,
|
|
1710
|
+
LOGNAME: account.name,
|
|
1711
|
+
PNPM_HOME: pnpm,
|
|
1712
|
+
PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
var UserDb = class {
|
|
1716
|
+
fs;
|
|
1717
|
+
umasks = /* @__PURE__ */ new Map();
|
|
1718
|
+
writing = Promise.resolve();
|
|
1719
|
+
constructor(fs) {
|
|
1720
|
+
this.fs = fs;
|
|
1721
|
+
}
|
|
1722
|
+
async read(path, fallback) {
|
|
1723
|
+
try {
|
|
1724
|
+
return await this.fs.readFile(path);
|
|
1725
|
+
} catch (err) {
|
|
1726
|
+
if (err?.code === "ENOENT") return fallback;
|
|
1727
|
+
throw err;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
async tables() {
|
|
1731
|
+
return {
|
|
1732
|
+
passwd: parsePasswd(await this.read(PASSWD, DEFAULT_PASSWD)),
|
|
1733
|
+
group: parseGroup(await this.read(GROUP, DEFAULT_GROUP))
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
async commit(passwd, group, then) {
|
|
1737
|
+
const before = [await this.read(PASSWD, DEFAULT_PASSWD), await this.read(GROUP, DEFAULT_GROUP)];
|
|
1738
|
+
try {
|
|
1739
|
+
await this.fs.writeFile(GROUP, group);
|
|
1740
|
+
await this.fs.writeFile(PASSWD, passwd);
|
|
1741
|
+
await then?.();
|
|
1742
|
+
} catch (err) {
|
|
1743
|
+
await this.fs.writeFile(GROUP, before[1]);
|
|
1744
|
+
await this.fs.writeFile(PASSWD, before[0]);
|
|
1745
|
+
throw err;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
serial(work) {
|
|
1749
|
+
const next2 = this.writing.then(work);
|
|
1750
|
+
this.writing = next2.catch(() => void 0);
|
|
1751
|
+
return next2;
|
|
1752
|
+
}
|
|
1753
|
+
async ensure() {
|
|
1754
|
+
await this.fs.mkdir("/etc", { recursive: true });
|
|
1755
|
+
for (const [path, text] of [
|
|
1756
|
+
[PASSWD, DEFAULT_PASSWD],
|
|
1757
|
+
[GROUP, DEFAULT_GROUP]
|
|
1758
|
+
]) {
|
|
1759
|
+
if (!await this.fs.exists(path)) await this.fs.writeFile(path, text);
|
|
1760
|
+
}
|
|
1761
|
+
if (!await this.fs.exists("/root")) {
|
|
1762
|
+
await this.fs.mkdir("/root", { recursive: true });
|
|
1763
|
+
try {
|
|
1764
|
+
await this.fs.chmod("/root", 448);
|
|
1765
|
+
} catch (err) {
|
|
1766
|
+
await this.fs.rm("/root", { recursive: true, force: true });
|
|
1767
|
+
throw err;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
account(row, groups) {
|
|
1772
|
+
const extra = groups.filter((g) => g.members.includes(row.name) && g.gid !== row.gid);
|
|
1773
|
+
const umask = this.umasks.get(row.name);
|
|
1774
|
+
return {
|
|
1775
|
+
name: row.name,
|
|
1776
|
+
uid: row.uid,
|
|
1777
|
+
gid: row.gid,
|
|
1778
|
+
home: row.home,
|
|
1779
|
+
shell: row.shell,
|
|
1780
|
+
groups: [row.gid, ...extra.map((g) => g.gid)],
|
|
1781
|
+
...umask !== void 0 ? { umask } : {}
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
async lookup(user) {
|
|
1785
|
+
const { passwd, group } = await this.tables();
|
|
1786
|
+
const key = typeof user === "number" ? "uid" : "name";
|
|
1787
|
+
const row = parsed(passwd).find((r) => r[key] === user);
|
|
1788
|
+
return row && this.account(row, parsed(group));
|
|
1789
|
+
}
|
|
1790
|
+
async list() {
|
|
1791
|
+
const { passwd, group } = await this.tables();
|
|
1792
|
+
const groups = parsed(group);
|
|
1793
|
+
return parsed(passwd).map((row) => this.account(row, groups));
|
|
1794
|
+
}
|
|
1795
|
+
add(options) {
|
|
1796
|
+
return this.serial(async () => {
|
|
1797
|
+
const { name: name2 } = options;
|
|
1798
|
+
if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME.test(name2)) {
|
|
1799
|
+
throw fsError("EINVAL", `not a user name: ${String(name2)}`);
|
|
1800
|
+
}
|
|
1801
|
+
const remember = () => {
|
|
1802
|
+
if (options.umask !== void 0) this.umasks.set(name2, options.umask & 511);
|
|
1803
|
+
};
|
|
1804
|
+
const { passwd, group } = await this.tables();
|
|
1805
|
+
const users = parsed(passwd);
|
|
1806
|
+
const groups = parsed(group);
|
|
1807
|
+
const found = users.find((r) => r.name === name2);
|
|
1808
|
+
if (found) {
|
|
1809
|
+
remember();
|
|
1810
|
+
return this.account(found, groups);
|
|
1811
|
+
}
|
|
1812
|
+
const home = homeOf(options.home ?? `/home/${name2}`);
|
|
1813
|
+
const extra = [USERS_GROUP, ...options.groups ?? []].map((g) => {
|
|
1814
|
+
const row2 = groups.find((r) => r.name === g);
|
|
1815
|
+
if (!row2) throw fsError("ENOENT", `no group ${g}`);
|
|
1816
|
+
return row2;
|
|
1817
|
+
});
|
|
1818
|
+
if (groups.some((r) => r.name === name2)) throw fsError("EEXIST", `group ${name2} exists`);
|
|
1819
|
+
const uid = freeId(users, groups);
|
|
1820
|
+
const row = { name: name2, uid, gid: uid, gecos: name2, home, shell: "/bin/bash" };
|
|
1821
|
+
for (const g of extra) if (!g.members.includes(name2)) g.members.push(name2);
|
|
1822
|
+
group.push({ name: name2, gid: uid, members: [] });
|
|
1823
|
+
passwd.push(row);
|
|
1824
|
+
const fresh = !await this.fs.exists(home);
|
|
1825
|
+
await this.fs.mkdir(home, { recursive: true });
|
|
1826
|
+
try {
|
|
1827
|
+
await this.fs.mkdir("/etc", { recursive: true });
|
|
1828
|
+
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
1829
|
+
} catch (err) {
|
|
1830
|
+
if (fresh) await this.fs.rm(home, { recursive: true, force: true });
|
|
1831
|
+
throw err;
|
|
1832
|
+
}
|
|
1833
|
+
remember();
|
|
1834
|
+
return this.account(row, parsed(group));
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
remove(name2, options = {}) {
|
|
1838
|
+
return this.serial(async () => {
|
|
1839
|
+
const { passwd, group } = await this.tables();
|
|
1840
|
+
const row = parsed(passwd).find((r) => r.name === name2);
|
|
1841
|
+
if (!row) return false;
|
|
1842
|
+
if (row.uid < FIRST_UID || row.uid > LAST_UID) {
|
|
1843
|
+
throw fsError("EPERM", `${name2} is a system account`);
|
|
1844
|
+
}
|
|
1845
|
+
const keptGroups = group.filter(
|
|
1846
|
+
(r) => typeof r === "string" || !(r.name === name2 && r.gid === row.gid)
|
|
1847
|
+
);
|
|
1848
|
+
for (const r of parsed(keptGroups)) r.members = r.members.filter((m) => m !== name2);
|
|
1849
|
+
const home = this.fs.resolvePath("/", row.home);
|
|
1850
|
+
const shared = parsed(passwd).some(
|
|
1851
|
+
(r) => r !== row && this.fs.resolvePath("/", r.home) === home
|
|
1852
|
+
);
|
|
1853
|
+
const removeHome = !options.keepHome && !shared && HOME_DIR.test(home) && await this.fs.exists(home);
|
|
1854
|
+
const staged = `/home/.${name2}.removed-${Date.now()}`;
|
|
1855
|
+
await this.commit(
|
|
1856
|
+
formatPasswd(passwd.filter((r) => r !== row)),
|
|
1857
|
+
formatGroup(keptGroups),
|
|
1858
|
+
removeHome ? () => this.fs.rename(home, staged) : void 0
|
|
1859
|
+
);
|
|
1860
|
+
this.umasks.delete(name2);
|
|
1861
|
+
if (removeHome)
|
|
1862
|
+
await this.fs.rm(staged, { recursive: true, force: true }).catch(() => void 0);
|
|
1863
|
+
return true;
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
|
|
1587
1868
|
// src/client/protocol.ts
|
|
1588
|
-
var PROTOCOL = [1,
|
|
1869
|
+
var PROTOCOL = [1, 8];
|
|
1589
1870
|
var FS_METHODS = [
|
|
1590
1871
|
"readFile",
|
|
1591
1872
|
"writeFile",
|
|
@@ -1688,6 +1969,39 @@ function dialFor(l, req, open) {
|
|
|
1688
1969
|
);
|
|
1689
1970
|
return { result: port2, transfer: [port2] };
|
|
1690
1971
|
}
|
|
1972
|
+
async function accountOf(l, user) {
|
|
1973
|
+
const found = await l.users.lookup(user);
|
|
1974
|
+
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
1975
|
+
return found;
|
|
1976
|
+
}
|
|
1977
|
+
async function runAs(l, self, requested) {
|
|
1978
|
+
if (requested === void 0) return self;
|
|
1979
|
+
const target = await accountOf(l, requested);
|
|
1980
|
+
if (self.uid !== 0 && target.uid !== self.uid) {
|
|
1981
|
+
throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
|
|
1982
|
+
}
|
|
1983
|
+
return target;
|
|
1984
|
+
}
|
|
1985
|
+
function processes(l, self) {
|
|
1986
|
+
const viewer = credOf(self);
|
|
1987
|
+
return l.list(viewer).map(({ tid: _, cred = viewer, umask: _umask, ...entry }) => ({
|
|
1988
|
+
...entry,
|
|
1989
|
+
uid: cred.euid,
|
|
1990
|
+
gid: cred.egid
|
|
1991
|
+
}));
|
|
1992
|
+
}
|
|
1993
|
+
function killAs(l, host, self, req) {
|
|
1994
|
+
if (!l.kill(req.pid, signalOf(host)(req.signal), credOf(self))) {
|
|
1995
|
+
throw fsError("ESRCH", `pid ${req.pid}`);
|
|
1996
|
+
}
|
|
1997
|
+
return { result: true };
|
|
1998
|
+
}
|
|
1999
|
+
function terminalAction(terminals, req, host) {
|
|
2000
|
+
const session = terminals.get(req.terminal);
|
|
2001
|
+
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
2002
|
+
act(session, req, signalOf(host));
|
|
2003
|
+
return { result: true };
|
|
2004
|
+
}
|
|
1691
2005
|
function killGroups(host, pgids) {
|
|
1692
2006
|
if (pgids.length === 0) return;
|
|
1693
2007
|
void host.launcher().then((launcher) => {
|
|
@@ -1701,9 +2015,9 @@ function joinable(l, pgid, ours) {
|
|
|
1701
2015
|
}
|
|
1702
2016
|
function cdpRegistry(send, gone) {
|
|
1703
2017
|
const registered = /* @__PURE__ */ new Map();
|
|
1704
|
-
const drop = (
|
|
1705
|
-
registered.get(
|
|
1706
|
-
return registered.delete(
|
|
2018
|
+
const drop = (id2) => {
|
|
2019
|
+
registered.get(id2)?.();
|
|
2020
|
+
return registered.delete(id2);
|
|
1707
2021
|
};
|
|
1708
2022
|
return {
|
|
1709
2023
|
serve(l, req) {
|
|
@@ -1718,7 +2032,7 @@ function cdpRegistry(send, gone) {
|
|
|
1718
2032
|
},
|
|
1719
2033
|
drop,
|
|
1720
2034
|
close: () => {
|
|
1721
|
-
for (const
|
|
2035
|
+
for (const id2 of [...registered.keys()]) drop(id2);
|
|
1722
2036
|
}
|
|
1723
2037
|
};
|
|
1724
2038
|
}
|
|
@@ -1738,16 +2052,17 @@ function watchOp(l, req, watches, send) {
|
|
|
1738
2052
|
}
|
|
1739
2053
|
function serveClient(port, host) {
|
|
1740
2054
|
let state = "new";
|
|
2055
|
+
let self;
|
|
1741
2056
|
const terminals = /* @__PURE__ */ new Map();
|
|
1742
2057
|
const groups = /* @__PURE__ */ new Set();
|
|
1743
2058
|
const sessions = /* @__PURE__ */ new Set();
|
|
1744
2059
|
const watches = /* @__PURE__ */ new Map();
|
|
1745
2060
|
let net;
|
|
1746
2061
|
const ended = Promise.withResolvers();
|
|
1747
|
-
const send = (
|
|
1748
|
-
if (state !== "closed") port.postMessage(
|
|
2062
|
+
const send = (message3, transfer = []) => {
|
|
2063
|
+
if (state !== "closed") port.postMessage(message3, transfer);
|
|
1749
2064
|
};
|
|
1750
|
-
const reply = (
|
|
2065
|
+
const reply = (id2, body, transfer) => send({ id: id2, ...body }, transfer);
|
|
1751
2066
|
const cdps = cdpRegistry(send, ended.promise);
|
|
1752
2067
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
1753
2068
|
if (state === "closed") return;
|
|
@@ -1764,18 +2079,19 @@ function serveClient(port, host) {
|
|
|
1764
2079
|
port.close?.();
|
|
1765
2080
|
ended.resolve();
|
|
1766
2081
|
}
|
|
1767
|
-
async function hello(
|
|
1768
|
-
const refused = versionError(
|
|
2082
|
+
async function hello(message3) {
|
|
2083
|
+
const refused = versionError(message3.protocol);
|
|
1769
2084
|
if (refused) {
|
|
1770
2085
|
send({ hello: { protocol: PROTOCOL, error: refused } });
|
|
1771
2086
|
detach();
|
|
1772
2087
|
return;
|
|
1773
2088
|
}
|
|
1774
2089
|
const launcher = await host.launcher();
|
|
2090
|
+
if (host.scope !== "transport") self = await accountOf(launcher, host.user ?? 0);
|
|
1775
2091
|
const transport = host.transport?.() ?? launcher.transport;
|
|
1776
2092
|
net = serveTransport({ postMessage: (m, t) => send(m, t) }, transport);
|
|
1777
2093
|
state = "open";
|
|
1778
|
-
if (
|
|
2094
|
+
if (message3.lock && host.locks) void host.locks.request(message3.lock, () => detach());
|
|
1779
2095
|
send({
|
|
1780
2096
|
hello: {
|
|
1781
2097
|
protocol: PROTOCOL,
|
|
@@ -1787,6 +2103,7 @@ function serveClient(port, host) {
|
|
|
1787
2103
|
async function terminal(req, l) {
|
|
1788
2104
|
const session = await l.openTerminal(req.argv, {
|
|
1789
2105
|
...req.options,
|
|
2106
|
+
user: await runAs(l, self, req.options.user),
|
|
1790
2107
|
onData: (bytes) => reply(req.id, { fd: 1, bytes: bytes.slice() })
|
|
1791
2108
|
});
|
|
1792
2109
|
if (state === "closed") {
|
|
@@ -1808,6 +2125,7 @@ function serveClient(port, host) {
|
|
|
1808
2125
|
let leader;
|
|
1809
2126
|
const result = await l.run(req.argv, {
|
|
1810
2127
|
...req.options,
|
|
2128
|
+
user: await runAs(l, self, req.options.user),
|
|
1811
2129
|
collect: false,
|
|
1812
2130
|
onStdout: out(1),
|
|
1813
2131
|
onStderr: out(2),
|
|
@@ -1833,19 +2151,12 @@ function serveClient(port, host) {
|
|
|
1833
2151
|
return spawn(req, l);
|
|
1834
2152
|
case "open-terminal":
|
|
1835
2153
|
return terminal(req, l);
|
|
1836
|
-
case "terminal":
|
|
1837
|
-
|
|
1838
|
-
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
1839
|
-
act(session, req, signalOf(host));
|
|
1840
|
-
return { result: true };
|
|
1841
|
-
}
|
|
2154
|
+
case "terminal":
|
|
2155
|
+
return terminalAction(terminals, req, host);
|
|
1842
2156
|
case "kill":
|
|
1843
|
-
|
|
1844
|
-
return { result: true };
|
|
2157
|
+
return killAs(l, host, self, req);
|
|
1845
2158
|
case "ps":
|
|
1846
|
-
return {
|
|
1847
|
-
result: l.list().map(({ tid: _, ...entry }) => entry)
|
|
1848
|
-
};
|
|
2159
|
+
return { result: processes(l, self) };
|
|
1849
2160
|
case "fs":
|
|
1850
2161
|
if (!FS_METHODS.includes(req.method)) {
|
|
1851
2162
|
throw new Error(`unknown file system call ${req.method}`);
|
|
@@ -2611,9 +2922,9 @@ var UNLINKED_DIR = ".slicc-unlinked";
|
|
|
2611
2922
|
function pidOwner(proc) {
|
|
2612
2923
|
return {
|
|
2613
2924
|
id: `p${proc.pid}`,
|
|
2614
|
-
alive(
|
|
2925
|
+
alive(id2) {
|
|
2615
2926
|
try {
|
|
2616
|
-
proc.kill(Number(
|
|
2927
|
+
proc.kill(Number(id2.slice(1)), 0);
|
|
2617
2928
|
return true;
|
|
2618
2929
|
} catch (err) {
|
|
2619
2930
|
return err.code !== "ESRCH";
|
|
@@ -2800,9 +3111,9 @@ var UnlinkedKeeper = class {
|
|
|
2800
3111
|
if (!names) return;
|
|
2801
3112
|
const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
2802
3113
|
for (const name2 of names) {
|
|
2803
|
-
const
|
|
2804
|
-
const at =
|
|
2805
|
-
const dead =
|
|
3114
|
+
const id2 = name2.slice(0, name2.lastIndexOf("."));
|
|
3115
|
+
const at = id2.indexOf("~");
|
|
3116
|
+
const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
|
|
2806
3117
|
if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
2807
3118
|
}
|
|
2808
3119
|
await this.tidy(dir);
|
|
@@ -2813,8 +3124,8 @@ var UnlinkedKeeper = class {
|
|
|
2813
3124
|
// src/kernel/net/http1.ts
|
|
2814
3125
|
var HttpError = class extends Error {
|
|
2815
3126
|
status;
|
|
2816
|
-
constructor(status,
|
|
2817
|
-
super(
|
|
3127
|
+
constructor(status, message3) {
|
|
3128
|
+
super(message3);
|
|
2818
3129
|
this.status = status;
|
|
2819
3130
|
}
|
|
2820
3131
|
};
|
|
@@ -2938,21 +3249,21 @@ function latin1Bytes(text) {
|
|
|
2938
3249
|
return out;
|
|
2939
3250
|
}
|
|
2940
3251
|
function parseRequestHead(bytes) {
|
|
2941
|
-
const
|
|
2942
|
-
while (
|
|
2943
|
-
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(
|
|
3252
|
+
const lines2 = latin1(bytes).split(/\r?\n/);
|
|
3253
|
+
while (lines2.length > 0 && lines2[0] === "") lines2.shift();
|
|
3254
|
+
const match = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+) (\S+) HTTP\/(\d)\.(\d)$/.exec(lines2.shift() ?? "");
|
|
2944
3255
|
if (!match) throw new HttpError(400, "malformed request line");
|
|
2945
3256
|
if (match[3] !== "1") throw new HttpError(505, `HTTP/${match[3]}.${match[4]} is not supported`);
|
|
2946
3257
|
return {
|
|
2947
3258
|
method: match[1],
|
|
2948
3259
|
target: match[2],
|
|
2949
3260
|
minor: Number(match[4]),
|
|
2950
|
-
headers: parseFields(
|
|
3261
|
+
headers: parseFields(lines2)
|
|
2951
3262
|
};
|
|
2952
3263
|
}
|
|
2953
|
-
function parseFields(
|
|
3264
|
+
function parseFields(lines2) {
|
|
2954
3265
|
const headers = [];
|
|
2955
|
-
for (const line of
|
|
3266
|
+
for (const line of lines2) {
|
|
2956
3267
|
if (line === "") continue;
|
|
2957
3268
|
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
2958
3269
|
throw new HttpError(400, "obsolete line folding");
|
|
@@ -3081,8 +3392,8 @@ var OP = {
|
|
|
3081
3392
|
};
|
|
3082
3393
|
var WsError = class extends Error {
|
|
3083
3394
|
code;
|
|
3084
|
-
constructor(code,
|
|
3085
|
-
super(
|
|
3395
|
+
constructor(code, message3) {
|
|
3396
|
+
super(message3);
|
|
3086
3397
|
this.code = code;
|
|
3087
3398
|
}
|
|
3088
3399
|
};
|
|
@@ -3379,11 +3690,11 @@ var CdpFacade = class {
|
|
|
3379
3690
|
clearTimeout(timer);
|
|
3380
3691
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
3381
3692
|
};
|
|
3382
|
-
conn.onmessage = (
|
|
3383
|
-
const
|
|
3384
|
-
if (
|
|
3693
|
+
conn.onmessage = (message3) => {
|
|
3694
|
+
const parsed2 = JSON.parse(message3);
|
|
3695
|
+
if (parsed2.id !== 1) return;
|
|
3385
3696
|
clearTimeout(timer);
|
|
3386
|
-
resolve(
|
|
3697
|
+
resolve(parsed2);
|
|
3387
3698
|
};
|
|
3388
3699
|
conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
3389
3700
|
}
|
|
@@ -3448,9 +3759,9 @@ var Relay = class {
|
|
|
3448
3759
|
void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
|
|
3449
3760
|
};
|
|
3450
3761
|
});
|
|
3451
|
-
this.host.onmessage = (
|
|
3762
|
+
this.host.onmessage = (message3) => {
|
|
3452
3763
|
if (this.ended) return;
|
|
3453
|
-
const bytes = new TextEncoder().encode(
|
|
3764
|
+
const bytes = new TextEncoder().encode(message3);
|
|
3454
3765
|
if (bytes.length > this.max)
|
|
3455
3766
|
void this.end(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
3456
3767
|
else void this.send(frame(OP_TEXT, bytes));
|
|
@@ -3556,7 +3867,7 @@ function closeAll(entry) {
|
|
|
3556
3867
|
}
|
|
3557
3868
|
|
|
3558
3869
|
// src/commands.ts
|
|
3559
|
-
var
|
|
3870
|
+
var NAME2 = /^[A-Za-z0-9._+-]+$/;
|
|
3560
3871
|
var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3561
3872
|
var PACKAGE = /\$\{package\}/g;
|
|
3562
3873
|
function inside(pkg, rel2) {
|
|
@@ -3645,7 +3956,7 @@ function commandsOf(pkg, manifest) {
|
|
|
3645
3956
|
const shared = envOf(pkg, slicc.env);
|
|
3646
3957
|
const out = [];
|
|
3647
3958
|
for (const [name2, raw] of Object.entries(entries)) {
|
|
3648
|
-
if (!
|
|
3959
|
+
if (!NAME2.test(name2) || name2 === "." || name2 === ".." || !raw || typeof raw !== "object") {
|
|
3649
3960
|
continue;
|
|
3650
3961
|
}
|
|
3651
3962
|
const command = commandOf(pkg, name2, raw, packageAbi, shared);
|
|
@@ -3729,7 +4040,7 @@ async function scanFilesystems(fs, modules) {
|
|
|
3729
4040
|
if (!declared || typeof declared !== "object") continue;
|
|
3730
4041
|
for (const [type, entry] of Object.entries(declared)) {
|
|
3731
4042
|
const module = inside(pkg, entry?.module);
|
|
3732
|
-
if (
|
|
4043
|
+
if (NAME2.test(type) && module && !found.has(type)) found.set(type, module);
|
|
3733
4044
|
}
|
|
3734
4045
|
}
|
|
3735
4046
|
return found;
|
|
@@ -3749,7 +4060,7 @@ async function scanBinfmts(fs, modules, warn = () => {
|
|
|
3749
4060
|
const declared = manifest.slicc?.binfmt;
|
|
3750
4061
|
if (!declared || typeof declared !== "object") continue;
|
|
3751
4062
|
for (const [suffix, command] of Object.entries(declared)) {
|
|
3752
|
-
if (!SUFFIX.test(suffix) || typeof command !== "string" || !
|
|
4063
|
+
if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME2.test(command)) continue;
|
|
3753
4064
|
const owner = owners.get(suffix);
|
|
3754
4065
|
if (owner === void 0) {
|
|
3755
4066
|
found.set(suffix, command);
|
|
@@ -4038,10 +4349,10 @@ var SpawnError = class extends Error {
|
|
|
4038
4349
|
};
|
|
4039
4350
|
function interrupted(signal) {
|
|
4040
4351
|
const { promise, reject } = Promise.withResolvers();
|
|
4041
|
-
const
|
|
4042
|
-
if (signal?.aborted)
|
|
4043
|
-
else signal?.addEventListener("abort",
|
|
4044
|
-
return { promise, done: () => signal?.removeEventListener("abort",
|
|
4352
|
+
const fail3 = () => reject(new KernelError("EINTR"));
|
|
4353
|
+
if (signal?.aborted) fail3();
|
|
4354
|
+
else signal?.addEventListener("abort", fail3, { once: true });
|
|
4355
|
+
return { promise, done: () => signal?.removeEventListener("abort", fail3) };
|
|
4045
4356
|
}
|
|
4046
4357
|
function stoppedStatus(sig) {
|
|
4047
4358
|
return (sig & 255) << 8 | 127;
|
|
@@ -4232,8 +4543,8 @@ function unref(timer) {
|
|
|
4232
4543
|
timer.unref?.();
|
|
4233
4544
|
return timer;
|
|
4234
4545
|
}
|
|
4235
|
-
function errnoError(code,
|
|
4236
|
-
return Object.assign(new Error(`${code}: ${
|
|
4546
|
+
function errnoError(code, message3) {
|
|
4547
|
+
return Object.assign(new Error(`${code}: ${message3}`), { code });
|
|
4237
4548
|
}
|
|
4238
4549
|
var DriverConnection = class _DriverConnection {
|
|
4239
4550
|
capabilities;
|
|
@@ -4286,11 +4597,11 @@ var DriverConnection = class _DriverConnection {
|
|
|
4286
4597
|
}
|
|
4287
4598
|
call(call, transfer = []) {
|
|
4288
4599
|
if (this.failure) return Promise.reject(this.failure);
|
|
4289
|
-
const
|
|
4600
|
+
const id2 = ++this.nextId;
|
|
4290
4601
|
return new Promise((resolve, reject) => {
|
|
4291
4602
|
const timer = unref(
|
|
4292
4603
|
setTimeout(() => {
|
|
4293
|
-
this.waiting.delete(
|
|
4604
|
+
this.waiting.delete(id2);
|
|
4294
4605
|
const error = errnoError(
|
|
4295
4606
|
"EIO",
|
|
4296
4607
|
`the driver did not answer ${call.op} within ${this.timeoutMs} ms`
|
|
@@ -4300,8 +4611,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
4300
4611
|
this.onFail?.(error);
|
|
4301
4612
|
}, this.timeoutMs)
|
|
4302
4613
|
);
|
|
4303
|
-
this.waiting.set(
|
|
4304
|
-
this.port.postMessage({ ...call, id }, transfer);
|
|
4614
|
+
this.waiting.set(id2, { resolve, reject, timer });
|
|
4615
|
+
this.port.postMessage({ ...call, id: id2 }, transfer);
|
|
4305
4616
|
});
|
|
4306
4617
|
}
|
|
4307
4618
|
fail(error) {
|
|
@@ -4376,22 +4687,22 @@ var HeldPaths = class extends Map {
|
|
|
4376
4687
|
}
|
|
4377
4688
|
isKept(path) {
|
|
4378
4689
|
const ids = this.handlesAt(path);
|
|
4379
|
-
return ids.length === 0 ? this.unlinked.has(path) : ids.every((
|
|
4690
|
+
return ids.length === 0 ? this.unlinked.has(path) : ids.every((id2) => this.keptHandles.has(id2));
|
|
4380
4691
|
}
|
|
4381
4692
|
handlesAt(path) {
|
|
4382
|
-
return [...this.handles].filter(([, key]) => key === path).map(([
|
|
4693
|
+
return [...this.handles].filter(([, key]) => key === path).map(([id2]) => id2);
|
|
4383
4694
|
}
|
|
4384
4695
|
keptFor(handle) {
|
|
4385
4696
|
return this.keptHandles.get(handle);
|
|
4386
4697
|
}
|
|
4387
4698
|
keep(path, file) {
|
|
4388
|
-
const fresh = this.handlesAt(path).filter((
|
|
4389
|
-
for (const
|
|
4699
|
+
const fresh = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
4700
|
+
for (const id2 of fresh) this.keptHandles.set(id2, file);
|
|
4390
4701
|
this.uses.set(file, Math.max(fresh.length, 1));
|
|
4391
4702
|
this.unlinked.set(path, file);
|
|
4392
4703
|
}
|
|
4393
4704
|
unkeep(path, file) {
|
|
4394
|
-
for (const [
|
|
4705
|
+
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
4395
4706
|
this.uses.delete(file);
|
|
4396
4707
|
if (this.unlinked.get(path) === file) this.unlinked.delete(path);
|
|
4397
4708
|
}
|
|
@@ -4428,7 +4739,7 @@ var HeldPaths = class extends Map {
|
|
|
4428
4739
|
if (count > 0) this.opens.set(key, count);
|
|
4429
4740
|
else {
|
|
4430
4741
|
this.opens.delete(key);
|
|
4431
|
-
for (const
|
|
4742
|
+
for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
|
|
4432
4743
|
const kept = this.unlinked.get(key);
|
|
4433
4744
|
if (kept) this.letGo(kept, true);
|
|
4434
4745
|
for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
|
|
@@ -4475,8 +4786,8 @@ var HeldPaths = class extends Map {
|
|
|
4475
4786
|
this.opens.delete(path);
|
|
4476
4787
|
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
4477
4788
|
}
|
|
4478
|
-
for (const [
|
|
4479
|
-
if (within3(key, from)) this.handles.set(
|
|
4789
|
+
for (const [id2, key] of this.handles) {
|
|
4790
|
+
if (within3(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
4480
4791
|
}
|
|
4481
4792
|
}
|
|
4482
4793
|
isRevoked(path) {
|
|
@@ -5215,12 +5526,12 @@ function syncError(code, label) {
|
|
|
5215
5526
|
|
|
5216
5527
|
// src/realm/sync-fs-dispatch.ts
|
|
5217
5528
|
function toErrno(err) {
|
|
5218
|
-
const
|
|
5529
|
+
const message3 = err instanceof Error ? err.message : String(err);
|
|
5219
5530
|
const code = err?.code;
|
|
5220
5531
|
if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
|
|
5221
|
-
return { ok: false, errno: code, message:
|
|
5532
|
+
return { ok: false, errno: code, message: message3 };
|
|
5222
5533
|
}
|
|
5223
|
-
return { ok: false, errno: "EIO", message:
|
|
5534
|
+
return { ok: false, errno: "EIO", message: message3 };
|
|
5224
5535
|
}
|
|
5225
5536
|
function statJson(s) {
|
|
5226
5537
|
return {
|
|
@@ -5445,15 +5756,15 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
5445
5756
|
const pending = /* @__PURE__ */ new Map();
|
|
5446
5757
|
let disposed = false;
|
|
5447
5758
|
const { dispatch: dispatch2 } = opts;
|
|
5448
|
-
function drop(
|
|
5449
|
-
const entry = pending.get(
|
|
5759
|
+
function drop(id2) {
|
|
5760
|
+
const entry = pending.get(id2);
|
|
5450
5761
|
if (!entry) return;
|
|
5451
5762
|
clearTimeout(entry.timer);
|
|
5452
|
-
pending.delete(
|
|
5763
|
+
pending.delete(id2);
|
|
5453
5764
|
}
|
|
5454
|
-
function publish(
|
|
5455
|
-
if (Atomics.load(header3, SAB_I_REQ) !==
|
|
5456
|
-
drop(
|
|
5765
|
+
function publish(id2, entry, offset) {
|
|
5766
|
+
if (Atomics.load(header3, SAB_I_REQ) !== id2) {
|
|
5767
|
+
drop(id2);
|
|
5457
5768
|
return;
|
|
5458
5769
|
}
|
|
5459
5770
|
const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
|
|
@@ -5462,23 +5773,23 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
5462
5773
|
storeU53(header3, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
|
|
5463
5774
|
Atomics.store(header3, SAB_I_CHUNK, chunk2);
|
|
5464
5775
|
storeU53(header3, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
|
|
5465
|
-
Atomics.store(header3, SAB_I_SEQ,
|
|
5776
|
+
Atomics.store(header3, SAB_I_SEQ, id2);
|
|
5466
5777
|
Atomics.store(header3, SAB_I_STATE, SAB_STATE_READY);
|
|
5467
5778
|
Atomics.add(header3, SAB_I_PUB, 1);
|
|
5468
5779
|
Atomics.notify(header3, SAB_I_PUB);
|
|
5469
|
-
if (offset + chunk2 >= entry.payload.byteLength) drop(
|
|
5780
|
+
if (offset + chunk2 >= entry.payload.byteLength) drop(id2);
|
|
5470
5781
|
}
|
|
5471
|
-
function settle(
|
|
5782
|
+
function settle(id2, result) {
|
|
5472
5783
|
if (disposed) return;
|
|
5473
5784
|
const { status, payload } = encodeSabResult(result);
|
|
5474
|
-
drop(
|
|
5785
|
+
drop(id2);
|
|
5475
5786
|
const entry = {
|
|
5476
5787
|
status,
|
|
5477
5788
|
payload,
|
|
5478
|
-
timer: setTimeout(() => drop(
|
|
5789
|
+
timer: setTimeout(() => drop(id2), PENDING_TTL_MS)
|
|
5479
5790
|
};
|
|
5480
|
-
pending.set(
|
|
5481
|
-
publish(
|
|
5791
|
+
pending.set(id2, entry);
|
|
5792
|
+
publish(id2, entry, 0);
|
|
5482
5793
|
}
|
|
5483
5794
|
const handler = (event) => {
|
|
5484
5795
|
const data = event.data;
|
|
@@ -5494,14 +5805,14 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
5494
5805
|
return;
|
|
5495
5806
|
}
|
|
5496
5807
|
if (data.type !== SYNC_SAB_REQ_MSG || !data.req || typeof data.req !== "object") return;
|
|
5497
|
-
const
|
|
5808
|
+
const id2 = data.id;
|
|
5498
5809
|
const body = data.req;
|
|
5499
5810
|
const req = { ...body, token };
|
|
5500
5811
|
let dispatched;
|
|
5501
5812
|
try {
|
|
5502
5813
|
dispatched = dispatch2(req);
|
|
5503
5814
|
} catch (err) {
|
|
5504
|
-
settle(
|
|
5815
|
+
settle(id2, {
|
|
5505
5816
|
ok: false,
|
|
5506
5817
|
errno: "EIO",
|
|
5507
5818
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -5509,8 +5820,8 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
5509
5820
|
return;
|
|
5510
5821
|
}
|
|
5511
5822
|
void dispatched.then(
|
|
5512
|
-
(result) => settle(
|
|
5513
|
-
(err) => settle(
|
|
5823
|
+
(result) => settle(id2, result),
|
|
5824
|
+
(err) => settle(id2, {
|
|
5514
5825
|
ok: false,
|
|
5515
5826
|
errno: "EIO",
|
|
5516
5827
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -5523,7 +5834,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
5523
5834
|
if (disposed) return;
|
|
5524
5835
|
disposed = true;
|
|
5525
5836
|
port.removeEventListener("message", handler);
|
|
5526
|
-
for (const
|
|
5837
|
+
for (const id2 of [...pending.keys()]) drop(id2);
|
|
5527
5838
|
}
|
|
5528
5839
|
};
|
|
5529
5840
|
}
|
|
@@ -5583,47 +5894,47 @@ var AsyncOps = class {
|
|
|
5583
5894
|
this.onReady = onReady;
|
|
5584
5895
|
}
|
|
5585
5896
|
submit(req) {
|
|
5586
|
-
const
|
|
5897
|
+
const id2 = this.hold();
|
|
5587
5898
|
const running = new AbortController();
|
|
5588
|
-
this.running.set(
|
|
5899
|
+
this.running.set(id2, running);
|
|
5589
5900
|
this.run(req, running.signal).then(
|
|
5590
|
-
(result) => this.complete(
|
|
5591
|
-
(err) => this.complete(
|
|
5901
|
+
(result) => this.complete(id2, result),
|
|
5902
|
+
(err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
|
|
5592
5903
|
);
|
|
5593
|
-
return
|
|
5904
|
+
return id2;
|
|
5594
5905
|
}
|
|
5595
5906
|
resolve(value) {
|
|
5596
|
-
const
|
|
5597
|
-
this.complete(
|
|
5598
|
-
return
|
|
5907
|
+
const id2 = this.hold();
|
|
5908
|
+
this.complete(id2, json3(value));
|
|
5909
|
+
return id2;
|
|
5599
5910
|
}
|
|
5600
5911
|
hold() {
|
|
5601
|
-
const
|
|
5602
|
-
this.results.set(
|
|
5603
|
-
return
|
|
5912
|
+
const id2 = this.next++;
|
|
5913
|
+
this.results.set(id2, void 0);
|
|
5914
|
+
return id2;
|
|
5604
5915
|
}
|
|
5605
|
-
take(
|
|
5606
|
-
if (!this.results.has(
|
|
5607
|
-
const result = this.results.get(
|
|
5916
|
+
take(id2) {
|
|
5917
|
+
if (!this.results.has(id2)) throw new KernelError("EINVAL");
|
|
5918
|
+
const result = this.results.get(id2);
|
|
5608
5919
|
if (!result) throw new KernelError("EAGAIN");
|
|
5609
|
-
this.forget(
|
|
5920
|
+
this.forget(id2);
|
|
5610
5921
|
return result;
|
|
5611
5922
|
}
|
|
5612
|
-
cancel(
|
|
5613
|
-
this.running.get(
|
|
5614
|
-
this.forget(
|
|
5923
|
+
cancel(id2) {
|
|
5924
|
+
this.running.get(id2)?.abort();
|
|
5925
|
+
this.forget(id2);
|
|
5615
5926
|
}
|
|
5616
|
-
forget(
|
|
5617
|
-
this.running.delete(
|
|
5618
|
-
this.results.delete(
|
|
5619
|
-
const at = this.ready.indexOf(
|
|
5927
|
+
forget(id2) {
|
|
5928
|
+
this.running.delete(id2);
|
|
5929
|
+
this.results.delete(id2);
|
|
5930
|
+
const at = this.ready.indexOf(id2);
|
|
5620
5931
|
if (at >= 0) this.ready.splice(at, 1);
|
|
5621
5932
|
}
|
|
5622
5933
|
async wait(timeoutMs, signal) {
|
|
5623
5934
|
for (; ; ) {
|
|
5624
5935
|
if (this.closed) return -1;
|
|
5625
|
-
const
|
|
5626
|
-
if (
|
|
5936
|
+
const id2 = this.ready.shift();
|
|
5937
|
+
if (id2 !== void 0) return id2;
|
|
5627
5938
|
if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
|
|
5628
5939
|
const woke = await this.sleep(timeoutMs, signal);
|
|
5629
5940
|
if (!woke) return 0;
|
|
@@ -5659,11 +5970,11 @@ var AsyncOps = class {
|
|
|
5659
5970
|
}
|
|
5660
5971
|
});
|
|
5661
5972
|
}
|
|
5662
|
-
complete(
|
|
5663
|
-
this.running.delete(
|
|
5664
|
-
if (!this.results.has(
|
|
5665
|
-
this.results.set(
|
|
5666
|
-
this.ready.push(
|
|
5973
|
+
complete(id2, result) {
|
|
5974
|
+
this.running.delete(id2);
|
|
5975
|
+
if (!this.results.has(id2)) return;
|
|
5976
|
+
this.results.set(id2, result);
|
|
5977
|
+
this.ready.push(id2);
|
|
5667
5978
|
this.wake();
|
|
5668
5979
|
this.onReady?.();
|
|
5669
5980
|
}
|
|
@@ -5917,8 +6228,8 @@ function untilAborted(response, signal) {
|
|
|
5917
6228
|
response.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
5918
6229
|
});
|
|
5919
6230
|
}
|
|
5920
|
-
function plainResponse(status,
|
|
5921
|
-
const body = latin1Bytes(`slicc realm proxy: ${
|
|
6231
|
+
function plainResponse(status, message3) {
|
|
6232
|
+
const body = latin1Bytes(`slicc realm proxy: ${message3}
|
|
5922
6233
|
`);
|
|
5923
6234
|
return {
|
|
5924
6235
|
status,
|
|
@@ -6100,10 +6411,10 @@ var RealmProxy = class {
|
|
|
6100
6411
|
return await untilAborted(fetching, signal);
|
|
6101
6412
|
} catch (e) {
|
|
6102
6413
|
if (signal.aborted) throw e;
|
|
6103
|
-
const
|
|
6414
|
+
const message3 = e instanceof Error ? e.message : String(e);
|
|
6104
6415
|
const status = e?.status;
|
|
6105
6416
|
const own = typeof status === "number" && status >= 400 && status <= 599 ? status : 502;
|
|
6106
|
-
return plainResponse(own,
|
|
6417
|
+
return plainResponse(own, message3 || "upstream request failed");
|
|
6107
6418
|
}
|
|
6108
6419
|
}
|
|
6109
6420
|
async relay(conn, method, minor, response, keepAlive) {
|
|
@@ -6340,8 +6651,8 @@ function name(dn) {
|
|
|
6340
6651
|
rdns.push(set(seq(oid("2.5.4.3"), utf8(dn.commonName))));
|
|
6341
6652
|
return seq(...rdns);
|
|
6342
6653
|
}
|
|
6343
|
-
function extension(
|
|
6344
|
-
return critical ? seq(oid(
|
|
6654
|
+
function extension(id2, critical, value) {
|
|
6655
|
+
return critical ? seq(oid(id2), TRUE, octets(value)) : seq(oid(id2), octets(value));
|
|
6345
6656
|
}
|
|
6346
6657
|
function children(der) {
|
|
6347
6658
|
const out = [];
|
|
@@ -6457,9 +6768,9 @@ function pem(der) {
|
|
|
6457
6768
|
for (let i = 0; i < der.length; i += 32768) {
|
|
6458
6769
|
b64 += String.fromCharCode(...der.subarray(i, i + 32768));
|
|
6459
6770
|
}
|
|
6460
|
-
const
|
|
6771
|
+
const lines2 = btoa(b64).match(/.{1,64}/g) ?? [];
|
|
6461
6772
|
return `-----BEGIN CERTIFICATE-----
|
|
6462
|
-
${
|
|
6773
|
+
${lines2.join("\n")}
|
|
6463
6774
|
-----END CERTIFICATE-----
|
|
6464
6775
|
`;
|
|
6465
6776
|
}
|
|
@@ -6499,11 +6810,11 @@ var RealmCa = class _RealmCa {
|
|
|
6499
6810
|
static async create(owner, now) {
|
|
6500
6811
|
const pair = await crypto.subtle.generateKey(ECDSA, false, ["sign", "verify"]);
|
|
6501
6812
|
const spki = new Uint8Array(await crypto.subtle.exportKey("spki", pair.publicKey));
|
|
6502
|
-
const
|
|
6813
|
+
const id2 = Array.from(
|
|
6503
6814
|
crypto.getRandomValues(new Uint8Array(4)),
|
|
6504
6815
|
(b) => b.toString(16).padStart(2, "0")
|
|
6505
6816
|
).join("");
|
|
6506
|
-
const name2 = { organization: "SLICC", commonName: `SLICC realm CA ${owner} ${
|
|
6817
|
+
const name2 = { organization: "SLICC", commonName: `SLICC realm CA ${owner} ${id2}` };
|
|
6507
6818
|
const notAfter = now + CA_LIFETIME;
|
|
6508
6819
|
const cert = await certificate({
|
|
6509
6820
|
serial: randomSerial(),
|
|
@@ -6564,8 +6875,8 @@ var SCRATCH = 64 * 1024;
|
|
|
6564
6875
|
var EOF = -1;
|
|
6565
6876
|
var TlsError = class extends Error {
|
|
6566
6877
|
code;
|
|
6567
|
-
constructor(code,
|
|
6568
|
-
super(
|
|
6878
|
+
constructor(code, message3) {
|
|
6879
|
+
super(message3);
|
|
6569
6880
|
this.code = code;
|
|
6570
6881
|
}
|
|
6571
6882
|
};
|
|
@@ -6946,10 +7257,133 @@ function enableNetwork(net, options) {
|
|
|
6946
7257
|
return () => proxy;
|
|
6947
7258
|
}
|
|
6948
7259
|
|
|
7260
|
+
// src/kernel/net/ws-syscalls.ts
|
|
7261
|
+
var WS_OPS = [
|
|
7262
|
+
"net-ws-open",
|
|
7263
|
+
"net-ws-ready",
|
|
7264
|
+
"net-ws-recv",
|
|
7265
|
+
"net-ws-wait",
|
|
7266
|
+
"net-ws-send",
|
|
7267
|
+
"net-ws-close"
|
|
7268
|
+
];
|
|
7269
|
+
var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
7270
|
+
function message(e) {
|
|
7271
|
+
return e instanceof Error ? e.message : String(e);
|
|
7272
|
+
}
|
|
7273
|
+
function fail(errno, text) {
|
|
7274
|
+
return { ok: false, errno, message: text };
|
|
7275
|
+
}
|
|
7276
|
+
var WsHandles = class {
|
|
7277
|
+
transport;
|
|
7278
|
+
open = /* @__PURE__ */ new Map();
|
|
7279
|
+
next;
|
|
7280
|
+
constructor(transport, next2) {
|
|
7281
|
+
this.transport = transport;
|
|
7282
|
+
this.next = next2;
|
|
7283
|
+
}
|
|
7284
|
+
owns(handle) {
|
|
7285
|
+
return this.open.has(handle);
|
|
7286
|
+
}
|
|
7287
|
+
syscall(req) {
|
|
7288
|
+
if (req.op === "net-ws-open") return Promise.resolve(this.start(req));
|
|
7289
|
+
const entry = this.open.get(req.handle);
|
|
7290
|
+
if (!entry) return Promise.resolve(fail("EBADF", `no websocket ${req.handle}`));
|
|
7291
|
+
if (req.op === "net-ws-ready") return entry.ready;
|
|
7292
|
+
if (!entry.socket) return Promise.resolve(fail("ENOTCONN", "websocket not open"));
|
|
7293
|
+
if (req.op === "net-ws-recv") return this.recv(entry.socket, entry);
|
|
7294
|
+
if (req.op === "net-ws-wait") return this.closed(entry.socket);
|
|
7295
|
+
if (req.op === "net-ws-send") return Promise.resolve(this.send(entry.socket, req, entry));
|
|
7296
|
+
entry.ended = true;
|
|
7297
|
+
entry.socket.close(req.code ?? 1e3, req.reason ?? "");
|
|
7298
|
+
return Promise.resolve({ ok: true, kind: "void" });
|
|
7299
|
+
}
|
|
7300
|
+
start(req) {
|
|
7301
|
+
const handle = this.next();
|
|
7302
|
+
const entry = {
|
|
7303
|
+
abort: new AbortController(),
|
|
7304
|
+
ready: Promise.resolve({ ok: true, kind: "void" })
|
|
7305
|
+
};
|
|
7306
|
+
this.open.set(handle, entry);
|
|
7307
|
+
entry.ready = this.connect(handle, req, entry);
|
|
7308
|
+
return { ok: true, kind: "json", json: handle };
|
|
7309
|
+
}
|
|
7310
|
+
async connect(handle, req, entry) {
|
|
7311
|
+
const { transport } = this;
|
|
7312
|
+
if (!transport.traits.websocket || !transport.websocket) return fail("ENOTSUP", NO_WEBSOCKET2);
|
|
7313
|
+
let socket;
|
|
7314
|
+
try {
|
|
7315
|
+
socket = await transport.websocket({
|
|
7316
|
+
url: req.url,
|
|
7317
|
+
protocols: req.protocols,
|
|
7318
|
+
headers: req.headers,
|
|
7319
|
+
signal: entry.abort.signal
|
|
7320
|
+
});
|
|
7321
|
+
} catch (e) {
|
|
7322
|
+
return fail("ECONNREFUSED", message(e));
|
|
7323
|
+
}
|
|
7324
|
+
if (!this.open.has(handle)) {
|
|
7325
|
+
socket.close(1001, "the program went away");
|
|
7326
|
+
return fail("EBADF", `no websocket ${handle}`);
|
|
7327
|
+
}
|
|
7328
|
+
if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN3.test(socket.protocol))) {
|
|
7329
|
+
socket.close(1002, "subprotocol not offered");
|
|
7330
|
+
return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
|
|
7331
|
+
}
|
|
7332
|
+
entry.socket = socket;
|
|
7333
|
+
void socket.closed.then(() => {
|
|
7334
|
+
entry.ended = true;
|
|
7335
|
+
});
|
|
7336
|
+
entry.messages = socket.messages[Symbol.asyncIterator]();
|
|
7337
|
+
return { ok: true, kind: "json", json: { protocol: socket.protocol } };
|
|
7338
|
+
}
|
|
7339
|
+
async recv(socket, entry) {
|
|
7340
|
+
try {
|
|
7341
|
+
const next2 = await entry.messages.next();
|
|
7342
|
+
if (!next2.done) {
|
|
7343
|
+
return typeof next2.value === "string" ? { ok: true, kind: "json", json: { text: next2.value } } : { ok: true, kind: "bytes", bytes: next2.value };
|
|
7344
|
+
}
|
|
7345
|
+
const closed = await socket.closed;
|
|
7346
|
+
return { ok: true, kind: "json", json: { code: closed.code, reason: closed.reason } };
|
|
7347
|
+
} catch (e) {
|
|
7348
|
+
return fail("ECONNRESET", message(e));
|
|
7349
|
+
}
|
|
7350
|
+
}
|
|
7351
|
+
async closed(socket) {
|
|
7352
|
+
const { code, reason } = await socket.closed;
|
|
7353
|
+
return { ok: true, kind: "json", json: { code, reason } };
|
|
7354
|
+
}
|
|
7355
|
+
send(socket, req, entry) {
|
|
7356
|
+
const data = req.text ?? req.body;
|
|
7357
|
+
if (entry.ended) return fail("EPIPE", "the websocket is closed");
|
|
7358
|
+
try {
|
|
7359
|
+
if (data !== void 0) socket.send(data);
|
|
7360
|
+
} catch (e) {
|
|
7361
|
+
return fail("EPIPE", message(e));
|
|
7362
|
+
}
|
|
7363
|
+
return { ok: true, kind: "json", json: socket.buffered };
|
|
7364
|
+
}
|
|
7365
|
+
close(handle) {
|
|
7366
|
+
const entry = this.open.get(handle);
|
|
7367
|
+
if (!entry) return;
|
|
7368
|
+
this.open.delete(handle);
|
|
7369
|
+
entry.abort.abort();
|
|
7370
|
+
entry.socket?.close(1001, "the program went away");
|
|
7371
|
+
}
|
|
7372
|
+
closeAll() {
|
|
7373
|
+
for (const handle of [...this.open.keys()]) this.close(handle);
|
|
7374
|
+
}
|
|
7375
|
+
};
|
|
7376
|
+
|
|
6949
7377
|
// src/kernel/net/http-syscalls.ts
|
|
6950
|
-
var HTTP_OPS = [
|
|
7378
|
+
var HTTP_OPS = [
|
|
7379
|
+
"net-request",
|
|
7380
|
+
"net-read",
|
|
7381
|
+
"net-close",
|
|
7382
|
+
"net-traits",
|
|
7383
|
+
...WS_OPS
|
|
7384
|
+
];
|
|
6951
7385
|
var EMPTY = new Uint8Array(0);
|
|
6952
|
-
function
|
|
7386
|
+
function message2(e) {
|
|
6953
7387
|
return e instanceof Error ? e.message : String(e);
|
|
6954
7388
|
}
|
|
6955
7389
|
async function* once(bytes) {
|
|
@@ -6959,10 +7393,13 @@ var HttpHandles = class {
|
|
|
6959
7393
|
transport;
|
|
6960
7394
|
open = /* @__PURE__ */ new Map();
|
|
6961
7395
|
nextHandle = 0;
|
|
7396
|
+
ws;
|
|
6962
7397
|
constructor(transport) {
|
|
6963
7398
|
this.transport = transport;
|
|
7399
|
+
this.ws = new WsHandles(transport, () => ++this.nextHandle);
|
|
6964
7400
|
}
|
|
6965
7401
|
syscall(req) {
|
|
7402
|
+
if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
|
|
6966
7403
|
if (req.op === "net-request") return this.request(req);
|
|
6967
7404
|
if (req.op === "net-read") return this.read(req.handle, req.max);
|
|
6968
7405
|
if (req.op === "net-traits") return Promise.resolve(this.traits());
|
|
@@ -7005,7 +7442,7 @@ var HttpHandles = class {
|
|
|
7005
7442
|
};
|
|
7006
7443
|
return { ok: true, kind: "json", json: head };
|
|
7007
7444
|
} catch (e) {
|
|
7008
|
-
const text =
|
|
7445
|
+
const text = message2(e);
|
|
7009
7446
|
if (text === NO_TRANSPORT) return { ok: false, errno: "ENETUNREACH", message: text };
|
|
7010
7447
|
const status = e.status;
|
|
7011
7448
|
if (typeof status !== "number" || status === 502) {
|
|
@@ -7039,7 +7476,7 @@ var HttpHandles = class {
|
|
|
7039
7476
|
} catch (e) {
|
|
7040
7477
|
entry.done = true;
|
|
7041
7478
|
const timedOut = e?.code === "ETIMEDOUT";
|
|
7042
|
-
return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message:
|
|
7479
|
+
return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message: message2(e) };
|
|
7043
7480
|
}
|
|
7044
7481
|
}
|
|
7045
7482
|
const bytes = entry.left.subarray(0, Math.max(0, max));
|
|
@@ -7047,6 +7484,10 @@ var HttpHandles = class {
|
|
|
7047
7484
|
return { ok: true, kind: "bytes", bytes };
|
|
7048
7485
|
}
|
|
7049
7486
|
async close(handle) {
|
|
7487
|
+
if (this.ws.owns(handle)) {
|
|
7488
|
+
this.ws.close(handle);
|
|
7489
|
+
return { ok: true, kind: "void" };
|
|
7490
|
+
}
|
|
7050
7491
|
const entry = this.open.get(handle);
|
|
7051
7492
|
if (!entry) return { ok: false, errno: "EBADF", message: `no request ${handle}` };
|
|
7052
7493
|
this.open.delete(handle);
|
|
@@ -7055,6 +7496,7 @@ var HttpHandles = class {
|
|
|
7055
7496
|
return { ok: true, kind: "void" };
|
|
7056
7497
|
}
|
|
7057
7498
|
async closeAll() {
|
|
7499
|
+
this.ws.closeAll();
|
|
7058
7500
|
await Promise.all([...this.open.keys()].map((handle) => this.close(handle)));
|
|
7059
7501
|
}
|
|
7060
7502
|
};
|
|
@@ -7094,12 +7536,12 @@ function parsePrefix(text) {
|
|
|
7094
7536
|
const parts = text.trim().split("/");
|
|
7095
7537
|
if (parts.length > 2) return void 0;
|
|
7096
7538
|
const [address = "", size] = parts;
|
|
7097
|
-
const
|
|
7098
|
-
if (!
|
|
7099
|
-
const max =
|
|
7539
|
+
const parsed2 = addressBits(address);
|
|
7540
|
+
if (!parsed2) return void 0;
|
|
7541
|
+
const max = parsed2.v6 ? 128 : 32;
|
|
7100
7542
|
const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
7101
7543
|
if (length2 < 0 || length2 > max) return void 0;
|
|
7102
|
-
return { ...
|
|
7544
|
+
return { ...parsed2, length: length2 };
|
|
7103
7545
|
}
|
|
7104
7546
|
function within4(prefix, addr) {
|
|
7105
7547
|
if (prefix.v6 !== addr.v6) return false;
|
|
@@ -7125,10 +7567,10 @@ var Routes = class {
|
|
|
7125
7567
|
this.ipv6 = ipv62;
|
|
7126
7568
|
}
|
|
7127
7569
|
set(table) {
|
|
7128
|
-
const
|
|
7129
|
-
const bad = (table.prefixes ?? []).find((_, i) => !
|
|
7570
|
+
const parsed2 = (table.prefixes ?? []).map(parsePrefix);
|
|
7571
|
+
const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
|
|
7130
7572
|
if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
|
|
7131
|
-
this.table =
|
|
7573
|
+
this.table = parsed2;
|
|
7132
7574
|
this.exit = table.exit === true;
|
|
7133
7575
|
}
|
|
7134
7576
|
classify(host) {
|
|
@@ -7187,7 +7629,7 @@ var Resolver = class {
|
|
|
7187
7629
|
this.routes = options.routes;
|
|
7188
7630
|
this.now = options.now ?? Date.now;
|
|
7189
7631
|
this.timeoutMs = options.timeoutMs ?? RESOLVE_TIMEOUT_MS;
|
|
7190
|
-
this.log = options.log ?? ((
|
|
7632
|
+
this.log = options.log ?? ((message3) => console.warn(message3));
|
|
7191
7633
|
this.own = ownNames(options.hostname ?? DEFAULT_HOSTNAME);
|
|
7192
7634
|
}
|
|
7193
7635
|
async resolve(name2, family, blocking) {
|
|
@@ -7204,21 +7646,21 @@ var Resolver = class {
|
|
|
7204
7646
|
if (!this.uplink || key === "") return [];
|
|
7205
7647
|
const asked = this.uplink.traits.ipv6 ? family : 4;
|
|
7206
7648
|
if (family === 6 && asked !== 6) return [];
|
|
7207
|
-
const
|
|
7208
|
-
const hit = this.cache.get(
|
|
7649
|
+
const id2 = `${asked}:${key}`;
|
|
7650
|
+
const hit = this.cache.get(id2);
|
|
7209
7651
|
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
7210
7652
|
const signal = blocking?.();
|
|
7211
7653
|
if (signal?.aborted) throw new KernelError("EINTR");
|
|
7212
|
-
let lookup = this.pending.get(
|
|
7654
|
+
let lookup = this.pending.get(id2);
|
|
7213
7655
|
if (!lookup) {
|
|
7214
7656
|
const abort = new AbortController();
|
|
7215
|
-
lookup = { work: this.lookup(key, asked,
|
|
7216
|
-
this.pending.set(
|
|
7217
|
-
void lookup.work.then(() => this.pending.delete(
|
|
7657
|
+
lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
|
|
7658
|
+
this.pending.set(id2, lookup);
|
|
7659
|
+
void lookup.work.then(() => this.pending.delete(id2));
|
|
7218
7660
|
}
|
|
7219
7661
|
return waitFor(lookup, signal);
|
|
7220
7662
|
}
|
|
7221
|
-
async lookup(name2, family,
|
|
7663
|
+
async lookup(name2, family, id2, abandoned) {
|
|
7222
7664
|
const uplink = this.uplink;
|
|
7223
7665
|
const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
|
|
7224
7666
|
let answer;
|
|
@@ -7235,7 +7677,7 @@ var Resolver = class {
|
|
|
7235
7677
|
return [];
|
|
7236
7678
|
}
|
|
7237
7679
|
const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
|
|
7238
|
-
if (addresses.length > 0) this.remember(
|
|
7680
|
+
if (addresses.length > 0) this.remember(id2, addresses, answer.ttl);
|
|
7239
7681
|
return addresses;
|
|
7240
7682
|
}
|
|
7241
7683
|
usable(name2, address, family) {
|
|
@@ -7247,11 +7689,11 @@ var Resolver = class {
|
|
|
7247
7689
|
this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
|
|
7248
7690
|
return false;
|
|
7249
7691
|
}
|
|
7250
|
-
remember(
|
|
7692
|
+
remember(id2, addresses, ttl) {
|
|
7251
7693
|
const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
|
|
7252
7694
|
if (seconds <= 0) return;
|
|
7253
|
-
this.cache.delete(
|
|
7254
|
-
this.cache.set(
|
|
7695
|
+
this.cache.delete(id2);
|
|
7696
|
+
this.cache.set(id2, { addresses, expires: this.now() + seconds * 1e3 });
|
|
7255
7697
|
if (this.cache.size > RESOLVE_CACHE_MAX) {
|
|
7256
7698
|
this.cache.delete(this.cache.keys().next().value);
|
|
7257
7699
|
}
|
|
@@ -7747,10 +8189,10 @@ function ready(fds, read, write) {
|
|
|
7747
8189
|
}
|
|
7748
8190
|
function interruption(signal) {
|
|
7749
8191
|
const { promise, reject } = Promise.withResolvers();
|
|
7750
|
-
const
|
|
7751
|
-
if (signal.aborted)
|
|
7752
|
-
else signal.addEventListener("abort",
|
|
7753
|
-
return { promise, done: () => signal.removeEventListener("abort",
|
|
8192
|
+
const fail3 = () => reject(new KernelError("EINTR"));
|
|
8193
|
+
if (signal.aborted) fail3();
|
|
8194
|
+
else signal.addEventListener("abort", fail3, { once: true });
|
|
8195
|
+
return { promise, done: () => signal.removeEventListener("abort", fail3) };
|
|
7754
8196
|
}
|
|
7755
8197
|
async function selectFds(fds, read, write, timeoutMs, interrupt) {
|
|
7756
8198
|
const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
|
|
@@ -8693,6 +9135,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
8693
9135
|
"fd-list",
|
|
8694
9136
|
"proc-alarm",
|
|
8695
9137
|
"proc-umask",
|
|
9138
|
+
"proc-cred",
|
|
9139
|
+
"proc-setcred",
|
|
8696
9140
|
"dl-log",
|
|
8697
9141
|
"fd-renumber",
|
|
8698
9142
|
"fd-promote",
|
|
@@ -8743,6 +9187,7 @@ var WasmProcess = class {
|
|
|
8743
9187
|
interrupt = new AbortController();
|
|
8744
9188
|
dying = false;
|
|
8745
9189
|
umask = DEFAULT_UMASK;
|
|
9190
|
+
cred;
|
|
8746
9191
|
execChild;
|
|
8747
9192
|
killed = false;
|
|
8748
9193
|
execTermsig;
|
|
@@ -8762,6 +9207,7 @@ var WasmProcess = class {
|
|
|
8762
9207
|
this.options = options;
|
|
8763
9208
|
this.inherited = options.ignored ?? 0;
|
|
8764
9209
|
this.umask = options.umask ?? DEFAULT_UMASK;
|
|
9210
|
+
this.cred = copyCred(options.cred ?? ROOT);
|
|
8765
9211
|
this.children = new ChildTable(fds, options.spawner, options.forker);
|
|
8766
9212
|
this.nodes = options.nodes ?? new VfsNodes(options.fs);
|
|
8767
9213
|
if (!options.nodes) options.openFiles?.add(this.nodes);
|
|
@@ -9278,6 +9724,11 @@ var WasmProcess = class {
|
|
|
9278
9724
|
return { ok: true, kind: "void" };
|
|
9279
9725
|
case "proc-umask":
|
|
9280
9726
|
return { ok: true, kind: "json", json: this.setUmask(req.mask) };
|
|
9727
|
+
case "proc-cred":
|
|
9728
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9729
|
+
case "proc-setcred":
|
|
9730
|
+
this.cred = changeCred(this.cred, req.change);
|
|
9731
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9281
9732
|
case "dl-log":
|
|
9282
9733
|
if (req.append) this.dlLog.push(req.append);
|
|
9283
9734
|
return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
|
|
@@ -9372,9 +9823,9 @@ var KILL_GRACE_MS = 2e3;
|
|
|
9372
9823
|
var descIds = /* @__PURE__ */ new WeakMap();
|
|
9373
9824
|
var nextDescId = 1;
|
|
9374
9825
|
function descId(file) {
|
|
9375
|
-
const
|
|
9376
|
-
descIds.set(file,
|
|
9377
|
-
return
|
|
9826
|
+
const id2 = descIds.get(file) ?? nextDescId++;
|
|
9827
|
+
descIds.set(file, id2);
|
|
9828
|
+
return id2;
|
|
9378
9829
|
}
|
|
9379
9830
|
function inheritedFds(fds) {
|
|
9380
9831
|
return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
|
|
@@ -9491,7 +9942,8 @@ function processInit(opts, sab, umask, lane) {
|
|
|
9491
9942
|
function inherited(opts) {
|
|
9492
9943
|
return {
|
|
9493
9944
|
...opts.ignored ? { ignored: opts.ignored } : {},
|
|
9494
|
-
...opts.umask !== void 0 ? { umask: opts.umask } : {}
|
|
9945
|
+
...opts.umask !== void 0 ? { umask: opts.umask } : {},
|
|
9946
|
+
...opts.cred ? { cred: opts.cred } : {}
|
|
9495
9947
|
};
|
|
9496
9948
|
}
|
|
9497
9949
|
function holdingToken(opts, holds) {
|
|
@@ -9655,6 +10107,7 @@ function spawnWasmProcess(opts) {
|
|
|
9655
10107
|
ignoredSignals: (fork) => process2.inheritable(fork),
|
|
9656
10108
|
signalMasks: () => process2.signalMasks(),
|
|
9657
10109
|
umask: () => process2.umask,
|
|
10110
|
+
cred: () => copyCred(process2.cred),
|
|
9658
10111
|
writesBack: () => opts.program.abi === "wasi" && !done4
|
|
9659
10112
|
};
|
|
9660
10113
|
}
|
|
@@ -9713,6 +10166,9 @@ var JobTable = class {
|
|
|
9713
10166
|
pgidOf(pid) {
|
|
9714
10167
|
return this.members.get(pid)?.pgid;
|
|
9715
10168
|
}
|
|
10169
|
+
sidOf(pid) {
|
|
10170
|
+
return this.members.get(pid)?.sid;
|
|
10171
|
+
}
|
|
9716
10172
|
member(pid) {
|
|
9717
10173
|
const member = this.members.get(pid);
|
|
9718
10174
|
if (!member) throw new KernelError("ESRCH");
|
|
@@ -9721,27 +10177,27 @@ var JobTable = class {
|
|
|
9721
10177
|
idOf(pid) {
|
|
9722
10178
|
return this.members.get(pid)?.shownAs ?? pid;
|
|
9723
10179
|
}
|
|
9724
|
-
byId(
|
|
9725
|
-
for (const member of this.members.values()) if (member.shownAs ===
|
|
9726
|
-
return this.member(
|
|
10180
|
+
byId(id2) {
|
|
10181
|
+
for (const member of this.members.values()) if (member.shownAs === id2) return member;
|
|
10182
|
+
return this.member(id2);
|
|
9727
10183
|
}
|
|
9728
10184
|
setpgid(caller, pid, pgid) {
|
|
9729
10185
|
const me = this.idOf(caller);
|
|
9730
10186
|
const target = this.byId(pid || me);
|
|
9731
|
-
const
|
|
9732
|
-
const group = pgid ||
|
|
10187
|
+
const id2 = this.idOf(target.pid);
|
|
10188
|
+
const group = pgid || id2;
|
|
9733
10189
|
if (group < 0) throw new KernelError("EINVAL");
|
|
9734
10190
|
const self = this.member(caller);
|
|
9735
|
-
if (
|
|
10191
|
+
if (id2 !== me) {
|
|
9736
10192
|
if (target.ppid === void 0 || this.idOf(target.ppid) !== me) {
|
|
9737
10193
|
throw new KernelError("ESRCH");
|
|
9738
10194
|
}
|
|
9739
10195
|
if (target.execed) throw new KernelError("EACCES");
|
|
9740
10196
|
}
|
|
9741
10197
|
if (target.sid !== self.sid) throw new KernelError("EPERM");
|
|
9742
|
-
if (
|
|
10198
|
+
if (id2 === target.sid) throw new KernelError("EPERM");
|
|
9743
10199
|
const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
|
|
9744
|
-
if (group !==
|
|
10200
|
+
if (group !== id2 && !exists) throw new KernelError("EPERM");
|
|
9745
10201
|
target.pgid = group;
|
|
9746
10202
|
}
|
|
9747
10203
|
getpgid(caller, pid) {
|
|
@@ -9752,12 +10208,12 @@ var JobTable = class {
|
|
|
9752
10208
|
}
|
|
9753
10209
|
setsid(pid) {
|
|
9754
10210
|
const member = this.member(pid);
|
|
9755
|
-
const
|
|
9756
|
-
if (member.pgid ===
|
|
9757
|
-
member.sid =
|
|
9758
|
-
member.pgid =
|
|
9759
|
-
this.terminals.delete(
|
|
9760
|
-
return
|
|
10211
|
+
const id2 = this.idOf(pid);
|
|
10212
|
+
if (member.pgid === id2) throw new KernelError("EPERM");
|
|
10213
|
+
member.sid = id2;
|
|
10214
|
+
member.pgid = id2;
|
|
10215
|
+
this.terminals.delete(id2);
|
|
10216
|
+
return id2;
|
|
9761
10217
|
}
|
|
9762
10218
|
terminalNamed(name2) {
|
|
9763
10219
|
return this.knownTerminals().find((tty) => tty.name === name2);
|
|
@@ -9774,15 +10230,17 @@ var JobTable = class {
|
|
|
9774
10230
|
}
|
|
9775
10231
|
acquireTerminal(pid, tty) {
|
|
9776
10232
|
const member = this.members.get(pid);
|
|
9777
|
-
const
|
|
9778
|
-
if (!member || member.sid !==
|
|
10233
|
+
const id2 = this.idOf(pid);
|
|
10234
|
+
if (!member || member.sid !== id2 || this.terminals.has(id2)) return false;
|
|
9779
10235
|
if ([...this.terminals.values()].includes(tty)) return false;
|
|
9780
|
-
this.terminals.set(
|
|
10236
|
+
this.terminals.set(id2, tty);
|
|
9781
10237
|
this.foreground.set(tty, member.pgid);
|
|
9782
10238
|
return true;
|
|
9783
10239
|
}
|
|
9784
|
-
killGroup(pgid, sig) {
|
|
9785
|
-
const
|
|
10240
|
+
killGroup(pgid, sig, may = () => true) {
|
|
10241
|
+
const group = [...this.members.values()].filter((m) => m.pgid === pgid);
|
|
10242
|
+
const targets = group.filter((m) => may(m.pid));
|
|
10243
|
+
if (targets.length === 0 && group.length > 0) throw new KernelError("EPERM");
|
|
9786
10244
|
const pids = new Set(targets.map((m) => m.pid));
|
|
9787
10245
|
if (sig !== 0) {
|
|
9788
10246
|
for (const m of targets)
|
|
@@ -9883,8 +10341,8 @@ var SettlingChildren = class {
|
|
|
9883
10341
|
};
|
|
9884
10342
|
|
|
9885
10343
|
// src/mount/driver.ts
|
|
9886
|
-
function fsError2(code,
|
|
9887
|
-
return Object.assign(new Error(
|
|
10344
|
+
function fsError2(code, message3 = code) {
|
|
10345
|
+
return Object.assign(new Error(message3), { code });
|
|
9888
10346
|
}
|
|
9889
10347
|
async function dispatch(h, req) {
|
|
9890
10348
|
switch (req.op) {
|
|
@@ -9934,15 +10392,15 @@ async function dispatch(h, req) {
|
|
|
9934
10392
|
}
|
|
9935
10393
|
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
9936
10394
|
let open = false;
|
|
9937
|
-
const reply = (
|
|
9938
|
-
async function hello(
|
|
9939
|
-
const refused = driverVersionError(
|
|
10395
|
+
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
10396
|
+
async function hello(message3) {
|
|
10397
|
+
const refused = driverVersionError(message3.protocol);
|
|
9940
10398
|
if (refused) {
|
|
9941
10399
|
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: "EPROTO" } });
|
|
9942
10400
|
return;
|
|
9943
10401
|
}
|
|
9944
10402
|
try {
|
|
9945
|
-
const refined = await handlers.mount?.(
|
|
10403
|
+
const refined = await handlers.mount?.(message3.mount);
|
|
9946
10404
|
open = true;
|
|
9947
10405
|
reply({
|
|
9948
10406
|
hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } }
|
|
@@ -10244,7 +10702,7 @@ function unescape(field2) {
|
|
|
10244
10702
|
);
|
|
10245
10703
|
}
|
|
10246
10704
|
function parseFstab(text) {
|
|
10247
|
-
const
|
|
10705
|
+
const lines2 = [];
|
|
10248
10706
|
text.split("\n").forEach((raw, index) => {
|
|
10249
10707
|
const content = raw.replace(/#.*$/, "").trim();
|
|
10250
10708
|
if (!content) return;
|
|
@@ -10253,12 +10711,12 @@ function parseFstab(text) {
|
|
|
10253
10711
|
if (options.split(",").includes("noauto")) return;
|
|
10254
10712
|
const spec = { source, target, type, options: {} };
|
|
10255
10713
|
try {
|
|
10256
|
-
|
|
10714
|
+
lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
10257
10715
|
} catch (err) {
|
|
10258
|
-
|
|
10716
|
+
lines2.push({ line: index + 1, spec, invalid: err });
|
|
10259
10717
|
}
|
|
10260
10718
|
});
|
|
10261
|
-
return
|
|
10719
|
+
return lines2;
|
|
10262
10720
|
}
|
|
10263
10721
|
function codeOf3(err) {
|
|
10264
10722
|
const code = err?.code;
|
|
@@ -10276,11 +10734,11 @@ function wait(ms, signal) {
|
|
|
10276
10734
|
signal.addEventListener("abort", done4, { once: true });
|
|
10277
10735
|
});
|
|
10278
10736
|
}
|
|
10279
|
-
async function mountFstab(
|
|
10737
|
+
async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
10280
10738
|
}, settled = () => {
|
|
10281
10739
|
}) {
|
|
10282
10740
|
return Promise.all(
|
|
10283
|
-
|
|
10741
|
+
lines2.map(async (each) => {
|
|
10284
10742
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
10285
10743
|
settled(each, result);
|
|
10286
10744
|
return result;
|
|
@@ -10691,8 +11149,8 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
10691
11149
|
function memoryMedia() {
|
|
10692
11150
|
const handles = /* @__PURE__ */ new Map();
|
|
10693
11151
|
return {
|
|
10694
|
-
get: async (
|
|
10695
|
-
put: async (
|
|
11152
|
+
get: async (id2) => handles.get(id2),
|
|
11153
|
+
put: async (id2, handle) => void handles.set(id2, handle)
|
|
10696
11154
|
};
|
|
10697
11155
|
}
|
|
10698
11156
|
|
|
@@ -10936,10 +11394,10 @@ function importedMemory(bytes) {
|
|
|
10936
11394
|
return { initial, maximum, shared: (flags & 2) !== 0 };
|
|
10937
11395
|
};
|
|
10938
11396
|
while (at < bytes.length) {
|
|
10939
|
-
const
|
|
11397
|
+
const id2 = bytes[at++];
|
|
10940
11398
|
const size = u32();
|
|
10941
11399
|
const end = at + size;
|
|
10942
|
-
if (
|
|
11400
|
+
if (id2 !== 2) {
|
|
10943
11401
|
at = end;
|
|
10944
11402
|
continue;
|
|
10945
11403
|
}
|
|
@@ -11059,18 +11517,17 @@ function foreignImports(bytes) {
|
|
|
11059
11517
|
const r = new Reader(bytes);
|
|
11060
11518
|
let types = [];
|
|
11061
11519
|
while (r.at < bytes.length) {
|
|
11062
|
-
const
|
|
11520
|
+
const id2 = r.byte();
|
|
11063
11521
|
const end = r.u32() + r.at;
|
|
11064
|
-
if (
|
|
11522
|
+
if (id2 === 1) types = readTypes(r);
|
|
11065
11523
|
if (!types) return void 0;
|
|
11066
|
-
if (
|
|
11524
|
+
if (id2 === 2) return readImports(r, types);
|
|
11067
11525
|
r.at = end;
|
|
11068
11526
|
}
|
|
11069
11527
|
return {};
|
|
11070
11528
|
}
|
|
11071
11529
|
|
|
11072
11530
|
// src/launcher.ts
|
|
11073
|
-
var PNPM_HOME = "/home/.local/share/pnpm";
|
|
11074
11531
|
var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
11075
11532
|
var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
11076
11533
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -11170,6 +11627,8 @@ var Launcher = class {
|
|
|
11170
11627
|
zombies = /* @__PURE__ */ new Set();
|
|
11171
11628
|
orphans = /* @__PURE__ */ new Set();
|
|
11172
11629
|
described = /* @__PURE__ */ new Map();
|
|
11630
|
+
creds = /* @__PURE__ */ new Map();
|
|
11631
|
+
users;
|
|
11173
11632
|
jobs = new JobTable();
|
|
11174
11633
|
locks = new LockTable();
|
|
11175
11634
|
settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
|
|
@@ -11221,7 +11680,7 @@ var Launcher = class {
|
|
|
11221
11680
|
this.hostfs = options;
|
|
11222
11681
|
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
11223
11682
|
this.processMounts = options.processMounts ?? true;
|
|
11224
|
-
this.pnpmHome = options.env?.PNPM_HOME ??
|
|
11683
|
+
this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
|
|
11225
11684
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
11226
11685
|
this.cdp = new CdpHosts(options.cdp);
|
|
11227
11686
|
const hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
@@ -11257,6 +11716,7 @@ var Launcher = class {
|
|
|
11257
11716
|
}
|
|
11258
11717
|
);
|
|
11259
11718
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
11719
|
+
this.users = new UserDb(this.fs);
|
|
11260
11720
|
void this.unlinked.sweep("/");
|
|
11261
11721
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
11262
11722
|
this.catalog = void 0;
|
|
@@ -11272,10 +11732,10 @@ var Launcher = class {
|
|
|
11272
11732
|
}
|
|
11273
11733
|
interpreters() {
|
|
11274
11734
|
this.binfmts ??= this.roots().then(
|
|
11275
|
-
(roots) => scanBinfmts(this.base, roots, (
|
|
11276
|
-
if (this.warned.has(
|
|
11277
|
-
this.warned.add(
|
|
11278
|
-
console.warn(
|
|
11735
|
+
(roots) => scanBinfmts(this.base, roots, (message3) => {
|
|
11736
|
+
if (this.warned.has(message3)) return;
|
|
11737
|
+
this.warned.add(message3);
|
|
11738
|
+
console.warn(message3);
|
|
11279
11739
|
})
|
|
11280
11740
|
);
|
|
11281
11741
|
return this.binfmts;
|
|
@@ -11457,6 +11917,7 @@ var Launcher = class {
|
|
|
11457
11917
|
},
|
|
11458
11918
|
...req.ignored ? { ignored: req.ignored } : {},
|
|
11459
11919
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
11920
|
+
cred: req.cred,
|
|
11460
11921
|
program: req.program,
|
|
11461
11922
|
argv0: req.argv0,
|
|
11462
11923
|
args: req.args,
|
|
@@ -11468,9 +11929,9 @@ var Launcher = class {
|
|
|
11468
11929
|
onError: req.report,
|
|
11469
11930
|
spawner: this.spawner(pid, req.report),
|
|
11470
11931
|
forker: this.forker(pid, req),
|
|
11471
|
-
kill: (target, sig) => this.kill(target, sig),
|
|
11932
|
+
kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
|
|
11472
11933
|
writesBack: (target) => this.processes.get(target)?.writesBack() === true,
|
|
11473
|
-
processes: () => ({ boot: this.boot, processes: this.list() }),
|
|
11934
|
+
processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
|
|
11474
11935
|
openFiles: this.openFiles,
|
|
11475
11936
|
nodes: this.nodes,
|
|
11476
11937
|
held: this.held,
|
|
@@ -11490,6 +11951,8 @@ var Launcher = class {
|
|
|
11490
11951
|
...this.shownIds(req)
|
|
11491
11952
|
});
|
|
11492
11953
|
this.processes.set(pid, handle);
|
|
11954
|
+
const { cred } = req;
|
|
11955
|
+
this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
|
|
11493
11956
|
if (req.ppid !== void 0) this.settling.started(pid, req.ppid);
|
|
11494
11957
|
this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
|
|
11495
11958
|
if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
|
|
@@ -11516,12 +11979,13 @@ var Launcher = class {
|
|
|
11516
11979
|
forget(pid) {
|
|
11517
11980
|
this.jobs.remove(pid);
|
|
11518
11981
|
this.described.delete(pid);
|
|
11982
|
+
this.creds.delete(pid);
|
|
11519
11983
|
}
|
|
11520
11984
|
reaped(pid) {
|
|
11521
11985
|
if (this.zombies.delete(pid)) this.forget(pid);
|
|
11522
11986
|
else this.orphans.add(pid);
|
|
11523
11987
|
}
|
|
11524
|
-
list() {
|
|
11988
|
+
list(viewer = ROOT) {
|
|
11525
11989
|
const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
|
|
11526
11990
|
const rootOf = (member) => {
|
|
11527
11991
|
let root = member;
|
|
@@ -11539,6 +12003,8 @@ var Launcher = class {
|
|
|
11539
12003
|
for (const member of members.values()) {
|
|
11540
12004
|
const described = this.described.get(member.pid);
|
|
11541
12005
|
if (member.execed || !described) continue;
|
|
12006
|
+
const cred = this.credOf(member.pid);
|
|
12007
|
+
if (!maySee(viewer, cred)) continue;
|
|
11542
12008
|
const root = rootOf(member);
|
|
11543
12009
|
listed.push({
|
|
11544
12010
|
pid: root.pid,
|
|
@@ -11551,6 +12017,7 @@ var Launcher = class {
|
|
|
11551
12017
|
state: this.processes.has(member.pid) ? "S" : "Z",
|
|
11552
12018
|
memory: this.processes.get(member.pid)?.memory() ?? 0,
|
|
11553
12019
|
umask: this.umaskOf(member.pid),
|
|
12020
|
+
cred,
|
|
11554
12021
|
...this.signalMasksOf(member.pid)
|
|
11555
12022
|
});
|
|
11556
12023
|
}
|
|
@@ -11574,6 +12041,7 @@ var Launcher = class {
|
|
|
11574
12041
|
ppid,
|
|
11575
12042
|
ignored: this.ignoredBy(ppid),
|
|
11576
12043
|
umask: this.umaskOf(ppid),
|
|
12044
|
+
cred: this.credOf(ppid),
|
|
11577
12045
|
...exec
|
|
11578
12046
|
});
|
|
11579
12047
|
deciding?.attach();
|
|
@@ -11619,7 +12087,8 @@ var Launcher = class {
|
|
|
11619
12087
|
ppid,
|
|
11620
12088
|
fork: state,
|
|
11621
12089
|
ignored: this.ignoredBy(ppid, true),
|
|
11622
|
-
umask: this.umaskOf(ppid)
|
|
12090
|
+
umask: this.umaskOf(ppid),
|
|
12091
|
+
cred: this.credOf(ppid)
|
|
11623
12092
|
})
|
|
11624
12093
|
);
|
|
11625
12094
|
}
|
|
@@ -11631,6 +12100,18 @@ var Launcher = class {
|
|
|
11631
12100
|
umaskOf(pid) {
|
|
11632
12101
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
11633
12102
|
}
|
|
12103
|
+
imageOf(pid) {
|
|
12104
|
+
const members = this.jobs.list();
|
|
12105
|
+
let image = pid;
|
|
12106
|
+
for (; ; ) {
|
|
12107
|
+
const next2 = members.find((m) => m.execParent === image);
|
|
12108
|
+
if (!next2) return image;
|
|
12109
|
+
image = next2.pid;
|
|
12110
|
+
}
|
|
12111
|
+
}
|
|
12112
|
+
credOf(pid) {
|
|
12113
|
+
return this.creds.get(pid)?.() ?? copyCred(ROOT);
|
|
12114
|
+
}
|
|
11634
12115
|
released(path) {
|
|
11635
12116
|
const at = normalizePath(path);
|
|
11636
12117
|
this.unlinked.closed(at);
|
|
@@ -11644,10 +12125,13 @@ var Launcher = class {
|
|
|
11644
12125
|
ignoredBy(pid, fork = false) {
|
|
11645
12126
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
11646
12127
|
}
|
|
11647
|
-
kill(pid, sig) {
|
|
11648
|
-
|
|
12128
|
+
kill(pid, sig, sender, from) {
|
|
12129
|
+
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
12130
|
+
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);
|
|
12131
|
+
if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
|
|
11649
12132
|
const handle = this.processes.get(pid);
|
|
11650
12133
|
if (!handle) return false;
|
|
12134
|
+
if (!may(this.imageOf(pid))) throw fsError("EPERM", `pid ${pid}`);
|
|
11651
12135
|
if (sig !== 0) handle.signal(sig);
|
|
11652
12136
|
return true;
|
|
11653
12137
|
}
|
|
@@ -11718,9 +12202,9 @@ var Launcher = class {
|
|
|
11718
12202
|
ask(handle);
|
|
11719
12203
|
});
|
|
11720
12204
|
const served = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
|
|
11721
|
-
const
|
|
11722
|
-
this.removable.set(target, { id, medium, served });
|
|
11723
|
-
const stored = this.inserted.get(
|
|
12205
|
+
const id2 = source.slice("fsa:".length);
|
|
12206
|
+
this.removable.set(target, { id: id2, medium, served });
|
|
12207
|
+
const stored = this.inserted.get(id2) ?? await this.media.get(id2);
|
|
11724
12208
|
if (stored && await granted(stored)) medium.insert(stored);
|
|
11725
12209
|
else ask(stored);
|
|
11726
12210
|
return {
|
|
@@ -11804,15 +12288,16 @@ var Launcher = class {
|
|
|
11804
12288
|
}
|
|
11805
12289
|
async prepare() {
|
|
11806
12290
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
12291
|
+
await this.users.ensure();
|
|
11807
12292
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
11808
12293
|
this.fstab = this.mountFstab();
|
|
11809
12294
|
}
|
|
11810
12295
|
async mountFstab() {
|
|
11811
12296
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
11812
|
-
const
|
|
11813
|
-
for (const { spec } of
|
|
12297
|
+
const lines2 = parseFstab(text);
|
|
12298
|
+
for (const { spec } of lines2) this.mounts.note(spec, "pending");
|
|
11814
12299
|
return mountFstab(
|
|
11815
|
-
|
|
12300
|
+
lines2,
|
|
11816
12301
|
(spec) => this.mount(spec).catch((err) => {
|
|
11817
12302
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
11818
12303
|
throw err;
|
|
@@ -11827,14 +12312,19 @@ var Launcher = class {
|
|
|
11827
12312
|
}
|
|
11828
12313
|
);
|
|
11829
12314
|
}
|
|
11830
|
-
environment(cwd, extra) {
|
|
12315
|
+
environment(cwd, extra, account) {
|
|
12316
|
+
return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
|
|
12317
|
+
}
|
|
12318
|
+
async account(user) {
|
|
12319
|
+
if (typeof user === "object") return user;
|
|
12320
|
+
const found = await this.users.lookup(user ?? 0);
|
|
12321
|
+
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
12322
|
+
return found;
|
|
12323
|
+
}
|
|
12324
|
+
identity(account) {
|
|
11831
12325
|
return {
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
PNPM_HOME: this.pnpmHome,
|
|
11835
|
-
...this.env,
|
|
11836
|
-
PWD: cwd,
|
|
11837
|
-
...extra
|
|
12326
|
+
cred: credOf(account),
|
|
12327
|
+
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
11838
12328
|
};
|
|
11839
12329
|
}
|
|
11840
12330
|
async starting(argv, dir) {
|
|
@@ -11847,6 +12337,7 @@ var Launcher = class {
|
|
|
11847
12337
|
return { cwd, file, planned: await this.plan(file, argv, cwd) };
|
|
11848
12338
|
}
|
|
11849
12339
|
async openTerminal(argv, options) {
|
|
12340
|
+
const account = await this.account(options.user);
|
|
11850
12341
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
11851
12342
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
11852
12343
|
let leader = 0;
|
|
@@ -11862,14 +12353,18 @@ var Launcher = class {
|
|
|
11862
12353
|
fds.installAt(0, stdio);
|
|
11863
12354
|
fds.installAt(1, stdio.retain());
|
|
11864
12355
|
fds.installAt(2, stdio.retain());
|
|
11865
|
-
const env = this.environment(
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
12356
|
+
const env = this.environment(
|
|
12357
|
+
cwd,
|
|
12358
|
+
{
|
|
12359
|
+
TERM: "xterm-256color",
|
|
12360
|
+
COLORTERM: "truecolor",
|
|
12361
|
+
...options.env
|
|
12362
|
+
},
|
|
12363
|
+
account
|
|
12364
|
+
);
|
|
12365
|
+
const report = (message3) => options.onData(encoder3.encode(`${message3}\r
|
|
11871
12366
|
`));
|
|
11872
|
-
const handle = await this.launch(planned, { env, cwd, fds, report });
|
|
12367
|
+
const handle = await this.launch(planned, { env, cwd, fds, report, ...this.identity(account) });
|
|
11873
12368
|
leader = handle.pid;
|
|
11874
12369
|
void handle.exited.then(() => tty.hangup());
|
|
11875
12370
|
return {
|
|
@@ -11885,8 +12380,9 @@ var Launcher = class {
|
|
|
11885
12380
|
};
|
|
11886
12381
|
}
|
|
11887
12382
|
async run(argv, options = {}) {
|
|
12383
|
+
const account = await this.account(options.user);
|
|
11888
12384
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
11889
|
-
const env = this.environment(cwd, options.env);
|
|
12385
|
+
const env = this.environment(cwd, options.env, account);
|
|
11890
12386
|
const out = [];
|
|
11891
12387
|
const err = [];
|
|
11892
12388
|
const keep = options.collect !== false;
|
|
@@ -11896,7 +12392,7 @@ var Launcher = class {
|
|
|
11896
12392
|
};
|
|
11897
12393
|
const stdout = collect(out, options.onStdout);
|
|
11898
12394
|
const stderr = collect(err, options.onStderr);
|
|
11899
|
-
const report = (
|
|
12395
|
+
const report = (message3) => stderr(encoder3.encode(`${message3}
|
|
11900
12396
|
`));
|
|
11901
12397
|
if (!planned) {
|
|
11902
12398
|
report(`${file}: command not found`);
|
|
@@ -11907,7 +12403,14 @@ var Launcher = class {
|
|
|
11907
12403
|
fds.installAt(1, sinkFile(stdout));
|
|
11908
12404
|
fds.installAt(2, sinkFile(stderr));
|
|
11909
12405
|
const group = options.pgid !== void 0 ? { pgid: options.pgid } : {};
|
|
11910
|
-
const handle = await this.launch(planned, {
|
|
12406
|
+
const handle = await this.launch(planned, {
|
|
12407
|
+
env,
|
|
12408
|
+
cwd,
|
|
12409
|
+
fds,
|
|
12410
|
+
report,
|
|
12411
|
+
...group,
|
|
12412
|
+
...this.identity(account)
|
|
12413
|
+
});
|
|
11911
12414
|
options.onStarted?.(handle.pid);
|
|
11912
12415
|
const status = await handle.exited;
|
|
11913
12416
|
return { status, stdout: concat5(out), stderr: concat5(err) };
|
|
@@ -11915,7 +12418,7 @@ var Launcher = class {
|
|
|
11915
12418
|
};
|
|
11916
12419
|
|
|
11917
12420
|
// src/node/memory-root.ts
|
|
11918
|
-
var
|
|
12421
|
+
var fail2 = (name2) => {
|
|
11919
12422
|
throw new DOMException(name2, name2);
|
|
11920
12423
|
};
|
|
11921
12424
|
function valid(name2) {
|
|
@@ -12028,7 +12531,7 @@ var MemoryFile = class {
|
|
|
12028
12531
|
}
|
|
12029
12532
|
async move(parent, name2) {
|
|
12030
12533
|
valid(name2);
|
|
12031
|
-
if (parent.children.get(name2)?.kind === "directory")
|
|
12534
|
+
if (parent.children.get(name2)?.kind === "directory") fail2("InvalidModificationError");
|
|
12032
12535
|
this.parent.children.delete(this.name);
|
|
12033
12536
|
this.name = name2;
|
|
12034
12537
|
this.parent = parent;
|
|
@@ -12046,15 +12549,15 @@ var MemoryDirectory = class _MemoryDirectory {
|
|
|
12046
12549
|
this.parent = parent;
|
|
12047
12550
|
}
|
|
12048
12551
|
present() {
|
|
12049
|
-
if (this.removed)
|
|
12552
|
+
if (this.removed) fail2("NotFoundError");
|
|
12050
12553
|
}
|
|
12051
12554
|
async getDirectoryHandle(name2, { create = false } = {}) {
|
|
12052
12555
|
this.present();
|
|
12053
12556
|
valid(name2);
|
|
12054
12557
|
const found = this.children.get(name2);
|
|
12055
|
-
if (found?.kind === "file")
|
|
12558
|
+
if (found?.kind === "file") fail2("TypeMismatchError");
|
|
12056
12559
|
if (found) return found;
|
|
12057
|
-
if (!create)
|
|
12560
|
+
if (!create) fail2("NotFoundError");
|
|
12058
12561
|
const dir = new _MemoryDirectory(name2, this);
|
|
12059
12562
|
this.children.set(name2, dir);
|
|
12060
12563
|
return dir;
|
|
@@ -12063,9 +12566,9 @@ var MemoryDirectory = class _MemoryDirectory {
|
|
|
12063
12566
|
this.present();
|
|
12064
12567
|
valid(name2);
|
|
12065
12568
|
const found = this.children.get(name2);
|
|
12066
|
-
if (found?.kind === "directory")
|
|
12569
|
+
if (found?.kind === "directory") fail2("TypeMismatchError");
|
|
12067
12570
|
if (found) return found;
|
|
12068
|
-
if (!create)
|
|
12571
|
+
if (!create) fail2("NotFoundError");
|
|
12069
12572
|
const file = new MemoryFile(name2, this);
|
|
12070
12573
|
this.children.set(name2, file);
|
|
12071
12574
|
return file;
|
|
@@ -12074,9 +12577,9 @@ var MemoryDirectory = class _MemoryDirectory {
|
|
|
12074
12577
|
this.present();
|
|
12075
12578
|
valid(name2);
|
|
12076
12579
|
const found = this.children.get(name2);
|
|
12077
|
-
if (!found) return
|
|
12580
|
+
if (!found) return fail2("NotFoundError");
|
|
12078
12581
|
if (found.kind === "directory" && found.children.size > 0 && !recursive) {
|
|
12079
|
-
|
|
12582
|
+
fail2("InvalidModificationError");
|
|
12080
12583
|
}
|
|
12081
12584
|
this.children.delete(name2);
|
|
12082
12585
|
if (found.kind === "directory") found.remove();
|
|
@@ -12105,7 +12608,7 @@ var MemoryDirectory = class _MemoryDirectory {
|
|
|
12105
12608
|
}
|
|
12106
12609
|
async move(parent, name2) {
|
|
12107
12610
|
valid(name2);
|
|
12108
|
-
if (parent.children.has(name2))
|
|
12611
|
+
if (parent.children.has(name2)) fail2("InvalidModificationError");
|
|
12109
12612
|
this.parent?.children.delete(this.name);
|
|
12110
12613
|
this.name = name2;
|
|
12111
12614
|
this.parent = parent;
|
|
@@ -12123,8 +12626,8 @@ function isMemoryRoot(handle) {
|
|
|
12123
12626
|
var TransportError = class extends Error {
|
|
12124
12627
|
status;
|
|
12125
12628
|
code;
|
|
12126
|
-
constructor(
|
|
12127
|
-
super(
|
|
12629
|
+
constructor(message3, status, code) {
|
|
12630
|
+
super(message3);
|
|
12128
12631
|
this.status = status;
|
|
12129
12632
|
this.code = code;
|
|
12130
12633
|
}
|
|
@@ -12295,9 +12798,9 @@ function signalNumber(name2) {
|
|
|
12295
12798
|
}
|
|
12296
12799
|
|
|
12297
12800
|
// src/client/dial-stream.ts
|
|
12298
|
-
function errorWithCode(
|
|
12299
|
-
const code = /^E[A-Z]+/.exec(
|
|
12300
|
-
return Object.assign(new Error(
|
|
12801
|
+
function errorWithCode(message3) {
|
|
12802
|
+
const code = /^E[A-Z]+/.exec(message3)?.[0];
|
|
12803
|
+
return Object.assign(new Error(message3), code ? { code } : {});
|
|
12301
12804
|
}
|
|
12302
12805
|
function dialStream(port, onClose) {
|
|
12303
12806
|
let closed = false;
|
|
@@ -12315,15 +12818,15 @@ function dialStream(port, onClose) {
|
|
|
12315
12818
|
close();
|
|
12316
12819
|
}
|
|
12317
12820
|
});
|
|
12318
|
-
const finish = (
|
|
12821
|
+
const finish = (fail4) => {
|
|
12319
12822
|
const controller = reader;
|
|
12320
12823
|
reader = void 0;
|
|
12321
12824
|
for (const ack of acks.splice(0)) {
|
|
12322
|
-
if (
|
|
12825
|
+
if (fail4) ack.reject(fail4);
|
|
12323
12826
|
else ack.resolve();
|
|
12324
12827
|
}
|
|
12325
12828
|
if (!controller) return;
|
|
12326
|
-
if (
|
|
12829
|
+
if (fail4) controller.error(fail4);
|
|
12327
12830
|
else controller.close();
|
|
12328
12831
|
};
|
|
12329
12832
|
function shut() {
|
|
@@ -12359,11 +12862,11 @@ function dialStream(port, onClose) {
|
|
|
12359
12862
|
close();
|
|
12360
12863
|
}
|
|
12361
12864
|
});
|
|
12362
|
-
const
|
|
12865
|
+
const fail3 = (error) => {
|
|
12363
12866
|
shut();
|
|
12364
12867
|
finish(error);
|
|
12365
12868
|
};
|
|
12366
|
-
return { readable, writable, close, fail:
|
|
12869
|
+
return { readable, writable, close, fail: fail3 };
|
|
12367
12870
|
}
|
|
12368
12871
|
|
|
12369
12872
|
// src/client/loopback-fetch.ts
|
|
@@ -12415,11 +12918,11 @@ function indexOf(haystack, needle) {
|
|
|
12415
12918
|
return -1;
|
|
12416
12919
|
}
|
|
12417
12920
|
function parseHead(bytes) {
|
|
12418
|
-
const [statusLine = "", ...
|
|
12921
|
+
const [statusLine = "", ...lines2] = latin12.decode(bytes).split("\r\n");
|
|
12419
12922
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
12420
12923
|
if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
12421
12924
|
const headers = [];
|
|
12422
|
-
for (const line of
|
|
12925
|
+
for (const line of lines2) {
|
|
12423
12926
|
const colon = line.indexOf(":");
|
|
12424
12927
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
12425
12928
|
}
|
|
@@ -12488,13 +12991,13 @@ function stream(chunks2, socket, signal) {
|
|
|
12488
12991
|
async function requestBytes(request) {
|
|
12489
12992
|
const url = new URL(request.url);
|
|
12490
12993
|
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
|
|
12491
|
-
const
|
|
12994
|
+
const lines2 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
12492
12995
|
request.headers.forEach((value, name2) => {
|
|
12493
|
-
if (!OWN_HEADERS.has(name2))
|
|
12996
|
+
if (!OWN_HEADERS.has(name2)) lines2.push(`${name2}: ${value}`);
|
|
12494
12997
|
});
|
|
12495
|
-
if (body)
|
|
12496
|
-
|
|
12497
|
-
const head = encoder4.encode(
|
|
12998
|
+
if (body) lines2.push(`Content-Length: ${body.length}`);
|
|
12999
|
+
lines2.push("Connection: close", "", "");
|
|
13000
|
+
const head = encoder4.encode(lines2.join("\r\n"));
|
|
12498
13001
|
const out = new Uint8Array(head.length + (body?.length ?? 0));
|
|
12499
13002
|
out.set(head);
|
|
12500
13003
|
if (body) out.set(body, head.length);
|
|
@@ -12535,15 +13038,15 @@ async function loopbackFetch(dial, input, options) {
|
|
|
12535
13038
|
|
|
12536
13039
|
// src/client/attach.ts
|
|
12537
13040
|
var KernelGoneError = class extends Error {
|
|
12538
|
-
constructor(
|
|
12539
|
-
super(
|
|
13041
|
+
constructor(message3 = "the slicc-kernel this client was attached to is gone") {
|
|
13042
|
+
super(message3);
|
|
12540
13043
|
this.name = "KernelGoneError";
|
|
12541
13044
|
}
|
|
12542
13045
|
};
|
|
12543
13046
|
var KernelCallError = class extends Error {
|
|
12544
13047
|
code;
|
|
12545
|
-
constructor(
|
|
12546
|
-
super(
|
|
13048
|
+
constructor(message3, code) {
|
|
13049
|
+
super(message3);
|
|
12547
13050
|
this.name = "KernelCallError";
|
|
12548
13051
|
this.code = code;
|
|
12549
13052
|
}
|
|
@@ -12599,8 +13102,8 @@ var Channel = class {
|
|
|
12599
13102
|
port.addEventListener("close", () => this.end(new KernelGoneError()));
|
|
12600
13103
|
port.start?.();
|
|
12601
13104
|
}
|
|
12602
|
-
post(
|
|
12603
|
-
if (!this.failure) this.port.postMessage(
|
|
13105
|
+
post(message3) {
|
|
13106
|
+
if (!this.failure) this.port.postMessage(message3);
|
|
12604
13107
|
}
|
|
12605
13108
|
end(error) {
|
|
12606
13109
|
if (this.failure) return;
|
|
@@ -12616,14 +13119,14 @@ var Channel = class {
|
|
|
12616
13119
|
this.ended.resolve(error);
|
|
12617
13120
|
}
|
|
12618
13121
|
request(call, handlers = {}) {
|
|
12619
|
-
const
|
|
13122
|
+
const id2 = ++this.nextId;
|
|
12620
13123
|
const { failure } = this;
|
|
12621
|
-
if (failure) return { id, done: Promise.reject(failure) };
|
|
13124
|
+
if (failure) return { id: id2, done: Promise.reject(failure) };
|
|
12622
13125
|
const done4 = new Promise((resolve, reject) => {
|
|
12623
|
-
this.pending.set(
|
|
12624
|
-
this.port.postMessage({ ...call, id });
|
|
13126
|
+
this.pending.set(id2, { resolve, reject, ...handlers });
|
|
13127
|
+
this.port.postMessage({ ...call, id: id2 });
|
|
12625
13128
|
});
|
|
12626
|
-
return { id, done: done4 };
|
|
13129
|
+
return { id: id2, done: done4 };
|
|
12627
13130
|
}
|
|
12628
13131
|
call(call) {
|
|
12629
13132
|
return this.request(call).done;
|
|
@@ -12682,18 +13185,18 @@ async function serveCdpOn(channel, hook, options) {
|
|
|
12682
13185
|
op: "serve-cdp",
|
|
12683
13186
|
...options.runtime ? { runtime: options.runtime } : {}
|
|
12684
13187
|
};
|
|
12685
|
-
const { id, done: done4 } = channel.request(req);
|
|
12686
|
-
channel.cdpHooks.set(
|
|
13188
|
+
const { id: id2, done: done4 } = channel.request(req);
|
|
13189
|
+
channel.cdpHooks.set(id2, hook);
|
|
12687
13190
|
try {
|
|
12688
13191
|
await done4;
|
|
12689
13192
|
} catch (error) {
|
|
12690
|
-
channel.cdpHooks.delete(
|
|
13193
|
+
channel.cdpHooks.delete(id2);
|
|
12691
13194
|
throw error;
|
|
12692
13195
|
}
|
|
12693
13196
|
return {
|
|
12694
13197
|
close: () => {
|
|
12695
|
-
if (!channel.cdpHooks.delete(
|
|
12696
|
-
void channel.call({ op: "unserve-cdp", registration:
|
|
13198
|
+
if (!channel.cdpHooks.delete(id2)) return;
|
|
13199
|
+
void channel.call({ op: "unserve-cdp", registration: id2 }).catch(() => void 0);
|
|
12697
13200
|
}
|
|
12698
13201
|
};
|
|
12699
13202
|
}
|
|
@@ -12706,6 +13209,8 @@ async function spawnOn(channel, argv, opts) {
|
|
|
12706
13209
|
if (opts.pgid !== void 0)
|
|
12707
13210
|
await since(channel, 3, "spawn into a process group", async () => {
|
|
12708
13211
|
});
|
|
13212
|
+
if (opts.user !== void 0) await since(channel, 8, "spawn as a user", async () => {
|
|
13213
|
+
});
|
|
12709
13214
|
const started = Promise.withResolvers();
|
|
12710
13215
|
const options = { ...rest2, ...stdin !== void 0 ? { stdin: bytesOf2(stdin) } : {} };
|
|
12711
13216
|
const { done: done4 } = channel.request(
|
|
@@ -12744,10 +13249,12 @@ async function runOn(channel, argv, opts) {
|
|
|
12744
13249
|
}
|
|
12745
13250
|
async function terminalOn(channel, argv, opts) {
|
|
12746
13251
|
const { onData, ...options } = opts;
|
|
13252
|
+
if (opts.user !== void 0) await since(channel, 8, "a terminal as a user", async () => {
|
|
13253
|
+
});
|
|
12747
13254
|
let listener = onData ?? null;
|
|
12748
13255
|
const backlog = [];
|
|
12749
13256
|
const started = Promise.withResolvers();
|
|
12750
|
-
const { id, done: done4 } = channel.request(
|
|
13257
|
+
const { id: id2, done: done4 } = channel.request(
|
|
12751
13258
|
{ op: "open-terminal", argv, options },
|
|
12752
13259
|
{
|
|
12753
13260
|
output: (_fd, bytes) => listener ? listener(bytes) : backlog.push(bytes),
|
|
@@ -12756,7 +13263,7 @@ async function terminalOn(channel, argv, opts) {
|
|
|
12756
13263
|
);
|
|
12757
13264
|
done4.catch(started.reject);
|
|
12758
13265
|
const pid = await started.promise;
|
|
12759
|
-
const send = (action) => void channel.call({ op: "terminal", terminal:
|
|
13266
|
+
const send = (action) => void channel.call({ op: "terminal", terminal: id2, ...action }).catch(() => void 0);
|
|
12760
13267
|
return {
|
|
12761
13268
|
pid,
|
|
12762
13269
|
exited: done4,
|
|
@@ -12797,22 +13304,22 @@ function fsOn(channel) {
|
|
|
12797
13304
|
"ENOSYS"
|
|
12798
13305
|
);
|
|
12799
13306
|
}
|
|
12800
|
-
const { id, done: done5 } = channel.request({
|
|
13307
|
+
const { id: id2, done: done5 } = channel.request({
|
|
12801
13308
|
op: "watch",
|
|
12802
13309
|
paths,
|
|
12803
13310
|
recursive: options.recursive === true
|
|
12804
13311
|
});
|
|
12805
|
-
channel.watching.set(
|
|
13312
|
+
channel.watching.set(id2, onChange);
|
|
12806
13313
|
try {
|
|
12807
13314
|
await done5;
|
|
12808
13315
|
} catch (error) {
|
|
12809
|
-
channel.watching.delete(
|
|
13316
|
+
channel.watching.delete(id2);
|
|
12810
13317
|
throw error;
|
|
12811
13318
|
}
|
|
12812
13319
|
return {
|
|
12813
13320
|
close: () => {
|
|
12814
|
-
if (!channel.watching.delete(
|
|
12815
|
-
void channel.call({ op: "unwatch", watch:
|
|
13321
|
+
if (!channel.watching.delete(id2)) return;
|
|
13322
|
+
void channel.call({ op: "unwatch", watch: id2 }).catch(() => void 0);
|
|
12816
13323
|
}
|
|
12817
13324
|
};
|
|
12818
13325
|
}
|
|
@@ -12912,9 +13419,9 @@ var TERMINATED = "the kernel is terminated";
|
|
|
12912
13419
|
function nodeWorker(file, live, closed) {
|
|
12913
13420
|
const errors = /* @__PURE__ */ new Set();
|
|
12914
13421
|
const messages = /* @__PURE__ */ new Map();
|
|
12915
|
-
const
|
|
13422
|
+
const fail3 = (message3) => {
|
|
12916
13423
|
for (const handler of [...errors]) {
|
|
12917
|
-
handler({ message:
|
|
13424
|
+
handler({ message: message3, preventDefault() {
|
|
12918
13425
|
} });
|
|
12919
13426
|
}
|
|
12920
13427
|
};
|
|
@@ -12922,16 +13429,16 @@ function nodeWorker(file, live, closed) {
|
|
|
12922
13429
|
if (worker) {
|
|
12923
13430
|
const kill = () => {
|
|
12924
13431
|
void worker.terminate();
|
|
12925
|
-
|
|
13432
|
+
fail3(TERMINATED);
|
|
12926
13433
|
};
|
|
12927
13434
|
live.add(kill);
|
|
12928
13435
|
worker.once("exit", () => live.delete(kill));
|
|
12929
|
-
worker.on("error", (err) =>
|
|
13436
|
+
worker.on("error", (err) => fail3(String(err)));
|
|
12930
13437
|
} else {
|
|
12931
|
-
setTimeout(() =>
|
|
13438
|
+
setTimeout(() => fail3(TERMINATED), 0);
|
|
12932
13439
|
}
|
|
12933
13440
|
return {
|
|
12934
|
-
postMessage: (
|
|
13441
|
+
postMessage: (message3, transfer) => worker?.postMessage(message3, transfer),
|
|
12935
13442
|
addEventListener(type, handler) {
|
|
12936
13443
|
if (type === "error") {
|
|
12937
13444
|
errors.add(handler);
|
|
@@ -13047,12 +13554,21 @@ async function createNodeKernel(options = {}) {
|
|
|
13047
13554
|
umount: (target) => guard(async () => launcher.umount(target)),
|
|
13048
13555
|
mounts: async () => launcher.mounts.list(),
|
|
13049
13556
|
setRoutes: (routes) => guard(async () => launcher.setRoutes(routes)),
|
|
13050
|
-
|
|
13557
|
+
users: {
|
|
13558
|
+
add: (user) => guard(() => launcher.users.add(user)),
|
|
13559
|
+
remove: (name2, options2) => guard(() => launcher.users.remove(name2, options2)),
|
|
13560
|
+
list: () => guard(() => launcher.users.list())
|
|
13561
|
+
},
|
|
13562
|
+
async connect({ user } = {}) {
|
|
13051
13563
|
if (terminated) throw new Error(TERMINATED);
|
|
13564
|
+
if (user !== void 0 && !await launcher.users.lookup(user)) {
|
|
13565
|
+
throw fsError("ENOENT", `no user ${String(user)}`);
|
|
13566
|
+
}
|
|
13052
13567
|
const { port1, port2 } = new MessageChannel2();
|
|
13053
13568
|
const served = serveClient(port1, {
|
|
13054
13569
|
launcher: async () => launcher,
|
|
13055
|
-
signal: signalNumber
|
|
13570
|
+
signal: signalNumber,
|
|
13571
|
+
...user !== void 0 ? { user } : {}
|
|
13056
13572
|
});
|
|
13057
13573
|
clients.add(served);
|
|
13058
13574
|
void served.closed.then(() => clients.delete(served));
|