@ai-ecoverse/slicc-kernel 1.43.0 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -14
- package/dist/driver-worker.js +5 -1
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +18 -3
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +591 -198
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +5 -1
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.js +65 -6
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +594 -209
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +65 -6
- package/dist/process-worker.js.map +3 -3
- package/package.json +3 -3
package/dist/node.js
CHANGED
|
@@ -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,6 +2052,7 @@ 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();
|
|
@@ -1747,7 +2062,7 @@ function serveClient(port, host) {
|
|
|
1747
2062
|
const send = (message2, transfer = []) => {
|
|
1748
2063
|
if (state !== "closed") port.postMessage(message2, 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;
|
|
@@ -1772,6 +2087,7 @@ function serveClient(port, host) {
|
|
|
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";
|
|
@@ -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);
|
|
@@ -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");
|
|
@@ -3380,10 +3691,10 @@ var CdpFacade = class {
|
|
|
3380
3691
|
reject(new HttpError(502, `the CDP host closed: ${reason ?? "no reason"}`));
|
|
3381
3692
|
};
|
|
3382
3693
|
conn.onmessage = (message2) => {
|
|
3383
|
-
const
|
|
3384
|
-
if (
|
|
3694
|
+
const parsed2 = JSON.parse(message2);
|
|
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
|
}
|
|
@@ -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);
|
|
@@ -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) {
|
|
@@ -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
|
}
|
|
@@ -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(),
|
|
@@ -7094,12 +7405,12 @@ function parsePrefix(text) {
|
|
|
7094
7405
|
const parts = text.trim().split("/");
|
|
7095
7406
|
if (parts.length > 2) return void 0;
|
|
7096
7407
|
const [address = "", size] = parts;
|
|
7097
|
-
const
|
|
7098
|
-
if (!
|
|
7099
|
-
const max =
|
|
7408
|
+
const parsed2 = addressBits(address);
|
|
7409
|
+
if (!parsed2) return void 0;
|
|
7410
|
+
const max = parsed2.v6 ? 128 : 32;
|
|
7100
7411
|
const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
7101
7412
|
if (length2 < 0 || length2 > max) return void 0;
|
|
7102
|
-
return { ...
|
|
7413
|
+
return { ...parsed2, length: length2 };
|
|
7103
7414
|
}
|
|
7104
7415
|
function within4(prefix, addr) {
|
|
7105
7416
|
if (prefix.v6 !== addr.v6) return false;
|
|
@@ -7125,10 +7436,10 @@ var Routes = class {
|
|
|
7125
7436
|
this.ipv6 = ipv62;
|
|
7126
7437
|
}
|
|
7127
7438
|
set(table) {
|
|
7128
|
-
const
|
|
7129
|
-
const bad = (table.prefixes ?? []).find((_, i) => !
|
|
7439
|
+
const parsed2 = (table.prefixes ?? []).map(parsePrefix);
|
|
7440
|
+
const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
|
|
7130
7441
|
if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
|
|
7131
|
-
this.table =
|
|
7442
|
+
this.table = parsed2;
|
|
7132
7443
|
this.exit = table.exit === true;
|
|
7133
7444
|
}
|
|
7134
7445
|
classify(host) {
|
|
@@ -7204,21 +7515,21 @@ var Resolver = class {
|
|
|
7204
7515
|
if (!this.uplink || key === "") return [];
|
|
7205
7516
|
const asked = this.uplink.traits.ipv6 ? family : 4;
|
|
7206
7517
|
if (family === 6 && asked !== 6) return [];
|
|
7207
|
-
const
|
|
7208
|
-
const hit = this.cache.get(
|
|
7518
|
+
const id2 = `${asked}:${key}`;
|
|
7519
|
+
const hit = this.cache.get(id2);
|
|
7209
7520
|
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
7210
7521
|
const signal = blocking?.();
|
|
7211
7522
|
if (signal?.aborted) throw new KernelError("EINTR");
|
|
7212
|
-
let lookup = this.pending.get(
|
|
7523
|
+
let lookup = this.pending.get(id2);
|
|
7213
7524
|
if (!lookup) {
|
|
7214
7525
|
const abort = new AbortController();
|
|
7215
|
-
lookup = { work: this.lookup(key, asked,
|
|
7216
|
-
this.pending.set(
|
|
7217
|
-
void lookup.work.then(() => this.pending.delete(
|
|
7526
|
+
lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
|
|
7527
|
+
this.pending.set(id2, lookup);
|
|
7528
|
+
void lookup.work.then(() => this.pending.delete(id2));
|
|
7218
7529
|
}
|
|
7219
7530
|
return waitFor(lookup, signal);
|
|
7220
7531
|
}
|
|
7221
|
-
async lookup(name2, family,
|
|
7532
|
+
async lookup(name2, family, id2, abandoned) {
|
|
7222
7533
|
const uplink = this.uplink;
|
|
7223
7534
|
const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
|
|
7224
7535
|
let answer;
|
|
@@ -7235,7 +7546,7 @@ var Resolver = class {
|
|
|
7235
7546
|
return [];
|
|
7236
7547
|
}
|
|
7237
7548
|
const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
|
|
7238
|
-
if (addresses.length > 0) this.remember(
|
|
7549
|
+
if (addresses.length > 0) this.remember(id2, addresses, answer.ttl);
|
|
7239
7550
|
return addresses;
|
|
7240
7551
|
}
|
|
7241
7552
|
usable(name2, address, family) {
|
|
@@ -7247,11 +7558,11 @@ var Resolver = class {
|
|
|
7247
7558
|
this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
|
|
7248
7559
|
return false;
|
|
7249
7560
|
}
|
|
7250
|
-
remember(
|
|
7561
|
+
remember(id2, addresses, ttl) {
|
|
7251
7562
|
const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
|
|
7252
7563
|
if (seconds <= 0) return;
|
|
7253
|
-
this.cache.delete(
|
|
7254
|
-
this.cache.set(
|
|
7564
|
+
this.cache.delete(id2);
|
|
7565
|
+
this.cache.set(id2, { addresses, expires: this.now() + seconds * 1e3 });
|
|
7255
7566
|
if (this.cache.size > RESOLVE_CACHE_MAX) {
|
|
7256
7567
|
this.cache.delete(this.cache.keys().next().value);
|
|
7257
7568
|
}
|
|
@@ -8693,6 +9004,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
8693
9004
|
"fd-list",
|
|
8694
9005
|
"proc-alarm",
|
|
8695
9006
|
"proc-umask",
|
|
9007
|
+
"proc-cred",
|
|
9008
|
+
"proc-setcred",
|
|
8696
9009
|
"dl-log",
|
|
8697
9010
|
"fd-renumber",
|
|
8698
9011
|
"fd-promote",
|
|
@@ -8743,6 +9056,7 @@ var WasmProcess = class {
|
|
|
8743
9056
|
interrupt = new AbortController();
|
|
8744
9057
|
dying = false;
|
|
8745
9058
|
umask = DEFAULT_UMASK;
|
|
9059
|
+
cred;
|
|
8746
9060
|
execChild;
|
|
8747
9061
|
killed = false;
|
|
8748
9062
|
execTermsig;
|
|
@@ -8762,6 +9076,7 @@ var WasmProcess = class {
|
|
|
8762
9076
|
this.options = options;
|
|
8763
9077
|
this.inherited = options.ignored ?? 0;
|
|
8764
9078
|
this.umask = options.umask ?? DEFAULT_UMASK;
|
|
9079
|
+
this.cred = copyCred(options.cred ?? ROOT);
|
|
8765
9080
|
this.children = new ChildTable(fds, options.spawner, options.forker);
|
|
8766
9081
|
this.nodes = options.nodes ?? new VfsNodes(options.fs);
|
|
8767
9082
|
if (!options.nodes) options.openFiles?.add(this.nodes);
|
|
@@ -9278,6 +9593,11 @@ var WasmProcess = class {
|
|
|
9278
9593
|
return { ok: true, kind: "void" };
|
|
9279
9594
|
case "proc-umask":
|
|
9280
9595
|
return { ok: true, kind: "json", json: this.setUmask(req.mask) };
|
|
9596
|
+
case "proc-cred":
|
|
9597
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9598
|
+
case "proc-setcred":
|
|
9599
|
+
this.cred = changeCred(this.cred, req.change);
|
|
9600
|
+
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
9281
9601
|
case "dl-log":
|
|
9282
9602
|
if (req.append) this.dlLog.push(req.append);
|
|
9283
9603
|
return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
|
|
@@ -9372,9 +9692,9 @@ var KILL_GRACE_MS = 2e3;
|
|
|
9372
9692
|
var descIds = /* @__PURE__ */ new WeakMap();
|
|
9373
9693
|
var nextDescId = 1;
|
|
9374
9694
|
function descId(file) {
|
|
9375
|
-
const
|
|
9376
|
-
descIds.set(file,
|
|
9377
|
-
return
|
|
9695
|
+
const id2 = descIds.get(file) ?? nextDescId++;
|
|
9696
|
+
descIds.set(file, id2);
|
|
9697
|
+
return id2;
|
|
9378
9698
|
}
|
|
9379
9699
|
function inheritedFds(fds) {
|
|
9380
9700
|
return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
|
|
@@ -9491,7 +9811,8 @@ function processInit(opts, sab, umask, lane) {
|
|
|
9491
9811
|
function inherited(opts) {
|
|
9492
9812
|
return {
|
|
9493
9813
|
...opts.ignored ? { ignored: opts.ignored } : {},
|
|
9494
|
-
...opts.umask !== void 0 ? { umask: opts.umask } : {}
|
|
9814
|
+
...opts.umask !== void 0 ? { umask: opts.umask } : {},
|
|
9815
|
+
...opts.cred ? { cred: opts.cred } : {}
|
|
9495
9816
|
};
|
|
9496
9817
|
}
|
|
9497
9818
|
function holdingToken(opts, holds) {
|
|
@@ -9655,6 +9976,7 @@ function spawnWasmProcess(opts) {
|
|
|
9655
9976
|
ignoredSignals: (fork) => process2.inheritable(fork),
|
|
9656
9977
|
signalMasks: () => process2.signalMasks(),
|
|
9657
9978
|
umask: () => process2.umask,
|
|
9979
|
+
cred: () => copyCred(process2.cred),
|
|
9658
9980
|
writesBack: () => opts.program.abi === "wasi" && !done4
|
|
9659
9981
|
};
|
|
9660
9982
|
}
|
|
@@ -9713,6 +10035,9 @@ var JobTable = class {
|
|
|
9713
10035
|
pgidOf(pid) {
|
|
9714
10036
|
return this.members.get(pid)?.pgid;
|
|
9715
10037
|
}
|
|
10038
|
+
sidOf(pid) {
|
|
10039
|
+
return this.members.get(pid)?.sid;
|
|
10040
|
+
}
|
|
9716
10041
|
member(pid) {
|
|
9717
10042
|
const member = this.members.get(pid);
|
|
9718
10043
|
if (!member) throw new KernelError("ESRCH");
|
|
@@ -9721,27 +10046,27 @@ var JobTable = class {
|
|
|
9721
10046
|
idOf(pid) {
|
|
9722
10047
|
return this.members.get(pid)?.shownAs ?? pid;
|
|
9723
10048
|
}
|
|
9724
|
-
byId(
|
|
9725
|
-
for (const member of this.members.values()) if (member.shownAs ===
|
|
9726
|
-
return this.member(
|
|
10049
|
+
byId(id2) {
|
|
10050
|
+
for (const member of this.members.values()) if (member.shownAs === id2) return member;
|
|
10051
|
+
return this.member(id2);
|
|
9727
10052
|
}
|
|
9728
10053
|
setpgid(caller, pid, pgid) {
|
|
9729
10054
|
const me = this.idOf(caller);
|
|
9730
10055
|
const target = this.byId(pid || me);
|
|
9731
|
-
const
|
|
9732
|
-
const group = pgid ||
|
|
10056
|
+
const id2 = this.idOf(target.pid);
|
|
10057
|
+
const group = pgid || id2;
|
|
9733
10058
|
if (group < 0) throw new KernelError("EINVAL");
|
|
9734
10059
|
const self = this.member(caller);
|
|
9735
|
-
if (
|
|
10060
|
+
if (id2 !== me) {
|
|
9736
10061
|
if (target.ppid === void 0 || this.idOf(target.ppid) !== me) {
|
|
9737
10062
|
throw new KernelError("ESRCH");
|
|
9738
10063
|
}
|
|
9739
10064
|
if (target.execed) throw new KernelError("EACCES");
|
|
9740
10065
|
}
|
|
9741
10066
|
if (target.sid !== self.sid) throw new KernelError("EPERM");
|
|
9742
|
-
if (
|
|
10067
|
+
if (id2 === target.sid) throw new KernelError("EPERM");
|
|
9743
10068
|
const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
|
|
9744
|
-
if (group !==
|
|
10069
|
+
if (group !== id2 && !exists) throw new KernelError("EPERM");
|
|
9745
10070
|
target.pgid = group;
|
|
9746
10071
|
}
|
|
9747
10072
|
getpgid(caller, pid) {
|
|
@@ -9752,12 +10077,12 @@ var JobTable = class {
|
|
|
9752
10077
|
}
|
|
9753
10078
|
setsid(pid) {
|
|
9754
10079
|
const member = this.member(pid);
|
|
9755
|
-
const
|
|
9756
|
-
if (member.pgid ===
|
|
9757
|
-
member.sid =
|
|
9758
|
-
member.pgid =
|
|
9759
|
-
this.terminals.delete(
|
|
9760
|
-
return
|
|
10080
|
+
const id2 = this.idOf(pid);
|
|
10081
|
+
if (member.pgid === id2) throw new KernelError("EPERM");
|
|
10082
|
+
member.sid = id2;
|
|
10083
|
+
member.pgid = id2;
|
|
10084
|
+
this.terminals.delete(id2);
|
|
10085
|
+
return id2;
|
|
9761
10086
|
}
|
|
9762
10087
|
terminalNamed(name2) {
|
|
9763
10088
|
return this.knownTerminals().find((tty) => tty.name === name2);
|
|
@@ -9774,15 +10099,17 @@ var JobTable = class {
|
|
|
9774
10099
|
}
|
|
9775
10100
|
acquireTerminal(pid, tty) {
|
|
9776
10101
|
const member = this.members.get(pid);
|
|
9777
|
-
const
|
|
9778
|
-
if (!member || member.sid !==
|
|
10102
|
+
const id2 = this.idOf(pid);
|
|
10103
|
+
if (!member || member.sid !== id2 || this.terminals.has(id2)) return false;
|
|
9779
10104
|
if ([...this.terminals.values()].includes(tty)) return false;
|
|
9780
|
-
this.terminals.set(
|
|
10105
|
+
this.terminals.set(id2, tty);
|
|
9781
10106
|
this.foreground.set(tty, member.pgid);
|
|
9782
10107
|
return true;
|
|
9783
10108
|
}
|
|
9784
|
-
killGroup(pgid, sig) {
|
|
9785
|
-
const
|
|
10109
|
+
killGroup(pgid, sig, may = () => true) {
|
|
10110
|
+
const group = [...this.members.values()].filter((m) => m.pgid === pgid);
|
|
10111
|
+
const targets = group.filter((m) => may(m.pid));
|
|
10112
|
+
if (targets.length === 0 && group.length > 0) throw new KernelError("EPERM");
|
|
9786
10113
|
const pids = new Set(targets.map((m) => m.pid));
|
|
9787
10114
|
if (sig !== 0) {
|
|
9788
10115
|
for (const m of targets)
|
|
@@ -10244,7 +10571,7 @@ function unescape(field2) {
|
|
|
10244
10571
|
);
|
|
10245
10572
|
}
|
|
10246
10573
|
function parseFstab(text) {
|
|
10247
|
-
const
|
|
10574
|
+
const lines2 = [];
|
|
10248
10575
|
text.split("\n").forEach((raw, index) => {
|
|
10249
10576
|
const content = raw.replace(/#.*$/, "").trim();
|
|
10250
10577
|
if (!content) return;
|
|
@@ -10253,12 +10580,12 @@ function parseFstab(text) {
|
|
|
10253
10580
|
if (options.split(",").includes("noauto")) return;
|
|
10254
10581
|
const spec = { source, target, type, options: {} };
|
|
10255
10582
|
try {
|
|
10256
|
-
|
|
10583
|
+
lines2.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
10257
10584
|
} catch (err) {
|
|
10258
|
-
|
|
10585
|
+
lines2.push({ line: index + 1, spec, invalid: err });
|
|
10259
10586
|
}
|
|
10260
10587
|
});
|
|
10261
|
-
return
|
|
10588
|
+
return lines2;
|
|
10262
10589
|
}
|
|
10263
10590
|
function codeOf3(err) {
|
|
10264
10591
|
const code = err?.code;
|
|
@@ -10276,11 +10603,11 @@ function wait(ms, signal) {
|
|
|
10276
10603
|
signal.addEventListener("abort", done4, { once: true });
|
|
10277
10604
|
});
|
|
10278
10605
|
}
|
|
10279
|
-
async function mountFstab(
|
|
10606
|
+
async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
10280
10607
|
}, settled = () => {
|
|
10281
10608
|
}) {
|
|
10282
10609
|
return Promise.all(
|
|
10283
|
-
|
|
10610
|
+
lines2.map(async (each) => {
|
|
10284
10611
|
const result = await mountLine(each, mount, signal, retries, discard);
|
|
10285
10612
|
settled(each, result);
|
|
10286
10613
|
return result;
|
|
@@ -10691,8 +11018,8 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
10691
11018
|
function memoryMedia() {
|
|
10692
11019
|
const handles = /* @__PURE__ */ new Map();
|
|
10693
11020
|
return {
|
|
10694
|
-
get: async (
|
|
10695
|
-
put: async (
|
|
11021
|
+
get: async (id2) => handles.get(id2),
|
|
11022
|
+
put: async (id2, handle) => void handles.set(id2, handle)
|
|
10696
11023
|
};
|
|
10697
11024
|
}
|
|
10698
11025
|
|
|
@@ -10936,10 +11263,10 @@ function importedMemory(bytes) {
|
|
|
10936
11263
|
return { initial, maximum, shared: (flags & 2) !== 0 };
|
|
10937
11264
|
};
|
|
10938
11265
|
while (at < bytes.length) {
|
|
10939
|
-
const
|
|
11266
|
+
const id2 = bytes[at++];
|
|
10940
11267
|
const size = u32();
|
|
10941
11268
|
const end = at + size;
|
|
10942
|
-
if (
|
|
11269
|
+
if (id2 !== 2) {
|
|
10943
11270
|
at = end;
|
|
10944
11271
|
continue;
|
|
10945
11272
|
}
|
|
@@ -11059,18 +11386,17 @@ function foreignImports(bytes) {
|
|
|
11059
11386
|
const r = new Reader(bytes);
|
|
11060
11387
|
let types = [];
|
|
11061
11388
|
while (r.at < bytes.length) {
|
|
11062
|
-
const
|
|
11389
|
+
const id2 = r.byte();
|
|
11063
11390
|
const end = r.u32() + r.at;
|
|
11064
|
-
if (
|
|
11391
|
+
if (id2 === 1) types = readTypes(r);
|
|
11065
11392
|
if (!types) return void 0;
|
|
11066
|
-
if (
|
|
11393
|
+
if (id2 === 2) return readImports(r, types);
|
|
11067
11394
|
r.at = end;
|
|
11068
11395
|
}
|
|
11069
11396
|
return {};
|
|
11070
11397
|
}
|
|
11071
11398
|
|
|
11072
11399
|
// src/launcher.ts
|
|
11073
|
-
var PNPM_HOME = "/home/.local/share/pnpm";
|
|
11074
11400
|
var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
11075
11401
|
var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
11076
11402
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -11170,6 +11496,8 @@ var Launcher = class {
|
|
|
11170
11496
|
zombies = /* @__PURE__ */ new Set();
|
|
11171
11497
|
orphans = /* @__PURE__ */ new Set();
|
|
11172
11498
|
described = /* @__PURE__ */ new Map();
|
|
11499
|
+
creds = /* @__PURE__ */ new Map();
|
|
11500
|
+
users;
|
|
11173
11501
|
jobs = new JobTable();
|
|
11174
11502
|
locks = new LockTable();
|
|
11175
11503
|
settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
|
|
@@ -11221,7 +11549,7 @@ var Launcher = class {
|
|
|
11221
11549
|
this.hostfs = options;
|
|
11222
11550
|
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
11223
11551
|
this.processMounts = options.processMounts ?? true;
|
|
11224
|
-
this.pnpmHome = options.env?.PNPM_HOME ??
|
|
11552
|
+
this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
|
|
11225
11553
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
11226
11554
|
this.cdp = new CdpHosts(options.cdp);
|
|
11227
11555
|
const hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
@@ -11257,6 +11585,7 @@ var Launcher = class {
|
|
|
11257
11585
|
}
|
|
11258
11586
|
);
|
|
11259
11587
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
11588
|
+
this.users = new UserDb(this.fs);
|
|
11260
11589
|
void this.unlinked.sweep("/");
|
|
11261
11590
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
11262
11591
|
this.catalog = void 0;
|
|
@@ -11457,6 +11786,7 @@ var Launcher = class {
|
|
|
11457
11786
|
},
|
|
11458
11787
|
...req.ignored ? { ignored: req.ignored } : {},
|
|
11459
11788
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
11789
|
+
cred: req.cred,
|
|
11460
11790
|
program: req.program,
|
|
11461
11791
|
argv0: req.argv0,
|
|
11462
11792
|
args: req.args,
|
|
@@ -11468,9 +11798,9 @@ var Launcher = class {
|
|
|
11468
11798
|
onError: req.report,
|
|
11469
11799
|
spawner: this.spawner(pid, req.report),
|
|
11470
11800
|
forker: this.forker(pid, req),
|
|
11471
|
-
kill: (target, sig) => this.kill(target, sig),
|
|
11801
|
+
kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
|
|
11472
11802
|
writesBack: (target) => this.processes.get(target)?.writesBack() === true,
|
|
11473
|
-
processes: () => ({ boot: this.boot, processes: this.list() }),
|
|
11803
|
+
processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
|
|
11474
11804
|
openFiles: this.openFiles,
|
|
11475
11805
|
nodes: this.nodes,
|
|
11476
11806
|
held: this.held,
|
|
@@ -11490,6 +11820,8 @@ var Launcher = class {
|
|
|
11490
11820
|
...this.shownIds(req)
|
|
11491
11821
|
});
|
|
11492
11822
|
this.processes.set(pid, handle);
|
|
11823
|
+
const { cred } = req;
|
|
11824
|
+
this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
|
|
11493
11825
|
if (req.ppid !== void 0) this.settling.started(pid, req.ppid);
|
|
11494
11826
|
this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
|
|
11495
11827
|
if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
|
|
@@ -11516,12 +11848,13 @@ var Launcher = class {
|
|
|
11516
11848
|
forget(pid) {
|
|
11517
11849
|
this.jobs.remove(pid);
|
|
11518
11850
|
this.described.delete(pid);
|
|
11851
|
+
this.creds.delete(pid);
|
|
11519
11852
|
}
|
|
11520
11853
|
reaped(pid) {
|
|
11521
11854
|
if (this.zombies.delete(pid)) this.forget(pid);
|
|
11522
11855
|
else this.orphans.add(pid);
|
|
11523
11856
|
}
|
|
11524
|
-
list() {
|
|
11857
|
+
list(viewer = ROOT) {
|
|
11525
11858
|
const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
|
|
11526
11859
|
const rootOf = (member) => {
|
|
11527
11860
|
let root = member;
|
|
@@ -11539,6 +11872,8 @@ var Launcher = class {
|
|
|
11539
11872
|
for (const member of members.values()) {
|
|
11540
11873
|
const described = this.described.get(member.pid);
|
|
11541
11874
|
if (member.execed || !described) continue;
|
|
11875
|
+
const cred = this.credOf(member.pid);
|
|
11876
|
+
if (!maySee(viewer, cred)) continue;
|
|
11542
11877
|
const root = rootOf(member);
|
|
11543
11878
|
listed.push({
|
|
11544
11879
|
pid: root.pid,
|
|
@@ -11551,6 +11886,7 @@ var Launcher = class {
|
|
|
11551
11886
|
state: this.processes.has(member.pid) ? "S" : "Z",
|
|
11552
11887
|
memory: this.processes.get(member.pid)?.memory() ?? 0,
|
|
11553
11888
|
umask: this.umaskOf(member.pid),
|
|
11889
|
+
cred,
|
|
11554
11890
|
...this.signalMasksOf(member.pid)
|
|
11555
11891
|
});
|
|
11556
11892
|
}
|
|
@@ -11574,6 +11910,7 @@ var Launcher = class {
|
|
|
11574
11910
|
ppid,
|
|
11575
11911
|
ignored: this.ignoredBy(ppid),
|
|
11576
11912
|
umask: this.umaskOf(ppid),
|
|
11913
|
+
cred: this.credOf(ppid),
|
|
11577
11914
|
...exec
|
|
11578
11915
|
});
|
|
11579
11916
|
deciding?.attach();
|
|
@@ -11619,7 +11956,8 @@ var Launcher = class {
|
|
|
11619
11956
|
ppid,
|
|
11620
11957
|
fork: state,
|
|
11621
11958
|
ignored: this.ignoredBy(ppid, true),
|
|
11622
|
-
umask: this.umaskOf(ppid)
|
|
11959
|
+
umask: this.umaskOf(ppid),
|
|
11960
|
+
cred: this.credOf(ppid)
|
|
11623
11961
|
})
|
|
11624
11962
|
);
|
|
11625
11963
|
}
|
|
@@ -11631,6 +11969,18 @@ var Launcher = class {
|
|
|
11631
11969
|
umaskOf(pid) {
|
|
11632
11970
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
11633
11971
|
}
|
|
11972
|
+
imageOf(pid) {
|
|
11973
|
+
const members = this.jobs.list();
|
|
11974
|
+
let image = pid;
|
|
11975
|
+
for (; ; ) {
|
|
11976
|
+
const next2 = members.find((m) => m.execParent === image);
|
|
11977
|
+
if (!next2) return image;
|
|
11978
|
+
image = next2.pid;
|
|
11979
|
+
}
|
|
11980
|
+
}
|
|
11981
|
+
credOf(pid) {
|
|
11982
|
+
return this.creds.get(pid)?.() ?? copyCred(ROOT);
|
|
11983
|
+
}
|
|
11634
11984
|
released(path) {
|
|
11635
11985
|
const at = normalizePath(path);
|
|
11636
11986
|
this.unlinked.closed(at);
|
|
@@ -11644,10 +11994,13 @@ var Launcher = class {
|
|
|
11644
11994
|
ignoredBy(pid, fork = false) {
|
|
11645
11995
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
11646
11996
|
}
|
|
11647
|
-
kill(pid, sig) {
|
|
11648
|
-
|
|
11997
|
+
kill(pid, sig, sender, from) {
|
|
11998
|
+
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
11999
|
+
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);
|
|
12000
|
+
if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
|
|
11649
12001
|
const handle = this.processes.get(pid);
|
|
11650
12002
|
if (!handle) return false;
|
|
12003
|
+
if (!may(this.imageOf(pid))) throw fsError("EPERM", `pid ${pid}`);
|
|
11651
12004
|
if (sig !== 0) handle.signal(sig);
|
|
11652
12005
|
return true;
|
|
11653
12006
|
}
|
|
@@ -11718,9 +12071,9 @@ var Launcher = class {
|
|
|
11718
12071
|
ask(handle);
|
|
11719
12072
|
});
|
|
11720
12073
|
const served = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
|
|
11721
|
-
const
|
|
11722
|
-
this.removable.set(target, { id, medium, served });
|
|
11723
|
-
const stored = this.inserted.get(
|
|
12074
|
+
const id2 = source.slice("fsa:".length);
|
|
12075
|
+
this.removable.set(target, { id: id2, medium, served });
|
|
12076
|
+
const stored = this.inserted.get(id2) ?? await this.media.get(id2);
|
|
11724
12077
|
if (stored && await granted(stored)) medium.insert(stored);
|
|
11725
12078
|
else ask(stored);
|
|
11726
12079
|
return {
|
|
@@ -11804,15 +12157,16 @@ var Launcher = class {
|
|
|
11804
12157
|
}
|
|
11805
12158
|
async prepare() {
|
|
11806
12159
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
12160
|
+
await this.users.ensure();
|
|
11807
12161
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
11808
12162
|
this.fstab = this.mountFstab();
|
|
11809
12163
|
}
|
|
11810
12164
|
async mountFstab() {
|
|
11811
12165
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
11812
|
-
const
|
|
11813
|
-
for (const { spec } of
|
|
12166
|
+
const lines2 = parseFstab(text);
|
|
12167
|
+
for (const { spec } of lines2) this.mounts.note(spec, "pending");
|
|
11814
12168
|
return mountFstab(
|
|
11815
|
-
|
|
12169
|
+
lines2,
|
|
11816
12170
|
(spec) => this.mount(spec).catch((err) => {
|
|
11817
12171
|
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
11818
12172
|
throw err;
|
|
@@ -11827,14 +12181,19 @@ var Launcher = class {
|
|
|
11827
12181
|
}
|
|
11828
12182
|
);
|
|
11829
12183
|
}
|
|
11830
|
-
environment(cwd, extra) {
|
|
12184
|
+
environment(cwd, extra, account) {
|
|
12185
|
+
return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
|
|
12186
|
+
}
|
|
12187
|
+
async account(user) {
|
|
12188
|
+
if (typeof user === "object") return user;
|
|
12189
|
+
const found = await this.users.lookup(user ?? 0);
|
|
12190
|
+
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
12191
|
+
return found;
|
|
12192
|
+
}
|
|
12193
|
+
identity(account) {
|
|
11831
12194
|
return {
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
PNPM_HOME: this.pnpmHome,
|
|
11835
|
-
...this.env,
|
|
11836
|
-
PWD: cwd,
|
|
11837
|
-
...extra
|
|
12195
|
+
cred: credOf(account),
|
|
12196
|
+
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
11838
12197
|
};
|
|
11839
12198
|
}
|
|
11840
12199
|
async starting(argv, dir) {
|
|
@@ -11847,6 +12206,7 @@ var Launcher = class {
|
|
|
11847
12206
|
return { cwd, file, planned: await this.plan(file, argv, cwd) };
|
|
11848
12207
|
}
|
|
11849
12208
|
async openTerminal(argv, options) {
|
|
12209
|
+
const account = await this.account(options.user);
|
|
11850
12210
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
11851
12211
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
11852
12212
|
let leader = 0;
|
|
@@ -11862,14 +12222,18 @@ var Launcher = class {
|
|
|
11862
12222
|
fds.installAt(0, stdio);
|
|
11863
12223
|
fds.installAt(1, stdio.retain());
|
|
11864
12224
|
fds.installAt(2, stdio.retain());
|
|
11865
|
-
const env = this.environment(
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
12225
|
+
const env = this.environment(
|
|
12226
|
+
cwd,
|
|
12227
|
+
{
|
|
12228
|
+
TERM: "xterm-256color",
|
|
12229
|
+
COLORTERM: "truecolor",
|
|
12230
|
+
...options.env
|
|
12231
|
+
},
|
|
12232
|
+
account
|
|
12233
|
+
);
|
|
11870
12234
|
const report = (message2) => options.onData(encoder3.encode(`${message2}\r
|
|
11871
12235
|
`));
|
|
11872
|
-
const handle = await this.launch(planned, { env, cwd, fds, report });
|
|
12236
|
+
const handle = await this.launch(planned, { env, cwd, fds, report, ...this.identity(account) });
|
|
11873
12237
|
leader = handle.pid;
|
|
11874
12238
|
void handle.exited.then(() => tty.hangup());
|
|
11875
12239
|
return {
|
|
@@ -11885,8 +12249,9 @@ var Launcher = class {
|
|
|
11885
12249
|
};
|
|
11886
12250
|
}
|
|
11887
12251
|
async run(argv, options = {}) {
|
|
12252
|
+
const account = await this.account(options.user);
|
|
11888
12253
|
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
11889
|
-
const env = this.environment(cwd, options.env);
|
|
12254
|
+
const env = this.environment(cwd, options.env, account);
|
|
11890
12255
|
const out = [];
|
|
11891
12256
|
const err = [];
|
|
11892
12257
|
const keep = options.collect !== false;
|
|
@@ -11907,7 +12272,14 @@ var Launcher = class {
|
|
|
11907
12272
|
fds.installAt(1, sinkFile(stdout));
|
|
11908
12273
|
fds.installAt(2, sinkFile(stderr));
|
|
11909
12274
|
const group = options.pgid !== void 0 ? { pgid: options.pgid } : {};
|
|
11910
|
-
const handle = await this.launch(planned, {
|
|
12275
|
+
const handle = await this.launch(planned, {
|
|
12276
|
+
env,
|
|
12277
|
+
cwd,
|
|
12278
|
+
fds,
|
|
12279
|
+
report,
|
|
12280
|
+
...group,
|
|
12281
|
+
...this.identity(account)
|
|
12282
|
+
});
|
|
11911
12283
|
options.onStarted?.(handle.pid);
|
|
11912
12284
|
const status = await handle.exited;
|
|
11913
12285
|
return { status, stdout: concat5(out), stderr: concat5(err) };
|
|
@@ -12415,11 +12787,11 @@ function indexOf(haystack, needle) {
|
|
|
12415
12787
|
return -1;
|
|
12416
12788
|
}
|
|
12417
12789
|
function parseHead(bytes) {
|
|
12418
|
-
const [statusLine = "", ...
|
|
12790
|
+
const [statusLine = "", ...lines2] = latin12.decode(bytes).split("\r\n");
|
|
12419
12791
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
12420
12792
|
if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
12421
12793
|
const headers = [];
|
|
12422
|
-
for (const line of
|
|
12794
|
+
for (const line of lines2) {
|
|
12423
12795
|
const colon = line.indexOf(":");
|
|
12424
12796
|
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
12425
12797
|
}
|
|
@@ -12488,13 +12860,13 @@ function stream(chunks2, socket, signal) {
|
|
|
12488
12860
|
async function requestBytes(request) {
|
|
12489
12861
|
const url = new URL(request.url);
|
|
12490
12862
|
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
|
|
12491
|
-
const
|
|
12863
|
+
const lines2 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
12492
12864
|
request.headers.forEach((value, name2) => {
|
|
12493
|
-
if (!OWN_HEADERS.has(name2))
|
|
12865
|
+
if (!OWN_HEADERS.has(name2)) lines2.push(`${name2}: ${value}`);
|
|
12494
12866
|
});
|
|
12495
|
-
if (body)
|
|
12496
|
-
|
|
12497
|
-
const head = encoder4.encode(
|
|
12867
|
+
if (body) lines2.push(`Content-Length: ${body.length}`);
|
|
12868
|
+
lines2.push("Connection: close", "", "");
|
|
12869
|
+
const head = encoder4.encode(lines2.join("\r\n"));
|
|
12498
12870
|
const out = new Uint8Array(head.length + (body?.length ?? 0));
|
|
12499
12871
|
out.set(head);
|
|
12500
12872
|
if (body) out.set(body, head.length);
|
|
@@ -12616,14 +12988,14 @@ var Channel = class {
|
|
|
12616
12988
|
this.ended.resolve(error);
|
|
12617
12989
|
}
|
|
12618
12990
|
request(call, handlers = {}) {
|
|
12619
|
-
const
|
|
12991
|
+
const id2 = ++this.nextId;
|
|
12620
12992
|
const { failure } = this;
|
|
12621
|
-
if (failure) return { id, done: Promise.reject(failure) };
|
|
12993
|
+
if (failure) return { id: id2, done: Promise.reject(failure) };
|
|
12622
12994
|
const done4 = new Promise((resolve, reject) => {
|
|
12623
|
-
this.pending.set(
|
|
12624
|
-
this.port.postMessage({ ...call, id });
|
|
12995
|
+
this.pending.set(id2, { resolve, reject, ...handlers });
|
|
12996
|
+
this.port.postMessage({ ...call, id: id2 });
|
|
12625
12997
|
});
|
|
12626
|
-
return { id, done: done4 };
|
|
12998
|
+
return { id: id2, done: done4 };
|
|
12627
12999
|
}
|
|
12628
13000
|
call(call) {
|
|
12629
13001
|
return this.request(call).done;
|
|
@@ -12682,18 +13054,18 @@ async function serveCdpOn(channel, hook, options) {
|
|
|
12682
13054
|
op: "serve-cdp",
|
|
12683
13055
|
...options.runtime ? { runtime: options.runtime } : {}
|
|
12684
13056
|
};
|
|
12685
|
-
const { id, done: done4 } = channel.request(req);
|
|
12686
|
-
channel.cdpHooks.set(
|
|
13057
|
+
const { id: id2, done: done4 } = channel.request(req);
|
|
13058
|
+
channel.cdpHooks.set(id2, hook);
|
|
12687
13059
|
try {
|
|
12688
13060
|
await done4;
|
|
12689
13061
|
} catch (error) {
|
|
12690
|
-
channel.cdpHooks.delete(
|
|
13062
|
+
channel.cdpHooks.delete(id2);
|
|
12691
13063
|
throw error;
|
|
12692
13064
|
}
|
|
12693
13065
|
return {
|
|
12694
13066
|
close: () => {
|
|
12695
|
-
if (!channel.cdpHooks.delete(
|
|
12696
|
-
void channel.call({ op: "unserve-cdp", registration:
|
|
13067
|
+
if (!channel.cdpHooks.delete(id2)) return;
|
|
13068
|
+
void channel.call({ op: "unserve-cdp", registration: id2 }).catch(() => void 0);
|
|
12697
13069
|
}
|
|
12698
13070
|
};
|
|
12699
13071
|
}
|
|
@@ -12706,6 +13078,8 @@ async function spawnOn(channel, argv, opts) {
|
|
|
12706
13078
|
if (opts.pgid !== void 0)
|
|
12707
13079
|
await since(channel, 3, "spawn into a process group", async () => {
|
|
12708
13080
|
});
|
|
13081
|
+
if (opts.user !== void 0) await since(channel, 8, "spawn as a user", async () => {
|
|
13082
|
+
});
|
|
12709
13083
|
const started = Promise.withResolvers();
|
|
12710
13084
|
const options = { ...rest2, ...stdin !== void 0 ? { stdin: bytesOf2(stdin) } : {} };
|
|
12711
13085
|
const { done: done4 } = channel.request(
|
|
@@ -12744,10 +13118,12 @@ async function runOn(channel, argv, opts) {
|
|
|
12744
13118
|
}
|
|
12745
13119
|
async function terminalOn(channel, argv, opts) {
|
|
12746
13120
|
const { onData, ...options } = opts;
|
|
13121
|
+
if (opts.user !== void 0) await since(channel, 8, "a terminal as a user", async () => {
|
|
13122
|
+
});
|
|
12747
13123
|
let listener = onData ?? null;
|
|
12748
13124
|
const backlog = [];
|
|
12749
13125
|
const started = Promise.withResolvers();
|
|
12750
|
-
const { id, done: done4 } = channel.request(
|
|
13126
|
+
const { id: id2, done: done4 } = channel.request(
|
|
12751
13127
|
{ op: "open-terminal", argv, options },
|
|
12752
13128
|
{
|
|
12753
13129
|
output: (_fd, bytes) => listener ? listener(bytes) : backlog.push(bytes),
|
|
@@ -12756,7 +13132,7 @@ async function terminalOn(channel, argv, opts) {
|
|
|
12756
13132
|
);
|
|
12757
13133
|
done4.catch(started.reject);
|
|
12758
13134
|
const pid = await started.promise;
|
|
12759
|
-
const send = (action) => void channel.call({ op: "terminal", terminal:
|
|
13135
|
+
const send = (action) => void channel.call({ op: "terminal", terminal: id2, ...action }).catch(() => void 0);
|
|
12760
13136
|
return {
|
|
12761
13137
|
pid,
|
|
12762
13138
|
exited: done4,
|
|
@@ -12797,22 +13173,22 @@ function fsOn(channel) {
|
|
|
12797
13173
|
"ENOSYS"
|
|
12798
13174
|
);
|
|
12799
13175
|
}
|
|
12800
|
-
const { id, done: done5 } = channel.request({
|
|
13176
|
+
const { id: id2, done: done5 } = channel.request({
|
|
12801
13177
|
op: "watch",
|
|
12802
13178
|
paths,
|
|
12803
13179
|
recursive: options.recursive === true
|
|
12804
13180
|
});
|
|
12805
|
-
channel.watching.set(
|
|
13181
|
+
channel.watching.set(id2, onChange);
|
|
12806
13182
|
try {
|
|
12807
13183
|
await done5;
|
|
12808
13184
|
} catch (error) {
|
|
12809
|
-
channel.watching.delete(
|
|
13185
|
+
channel.watching.delete(id2);
|
|
12810
13186
|
throw error;
|
|
12811
13187
|
}
|
|
12812
13188
|
return {
|
|
12813
13189
|
close: () => {
|
|
12814
|
-
if (!channel.watching.delete(
|
|
12815
|
-
void channel.call({ op: "unwatch", watch:
|
|
13190
|
+
if (!channel.watching.delete(id2)) return;
|
|
13191
|
+
void channel.call({ op: "unwatch", watch: id2 }).catch(() => void 0);
|
|
12816
13192
|
}
|
|
12817
13193
|
};
|
|
12818
13194
|
}
|
|
@@ -13047,12 +13423,21 @@ async function createNodeKernel(options = {}) {
|
|
|
13047
13423
|
umount: (target) => guard(async () => launcher.umount(target)),
|
|
13048
13424
|
mounts: async () => launcher.mounts.list(),
|
|
13049
13425
|
setRoutes: (routes) => guard(async () => launcher.setRoutes(routes)),
|
|
13050
|
-
|
|
13426
|
+
users: {
|
|
13427
|
+
add: (user) => guard(() => launcher.users.add(user)),
|
|
13428
|
+
remove: (name2, options2) => guard(() => launcher.users.remove(name2, options2)),
|
|
13429
|
+
list: () => guard(() => launcher.users.list())
|
|
13430
|
+
},
|
|
13431
|
+
async connect({ user } = {}) {
|
|
13051
13432
|
if (terminated) throw new Error(TERMINATED);
|
|
13433
|
+
if (user !== void 0 && !await launcher.users.lookup(user)) {
|
|
13434
|
+
throw fsError("ENOENT", `no user ${String(user)}`);
|
|
13435
|
+
}
|
|
13052
13436
|
const { port1, port2 } = new MessageChannel2();
|
|
13053
13437
|
const served = serveClient(port1, {
|
|
13054
13438
|
launcher: async () => launcher,
|
|
13055
|
-
signal: signalNumber
|
|
13439
|
+
signal: signalNumber,
|
|
13440
|
+
...user !== void 0 ? { user } : {}
|
|
13056
13441
|
});
|
|
13057
13442
|
clients.add(served);
|
|
13058
13443
|
void served.closed.then(() => clients.delete(served));
|