typescript-virtual-container 1.4.3 → 1.4.5
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/.vscode/settings.json +2 -1
- package/README.md +137 -19
- package/benchmark-results.txt +21 -21
- package/builds/self-standalone.js +322 -266
- package/builds/self-standalone.js.map +4 -4
- package/builds/standalone-wo-sftp.js +268 -212
- package/builds/standalone-wo-sftp.js.map +4 -4
- package/builds/standalone.js +268 -212
- package/builds/standalone.js.map +4 -4
- package/builds/web-full-api.min.js +5 -5
- package/builds/web-full-api.min.js.map +3 -3
- package/builds/web.min.js +5 -5
- package/builds/web.min.js.map +3 -3
- package/bun.lock +101 -6
- package/dist/Honeypot/index.js +1 -0
- package/dist/Honeypot/index.js.map +1 -0
- package/dist/SSHClient/index.js +1 -0
- package/dist/SSHClient/index.js.map +1 -0
- package/dist/SSHMimic/exec.js +1 -0
- package/dist/SSHMimic/exec.js.map +1 -0
- package/dist/SSHMimic/executor.js +1 -0
- package/dist/SSHMimic/executor.js.map +1 -0
- package/dist/SSHMimic/hostKey.js +1 -0
- package/dist/SSHMimic/hostKey.js.map +1 -0
- package/dist/SSHMimic/index.js +1 -0
- package/dist/SSHMimic/index.js.map +1 -0
- package/dist/SSHMimic/loginBanner.js +1 -0
- package/dist/SSHMimic/loginBanner.js.map +1 -0
- package/dist/SSHMimic/loginFormat.js +1 -0
- package/dist/SSHMimic/loginFormat.js.map +1 -0
- package/dist/SSHMimic/prompt.js +1 -0
- package/dist/SSHMimic/prompt.js.map +1 -0
- package/dist/SSHMimic/sftp.js +1 -0
- package/dist/SSHMimic/sftp.js.map +1 -0
- package/dist/VirtualFileSystem/binaryPack.js +1 -0
- package/dist/VirtualFileSystem/binaryPack.js.map +1 -0
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +1 -0
- package/dist/VirtualFileSystem/index.js.map +1 -0
- package/dist/VirtualFileSystem/internalTypes.js +1 -0
- package/dist/VirtualFileSystem/internalTypes.js.map +1 -0
- package/dist/VirtualFileSystem/path.js +1 -0
- package/dist/VirtualFileSystem/path.js.map +1 -0
- package/dist/VirtualPackageManager/index.js +1 -0
- package/dist/VirtualPackageManager/index.js.map +1 -0
- package/dist/VirtualShell/index.js +1 -0
- package/dist/VirtualShell/index.js.map +1 -0
- package/dist/VirtualShell/shell.js +1 -0
- package/dist/VirtualShell/shell.js.map +1 -0
- package/dist/VirtualShell/shellParser.d.ts.map +1 -1
- package/dist/VirtualShell/shellParser.js +3 -1
- package/dist/VirtualShell/shellParser.js.map +1 -0
- package/dist/VirtualUserManager/index.js +1 -0
- package/dist/VirtualUserManager/index.js.map +1 -0
- package/dist/commands/adduser.js +1 -0
- package/dist/commands/adduser.js.map +1 -0
- package/dist/commands/alias.js +1 -0
- package/dist/commands/alias.js.map +1 -0
- package/dist/commands/apt.js +1 -0
- package/dist/commands/apt.js.map +1 -0
- package/dist/commands/awk.d.ts.map +1 -1
- package/dist/commands/awk.js +2 -2
- package/dist/commands/awk.js.map +1 -0
- package/dist/commands/base64.js +1 -0
- package/dist/commands/base64.js.map +1 -0
- package/dist/commands/cat.js +1 -0
- package/dist/commands/cat.js.map +1 -0
- package/dist/commands/cd.js +3 -2
- package/dist/commands/cd.js.map +1 -0
- package/dist/commands/chmod.js +1 -0
- package/dist/commands/chmod.js.map +1 -0
- package/dist/commands/clear.js +1 -0
- package/dist/commands/clear.js.map +1 -0
- package/dist/commands/command-helpers.js +1 -0
- package/dist/commands/command-helpers.js.map +1 -0
- package/dist/commands/cp.js +1 -0
- package/dist/commands/cp.js.map +1 -0
- package/dist/commands/curl.js +1 -0
- package/dist/commands/curl.js.map +1 -0
- package/dist/commands/cut.js +1 -0
- package/dist/commands/cut.js.map +1 -0
- package/dist/commands/date.js +1 -0
- package/dist/commands/date.js.map +1 -0
- package/dist/commands/declare.js +1 -0
- package/dist/commands/declare.js.map +1 -0
- package/dist/commands/deluser.js +1 -0
- package/dist/commands/deluser.js.map +1 -0
- package/dist/commands/df.js +1 -0
- package/dist/commands/df.js.map +1 -0
- package/dist/commands/diff.js +1 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/dpkg.js +1 -0
- package/dist/commands/dpkg.js.map +1 -0
- package/dist/commands/du.js +1 -0
- package/dist/commands/du.js.map +1 -0
- package/dist/commands/echo.js +1 -0
- package/dist/commands/echo.js.map +1 -0
- package/dist/commands/env.js +1 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/exit.js +1 -0
- package/dist/commands/exit.js.map +1 -0
- package/dist/commands/export.js +1 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/find.js +1 -0
- package/dist/commands/find.js.map +1 -0
- package/dist/commands/free.js +1 -0
- package/dist/commands/free.js.map +1 -0
- package/dist/commands/grep.js +1 -0
- package/dist/commands/grep.js.map +1 -0
- package/dist/commands/groups.js +1 -0
- package/dist/commands/groups.js.map +1 -0
- package/dist/commands/gzip.js +1 -0
- package/dist/commands/gzip.js.map +1 -0
- package/dist/commands/head.js +1 -0
- package/dist/commands/head.js.map +1 -0
- package/dist/commands/help.js +1 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +4 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/history.js +1 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/hostname.js +1 -0
- package/dist/commands/hostname.js.map +1 -0
- package/dist/commands/htop.js +1 -0
- package/dist/commands/htop.js.map +1 -0
- package/dist/commands/id.js +1 -0
- package/dist/commands/id.js.map +1 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/kill.js +1 -0
- package/dist/commands/kill.js.map +1 -0
- package/dist/commands/ln.js +1 -0
- package/dist/commands/ln.js.map +1 -0
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +171 -37
- package/dist/commands/ls.js.map +1 -0
- package/dist/commands/lsb-release.js +1 -0
- package/dist/commands/lsb-release.js.map +1 -0
- package/dist/commands/man.js +1 -0
- package/dist/commands/man.js.map +1 -0
- package/dist/commands/mkdir.js +1 -0
- package/dist/commands/mkdir.js.map +1 -0
- package/dist/commands/mv.js +1 -0
- package/dist/commands/mv.js.map +1 -0
- package/dist/commands/nano.js +1 -0
- package/dist/commands/nano.js.map +1 -0
- package/dist/commands/neofetch.js +1 -0
- package/dist/commands/neofetch.js.map +1 -0
- package/dist/commands/node.js +1 -0
- package/dist/commands/node.js.map +1 -0
- package/dist/commands/npm.js +1 -0
- package/dist/commands/npm.js.map +1 -0
- package/dist/commands/passwd.js +1 -0
- package/dist/commands/passwd.js.map +1 -0
- package/dist/commands/ping.js +1 -0
- package/dist/commands/ping.js.map +1 -0
- package/dist/commands/printf.js +1 -0
- package/dist/commands/printf.js.map +1 -0
- package/dist/commands/ps.js +1 -0
- package/dist/commands/ps.js.map +1 -0
- package/dist/commands/pwd.js +1 -0
- package/dist/commands/pwd.js.map +1 -0
- package/dist/commands/python.js +1 -0
- package/dist/commands/python.js.map +1 -0
- package/dist/commands/read.js +1 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/registry.js +1 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/rm.js +1 -0
- package/dist/commands/rm.js.map +1 -0
- package/dist/commands/runtime.d.ts.map +1 -1
- package/dist/commands/runtime.js +37 -0
- package/dist/commands/runtime.js.map +1 -0
- package/dist/commands/sed.js +1 -0
- package/dist/commands/sed.js.map +1 -0
- package/dist/commands/seq.js +1 -0
- package/dist/commands/seq.js.map +1 -0
- package/dist/commands/set.js +1 -0
- package/dist/commands/set.js.map +1 -0
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +9 -4
- package/dist/commands/sh.js.map +1 -0
- package/dist/commands/shift.js +1 -0
- package/dist/commands/shift.js.map +1 -0
- package/dist/commands/sleep.js +1 -0
- package/dist/commands/sleep.js.map +1 -0
- package/dist/commands/sort.js +1 -0
- package/dist/commands/sort.js.map +1 -0
- package/dist/commands/source.js +1 -0
- package/dist/commands/source.js.map +1 -0
- package/dist/commands/stat.js +1 -0
- package/dist/commands/stat.js.map +1 -0
- package/dist/commands/su.js +1 -0
- package/dist/commands/su.js.map +1 -0
- package/dist/commands/sudo.js +1 -0
- package/dist/commands/sudo.js.map +1 -0
- package/dist/commands/tail.js +1 -0
- package/dist/commands/tail.js.map +1 -0
- package/dist/commands/tar.js +1 -0
- package/dist/commands/tar.js.map +1 -0
- package/dist/commands/tee.js +1 -0
- package/dist/commands/tee.js.map +1 -0
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +1 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/touch.js +1 -0
- package/dist/commands/touch.js.map +1 -0
- package/dist/commands/tr.js +1 -0
- package/dist/commands/tr.js.map +1 -0
- package/dist/commands/tree.js +1 -0
- package/dist/commands/tree.js.map +1 -0
- package/dist/commands/true.js +1 -0
- package/dist/commands/true.js.map +1 -0
- package/dist/commands/type.js +1 -0
- package/dist/commands/type.js.map +1 -0
- package/dist/commands/uname.js +1 -0
- package/dist/commands/uname.js.map +1 -0
- package/dist/commands/uniq.js +1 -0
- package/dist/commands/uniq.js.map +1 -0
- package/dist/commands/unset.js +1 -0
- package/dist/commands/unset.js.map +1 -0
- package/dist/commands/uptime.js +1 -0
- package/dist/commands/uptime.js.map +1 -0
- package/dist/commands/wc.js +2 -1
- package/dist/commands/wc.js.map +1 -0
- package/dist/commands/wget.js +1 -0
- package/dist/commands/wget.js.map +1 -0
- package/dist/commands/which.js +1 -0
- package/dist/commands/which.js.map +1 -0
- package/dist/commands/who.js +1 -0
- package/dist/commands/who.js.map +1 -0
- package/dist/commands/whoami.js +1 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/commands/xargs.js +1 -0
- package/dist/commands/xargs.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/linuxRootfs.d.ts +35 -17
- package/dist/modules/linuxRootfs.d.ts.map +1 -1
- package/dist/modules/linuxRootfs.js +332 -152
- package/dist/modules/linuxRootfs.js.map +1 -0
- package/dist/modules/neofetch.js +1 -0
- package/dist/modules/neofetch.js.map +1 -0
- package/dist/modules/shellInteractive.js +1 -0
- package/dist/modules/shellInteractive.js.map +1 -0
- package/dist/modules/shellRuntime.js +1 -0
- package/dist/modules/shellRuntime.js.map +1 -0
- package/dist/self-standalone.js +1 -0
- package/dist/self-standalone.js.map +1 -0
- package/dist/standalone-wo-sftp.js +1 -0
- package/dist/standalone-wo-sftp.js.map +1 -0
- package/dist/standalone.js +1 -0
- package/dist/standalone.js.map +1 -0
- package/dist/types/commands.d.ts +1 -1
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/commands.js +1 -0
- package/dist/types/commands.js.map +1 -0
- package/dist/types/pipeline.js +1 -0
- package/dist/types/pipeline.js.map +1 -0
- package/dist/types/streams.js +1 -0
- package/dist/types/streams.js.map +1 -0
- package/dist/types/vfs.js +1 -0
- package/dist/types/vfs.js.map +1 -0
- package/dist/utils/expand.d.ts +2 -2
- package/dist/utils/expand.d.ts.map +1 -1
- package/dist/utils/expand.js +336 -124
- package/dist/utils/expand.js.map +1 -0
- package/dist/utils/perfLogger.js +1 -0
- package/dist/utils/perfLogger.js.map +1 -0
- package/dist/utils/tokenize.js +1 -0
- package/dist/utils/tokenize.js.map +1 -0
- package/dist/utils/vfsDiff.js +1 -0
- package/dist/utils/vfsDiff.js.map +1 -0
- package/dist/web-api.js +1 -0
- package/dist/web-api.js.map +1 -0
- package/dist/web-full.js +1 -0
- package/dist/web-full.js.map +1 -0
- package/dist/web.js +1 -0
- package/dist/web.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/hierarchy.js +1 -0
- package/docs/assets/highlight.css +162 -0
- package/docs/assets/icons.js +18 -0
- package/docs/assets/icons.svg +1 -0
- package/docs/assets/main.js +60 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1633 -0
- package/docs/classes/HoneyPot.html +31 -0
- package/docs/classes/SshClient.html +66 -0
- package/docs/classes/VirtualFileSystem.html +262 -0
- package/docs/classes/VirtualPackageManager.html +63 -0
- package/docs/classes/VirtualSftpServer.html +169 -0
- package/docs/classes/VirtualShell.html +265 -0
- package/docs/classes/VirtualSshServer.html +177 -0
- package/docs/classes/VirtualUserManager.html +276 -0
- package/docs/docs/.nojekyll +1 -0
- package/docs/docs/assets/hierarchy.js +1 -0
- package/docs/docs/assets/highlight.css +162 -0
- package/docs/docs/assets/icons.js +18 -0
- package/docs/docs/assets/icons.svg +1 -0
- package/docs/docs/assets/main.js +60 -0
- package/docs/docs/assets/navigation.js +1 -0
- package/docs/docs/assets/search.js +1 -0
- package/docs/docs/assets/style.css +1633 -0
- package/docs/docs/hierarchy.html +1 -0
- package/docs/docs/index.html +1842 -0
- package/docs/docs/media/LICENSE +21 -0
- package/docs/docs/modules.html +1 -0
- package/docs/functions/assertDiff.html +6 -0
- package/docs/functions/diffSnapshots.html +7 -0
- package/docs/functions/formatDiff.html +6 -0
- package/docs/functions/getArg.html +13 -0
- package/docs/functions/getFlag.html +15 -0
- package/docs/functions/ifFlag.html +11 -0
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +1842 -0
- package/docs/interfaces/AuditLogEntry.html +6 -0
- package/docs/interfaces/CommandContext.html +22 -0
- package/docs/interfaces/CommandResult.html +26 -0
- package/docs/interfaces/ExecStream.html +11 -0
- package/docs/interfaces/HoneyPotStats.html +14 -0
- package/docs/interfaces/InstalledPackage.html +20 -0
- package/docs/interfaces/NanoEditorSession.html +8 -0
- package/docs/interfaces/PackageDefinition.html +30 -0
- package/docs/interfaces/PackageFile.html +8 -0
- package/docs/interfaces/RemoveOptions.html +4 -0
- package/docs/interfaces/ShellEnv.html +6 -0
- package/docs/interfaces/ShellModule.html +14 -0
- package/docs/interfaces/ShellProperties.html +14 -0
- package/docs/interfaces/ShellStream.html +11 -0
- package/docs/interfaces/SudoChallenge.html +24 -0
- package/docs/interfaces/VfsBaseNode.html +12 -0
- package/docs/interfaces/VfsDiff.html +10 -0
- package/docs/interfaces/VfsDiffEntry.html +6 -0
- package/docs/interfaces/VfsDiffModified.html +10 -0
- package/docs/interfaces/VfsDirectoryNode.html +15 -0
- package/docs/interfaces/VfsFileNode.html +17 -0
- package/docs/interfaces/VfsOptions.html +12 -0
- package/docs/interfaces/VfsSnapshot.html +3 -0
- package/docs/interfaces/VfsSnapshotBaseNode.html +8 -0
- package/docs/interfaces/VfsSnapshotDirectoryNode.html +10 -0
- package/docs/interfaces/VfsSnapshotFileNode.html +12 -0
- package/docs/interfaces/WriteFileOptions.html +6 -0
- package/docs/media/LICENSE +21 -0
- package/docs/modules.html +1 -0
- package/docs/types/CommandMode.html +2 -0
- package/docs/types/CommandOutcome.html +2 -0
- package/docs/types/VfsNodeStats.html +2 -0
- package/docs/types/VfsNodeType.html +2 -0
- package/docs/types/VfsPersistenceMode.html +5 -0
- package/docs/types/VfsSnapshotNode.html +2 -0
- package/examples/web.min.js +5 -5
- package/package.json +7 -4
- package/src/VirtualFileSystem/index.ts +11 -9
- package/src/VirtualShell/shellParser.ts +3 -2
- package/src/bun.d.ts +1 -0
- package/src/commands/awk.ts +1 -2
- package/src/commands/cd.ts +2 -2
- package/src/commands/helpers.ts +3 -0
- package/src/commands/ls.ts +210 -41
- package/src/commands/runtime.ts +56 -3
- package/src/commands/sh.ts +7 -4
- package/src/commands/test.ts +4 -2
- package/src/commands/wc.ts +1 -1
- package/src/modules/linuxRootfs.ts +420 -231
- package/src/types/commands.ts +1 -1
- package/src/utils/expand.ts +256 -76
- package/tests/command-helpers.test.ts +80 -0
- package/tests/commands-admin-net.test.ts +441 -0
- package/tests/commands-advanced.test.ts +456 -0
- package/tests/commands-core.test.ts +562 -0
- package/tests/commands-missing.test.ts +570 -0
- package/tests/commands-specific-units.test.ts +327 -0
- package/tests/commands-text-sys.test.ts +445 -0
- package/tests/expand.test.ts +170 -0
- package/tests/helpers.test.ts +75 -0
- package/tests/test-helper.ts +79 -0
- package/tsconfig.json +3 -0
- package/typedoc.json +8 -0
- package/tests/bun-test-shim.ts +0 -9
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
/** biome-ignore-all lint/style/useNamingConvention: ENV VAR KEYS */
|
|
1
|
+
/** biome-ignore-all lint/style/useNamingConvention: ENV VAR KEYS + system names */
|
|
2
2
|
/**
|
|
3
3
|
* linuxRootfs.ts
|
|
4
4
|
*
|
|
5
5
|
* Bootstraps a realistic Linux directory hierarchy in the VFS.
|
|
6
6
|
* Called once during VirtualShell initialization. Idempotent — skips
|
|
7
7
|
* paths that already exist so FS-mode snapshots survive restarts.
|
|
8
|
+
*
|
|
9
|
+
* Public API:
|
|
10
|
+
* - bootstrapLinuxRootfs() one-shot boot (VirtualShell calls this)
|
|
11
|
+
* - refreshProc() refresh /proc/* (call on session changes)
|
|
12
|
+
* - syncEtcPasswd() sync /etc/passwd|group|shadow from UserManager
|
|
13
|
+
* - createLinuxRootfsEngine() returns engine with .boot() + .tick() for
|
|
14
|
+
* runtimes that want a live refresh loop
|
|
8
15
|
*/
|
|
9
16
|
import * as os from "node:os";
|
|
10
17
|
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
@@ -16,6 +23,18 @@ function ensureFile(vfs, path, content, mode = 0o644) {
|
|
|
16
23
|
if (!vfs.exists(path))
|
|
17
24
|
vfs.writeFile(path, content, { mode });
|
|
18
25
|
}
|
|
26
|
+
function write(vfs, path, content) {
|
|
27
|
+
vfs.writeFile(path, content);
|
|
28
|
+
}
|
|
29
|
+
/** FNV-1a 32-bit — deterministic seed from any string */
|
|
30
|
+
function fnv1a(str) {
|
|
31
|
+
let h = 2166136261;
|
|
32
|
+
for (let i = 0; i < str.length; i++) {
|
|
33
|
+
h ^= str.charCodeAt(i);
|
|
34
|
+
h = Math.imul(h, 16777619);
|
|
35
|
+
}
|
|
36
|
+
return h >>> 0;
|
|
37
|
+
}
|
|
19
38
|
// ─── /etc ────────────────────────────────────────────────────────────────────
|
|
20
39
|
function bootstrapEtc(vfs, hostname, props) {
|
|
21
40
|
ensureDir(vfs, "/etc");
|
|
@@ -36,7 +55,7 @@ function bootstrapEtc(vfs, hostname, props) {
|
|
|
36
55
|
"export PATH=/usr/local/bin:/usr/bin:/bin",
|
|
37
56
|
"export PS1='\\u@\\h:\\w\\$ '",
|
|
38
57
|
].join("\n")}\n`);
|
|
39
|
-
ensureFile(vfs, "/etc/issue",
|
|
58
|
+
ensureFile(vfs, "/etc/issue", "Fortune GNU/Linux 1.0 \\n \\l\n");
|
|
40
59
|
ensureFile(vfs, "/etc/motd", ["", `Welcome to ${props.os}`, `Kernel: ${props.kernel}`, ""].join("\n"));
|
|
41
60
|
// APT sources
|
|
42
61
|
ensureDir(vfs, "/etc/apt");
|
|
@@ -46,7 +65,7 @@ function bootstrapEtc(vfs, hostname, props) {
|
|
|
46
65
|
"deb [virtual] fortune://packages.fortune.local aurora main contrib",
|
|
47
66
|
"deb [virtual] fortune://security.fortune.local aurora-security main",
|
|
48
67
|
].join("\n")}\n`);
|
|
49
|
-
// network
|
|
68
|
+
// network
|
|
50
69
|
ensureDir(vfs, "/etc/network");
|
|
51
70
|
ensureFile(vfs, "/etc/network/interfaces", `${[
|
|
52
71
|
"auto lo",
|
|
@@ -65,13 +84,56 @@ function bootstrapEtc(vfs, hostname, props) {
|
|
|
65
84
|
ensureDir(vfs, "/etc/init.d");
|
|
66
85
|
ensureDir(vfs, "/etc/systemd");
|
|
67
86
|
ensureDir(vfs, "/etc/systemd/system");
|
|
87
|
+
// fstab
|
|
88
|
+
ensureFile(vfs, "/etc/fstab", `${[
|
|
89
|
+
"# <file system> <mount point> <type> <options> <dump> <pass>",
|
|
90
|
+
"UUID=00000000-0000-0000-0000-000000000001 / ext4 errors=remount-ro 0 1",
|
|
91
|
+
"UUID=00000000-0000-0000-0000-000000000002 /boot ext4 defaults 0 2",
|
|
92
|
+
"UUID=00000000-0000-0000-0000-000000000003 none swap sw 0 0",
|
|
93
|
+
"tmpfs /tmp tmpfs defaults,noatime 0 0",
|
|
94
|
+
"tmpfs /run tmpfs defaults,noatime 0 0",
|
|
95
|
+
].join("\n")}\n`);
|
|
96
|
+
// login.defs — useradd/passwd defaults
|
|
97
|
+
ensureFile(vfs, "/etc/login.defs", `${[
|
|
98
|
+
"MAIL_DIR /var/mail",
|
|
99
|
+
"PASS_MAX_DAYS 99999",
|
|
100
|
+
"PASS_MIN_DAYS 0",
|
|
101
|
+
"PASS_WARN_AGE 7",
|
|
102
|
+
"UID_MIN 1000",
|
|
103
|
+
"UID_MAX 60000",
|
|
104
|
+
"GID_MIN 1000",
|
|
105
|
+
"GID_MAX 60000",
|
|
106
|
+
"CREATE_HOME yes",
|
|
107
|
+
"UMASK 022",
|
|
108
|
+
"USERGROUPS_ENAB yes",
|
|
109
|
+
"ENCRYPT_METHOD SHA512",
|
|
110
|
+
].join("\n")}\n`);
|
|
111
|
+
// security + pam
|
|
112
|
+
ensureDir(vfs, "/etc/security");
|
|
113
|
+
ensureFile(vfs, "/etc/security/limits.conf", "# /etc/security/limits.conf\n");
|
|
114
|
+
ensureFile(vfs, "/etc/security/access.conf", "# /etc/security/access.conf\n");
|
|
115
|
+
ensureDir(vfs, "/etc/pam.d");
|
|
116
|
+
ensureFile(vfs, "/etc/pam.d/common-auth", "auth required pam_unix.so\n");
|
|
117
|
+
ensureFile(vfs, "/etc/pam.d/common-account", "account required pam_unix.so\n");
|
|
118
|
+
ensureFile(vfs, "/etc/pam.d/common-password", "password required pam_unix.so obscure sha512\n");
|
|
119
|
+
ensureFile(vfs, "/etc/pam.d/common-session", "session required pam_unix.so\n");
|
|
120
|
+
ensureFile(vfs, "/etc/pam.d/sshd", "@include common-auth\n@include common-account\n@include common-session\n");
|
|
121
|
+
// sudo config
|
|
122
|
+
ensureDir(vfs, "/etc/sudoers.d");
|
|
123
|
+
ensureFile(vfs, "/etc/sudoers", "root ALL=(ALL:ALL) ALL\n%sudo ALL=(ALL:ALL) ALL\n", 0o440);
|
|
124
|
+
// ld
|
|
125
|
+
ensureFile(vfs, "/etc/ld.so.conf", "include /etc/ld.so.conf.d/*.conf\n");
|
|
126
|
+
ensureDir(vfs, "/etc/ld.so.conf.d");
|
|
127
|
+
ensureFile(vfs, "/etc/ld.so.conf.d/x86_64-linux-gnu.conf", "/lib/x86_64-linux-gnu\n/usr/lib/x86_64-linux-gnu\n");
|
|
128
|
+
// locale + timezone
|
|
129
|
+
ensureFile(vfs, "/etc/locale.conf", "LANG=en_US.UTF-8\n");
|
|
130
|
+
ensureFile(vfs, "/etc/timezone", "UTC\n");
|
|
131
|
+
ensureFile(vfs, "/etc/localtime", "UTC\n");
|
|
68
132
|
}
|
|
69
133
|
// ─── /etc/passwd + /etc/group + /etc/shadow ─────────────────────────────────
|
|
70
134
|
/**
|
|
71
135
|
* Sync `/etc/passwd`, `/etc/group`, and `/etc/shadow` from the
|
|
72
136
|
* VirtualUserManager's current user list into the VFS.
|
|
73
|
-
* @param vfs VirtualFileSystem instance to write files into
|
|
74
|
-
* @param users VirtualUserManager to source users from
|
|
75
137
|
*/
|
|
76
138
|
export function syncEtcPasswd(vfs, users) {
|
|
77
139
|
const userList = users.listUsers();
|
|
@@ -97,7 +159,7 @@ export function syncEtcPasswd(vfs, users) {
|
|
|
97
159
|
"nogroup:x:65534:",
|
|
98
160
|
];
|
|
99
161
|
vfs.writeFile("/etc/group", `${groupLines.join("\n")}\n`);
|
|
100
|
-
// shadow — fake hashes, never real
|
|
162
|
+
// shadow — fake hashes, never real credentials
|
|
101
163
|
const shadowLines = [
|
|
102
164
|
"root:*:19000:0:99999:7:::",
|
|
103
165
|
"daemon:*:19000:0:99999:7:::",
|
|
@@ -109,8 +171,8 @@ export function syncEtcPasswd(vfs, users) {
|
|
|
109
171
|
}
|
|
110
172
|
vfs.writeFile("/etc/shadow", `${shadowLines.join("\n")}\n`, { mode: 0o640 });
|
|
111
173
|
}
|
|
112
|
-
// ─── /proc
|
|
113
|
-
/** Derive a stable virtual PID from a tty string
|
|
174
|
+
// ─── /proc helpers ───────────────────────────────────────────────────────────
|
|
175
|
+
/** Derive a stable virtual PID from a tty string e.g. "pts/0" → 1000 */
|
|
114
176
|
function ttyToPid(tty) {
|
|
115
177
|
const match = tty.match(/(\d+)$/);
|
|
116
178
|
return 1000 + (match?.[1] ? parseInt(match[1], 10) : 0);
|
|
@@ -122,10 +184,11 @@ function writeProcPid(vfs, pid, username, _tty, cmdline, startedAt, env) {
|
|
|
122
184
|
ensureDir(vfs, `${dir}/fd`);
|
|
123
185
|
ensureDir(vfs, `${dir}/fdinfo`);
|
|
124
186
|
const uptimeSec = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000);
|
|
125
|
-
|
|
126
|
-
vfs
|
|
127
|
-
vfs
|
|
128
|
-
|
|
187
|
+
const comm = cmdline.split(/\s+/)[0] ?? "bash";
|
|
188
|
+
write(vfs, `${dir}/cmdline`, `${cmdline.replace(/\s+/g, "\0")}\0`);
|
|
189
|
+
write(vfs, `${dir}/comm`, comm);
|
|
190
|
+
write(vfs, `${dir}/status`, `${[
|
|
191
|
+
`Name: ${comm}`,
|
|
129
192
|
`State: S (sleeping)`,
|
|
130
193
|
`Pid: ${pid}`,
|
|
131
194
|
`PPid: 1`,
|
|
@@ -135,35 +198,45 @@ function writeProcPid(vfs, pid, username, _tty, cmdline, startedAt, env) {
|
|
|
135
198
|
`VmSize: 16384 kB`,
|
|
136
199
|
`Threads: 1`,
|
|
137
200
|
].join("\n")}\n`);
|
|
138
|
-
vfs
|
|
139
|
-
vfs
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
201
|
+
write(vfs, `${dir}/stat`, `${pid} (${comm}) S 1 ${pid} ${pid} 0 -1 4194304 0 0 0 0 ${uptimeSec} 0 0 0 20 0 1 0 0 16384 4096 0\n`);
|
|
202
|
+
write(vfs, `${dir}/environ`, `${Object.entries(env).map(([k, v]) => `${k}=${v}`).join("\0")}\0`);
|
|
203
|
+
write(vfs, `${dir}/cwd`, `/home/${username}\0`);
|
|
204
|
+
write(vfs, `${dir}/exe`, "/bin/bash\0");
|
|
205
|
+
// Standard fd stubs (stdin/stdout/stderr)
|
|
206
|
+
for (const fd of ["0", "1", "2"]) {
|
|
207
|
+
ensureFile(vfs, `${dir}/fd/${fd}`, "");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// ─── /proc boot log ──────────────────────────────────────────────────────────
|
|
211
|
+
function bootProcLog(vfs, props) {
|
|
212
|
+
ensureDir(vfs, "/proc/boot");
|
|
213
|
+
ensureFile(vfs, "/proc/boot/log", `${[
|
|
214
|
+
"[ 0.000000] Linux virtual kernel booting...",
|
|
215
|
+
"[ 0.000120] init memory subsystem",
|
|
216
|
+
"[ 0.000240] mount /proc /sys /dev",
|
|
217
|
+
"[ 0.000420] start init",
|
|
218
|
+
"[ 0.000680] system ready",
|
|
219
|
+
].join("\n")}\n`);
|
|
220
|
+
ensureFile(vfs, "/proc/boot/version", `Linux ${props.kernel} (virtual)\n`);
|
|
148
221
|
}
|
|
222
|
+
// ─── /proc refresh ───────────────────────────────────────────────────────────
|
|
149
223
|
/**
|
|
150
224
|
* Populate and refresh `/proc` virtual entries based on host stats and
|
|
151
|
-
* provided active sessions. Rewrites
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* @param hostname Hostname to write into /proc/hostname
|
|
156
|
-
* @param shellStartTime Start time used to compute uptime
|
|
157
|
-
* @param sessions Optional active sessions list to populate per-pid entries
|
|
225
|
+
* provided active sessions. Rewrites uptime, meminfo, cpuinfo, loadavg,
|
|
226
|
+
* per-pid entries, and /proc/self.
|
|
227
|
+
*
|
|
228
|
+
* Safe to call repeatedly — acts as a live kernel state snapshot.
|
|
158
229
|
*/
|
|
159
|
-
export function refreshProc(vfs, props, hostname, shellStartTime, sessions) {
|
|
230
|
+
export function refreshProc(vfs, props, hostname, shellStartTime, sessions = []) {
|
|
160
231
|
ensureDir(vfs, "/proc");
|
|
161
232
|
const uptimeSec = Math.floor((Date.now() - shellStartTime) / 1000);
|
|
162
|
-
|
|
233
|
+
const idleSec = Math.floor(uptimeSec * 0.9);
|
|
234
|
+
write(vfs, "/proc/uptime", `${uptimeSec}.00 ${idleSec}.00\n`);
|
|
235
|
+
// meminfo — real host values, Linux-compatible format
|
|
163
236
|
const totalMemKb = Math.floor(os.totalmem() / 1024);
|
|
164
237
|
const freeMemKb = Math.floor(os.freemem() / 1024);
|
|
165
238
|
const availMemKb = Math.floor(freeMemKb * 0.95);
|
|
166
|
-
vfs
|
|
239
|
+
write(vfs, "/proc/meminfo", `${[
|
|
167
240
|
`MemTotal: ${String(totalMemKb).padStart(10)} kB`,
|
|
168
241
|
`MemFree: ${String(freeMemKb).padStart(10)} kB`,
|
|
169
242
|
`MemAvailable: ${String(availMemKb).padStart(10)} kB`,
|
|
@@ -172,22 +245,22 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions) {
|
|
|
172
245
|
`SwapTotal: ${String(Math.floor(totalMemKb * 0.5)).padStart(10)} kB`,
|
|
173
246
|
`SwapFree: ${String(Math.floor(totalMemKb * 0.5)).padStart(10)} kB`,
|
|
174
247
|
].join("\n")}\n`);
|
|
248
|
+
// cpuinfo — real host CPU passthrough
|
|
175
249
|
const cpus = os.cpus();
|
|
176
250
|
const cpuLines = [];
|
|
177
251
|
for (let i = 0; i < cpus.length; i++) {
|
|
178
252
|
const c = cpus[i];
|
|
179
253
|
if (!c)
|
|
180
254
|
continue;
|
|
181
|
-
|
|
182
|
-
cpuLines.push(`processor\t: ${i}`, `model name\t: ${c.model}`, `cpu MHz\t\t: ${mhz}`, `cache size\t: 8192 KB`, "");
|
|
255
|
+
cpuLines.push(`processor\t: ${i}`, `model name\t: ${c.model}`, `cpu MHz\t\t: ${c.speed.toFixed(3)}`, `cache size\t: 8192 KB`, "");
|
|
183
256
|
}
|
|
184
|
-
vfs
|
|
185
|
-
vfs
|
|
186
|
-
vfs
|
|
187
|
-
//
|
|
257
|
+
write(vfs, "/proc/cpuinfo", `${cpuLines.join("\n")}\n`);
|
|
258
|
+
write(vfs, "/proc/version", `Linux version ${props.kernel} (fortune@build) (gcc version 12.2.0) #1 SMP\n`);
|
|
259
|
+
write(vfs, "/proc/hostname", `${hostname}\n`);
|
|
260
|
+
// loadavg — slightly random but bounded
|
|
188
261
|
const load = (Math.random() * 0.5).toFixed(2);
|
|
189
|
-
const numProcs = 1 +
|
|
190
|
-
vfs
|
|
262
|
+
const numProcs = 1 + sessions.length;
|
|
263
|
+
write(vfs, "/proc/loadavg", `${load} ${load} ${load} ${numProcs}/${numProcs} 1\n`);
|
|
191
264
|
// /proc/net stubs
|
|
192
265
|
ensureDir(vfs, "/proc/net");
|
|
193
266
|
ensureFile(vfs, "/proc/net/dev", `${[
|
|
@@ -196,11 +269,60 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions) {
|
|
|
196
269
|
" lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
|
197
270
|
" eth0: 131072 1024 0 0 0 0 0 0 65536 512 0 0 0 0 0 0",
|
|
198
271
|
].join("\n")}\n`);
|
|
199
|
-
|
|
272
|
+
ensureFile(vfs, "/proc/net/if_inet6", "");
|
|
273
|
+
ensureFile(vfs, "/proc/net/tcp", " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
274
|
+
ensureFile(vfs, "/proc/net/tcp6", " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
275
|
+
// /proc/cmdline — kernel boot args
|
|
276
|
+
write(vfs, "/proc/cmdline", `BOOT_IMAGE=/boot/vmlinuz-${props.kernel} root=/dev/sda2 ro quiet splash\n`);
|
|
277
|
+
// /proc/filesystems
|
|
278
|
+
ensureFile(vfs, "/proc/filesystems", `${["nodev\tsysfs", "nodev\ttmpfs", "nodev\tproc", "nodev\tdevtmpfs", "\text4", "\tvfat", "nodev\tsquashfs", "nodev\toverlay"].join("\n")}\n`);
|
|
279
|
+
// /proc/mounts (= /proc/self/mounts)
|
|
280
|
+
const mountsContent = `${[
|
|
281
|
+
"sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0",
|
|
282
|
+
"proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0",
|
|
283
|
+
"devtmpfs /dev devtmpfs rw,nosuid,size=8192k,nr_inodes=4096,mode=755 0 0",
|
|
284
|
+
"tmpfs /run tmpfs rw,nosuid,nodev,noexec,relatime,size=204800k,mode=755 0 0",
|
|
285
|
+
"tmpfs /tmp tmpfs rw,nosuid,nodev,noatime 0 0",
|
|
286
|
+
"/dev/sda2 / ext4 rw,relatime,errors=remount-ro 0 0",
|
|
287
|
+
"/dev/sda1 /boot ext4 rw,relatime 0 0",
|
|
288
|
+
"tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0",
|
|
289
|
+
"devpts /dev/pts devpts rw,nosuid,noexec,relatime,mode=620,ptmxmode=000 0 0",
|
|
290
|
+
"squashfs /snap/core squashfs ro,nodev,relatime 0 0",
|
|
291
|
+
].join("\n")}\n`;
|
|
292
|
+
write(vfs, "/proc/mounts", mountsContent);
|
|
293
|
+
ensureDir(vfs, "/proc/self");
|
|
294
|
+
write(vfs, "/proc/self/mounts", mountsContent);
|
|
295
|
+
// /proc/partitions
|
|
296
|
+
write(vfs, "/proc/partitions", `${[
|
|
297
|
+
"major minor #blocks name",
|
|
298
|
+
"",
|
|
299
|
+
" 8 0 41943040 sda",
|
|
300
|
+
" 8 1 524288 sda1",
|
|
301
|
+
" 8 2 41417216 sda2",
|
|
302
|
+
" 7 0 10485760 loop0",
|
|
303
|
+
].join("\n")}\n`);
|
|
304
|
+
// /proc/swaps
|
|
305
|
+
write(vfs, "/proc/swaps", "Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority\n" +
|
|
306
|
+
`/dev/sda3\t\t\t\tpartition\t${Math.floor(os.totalmem() / 2048)}\t\t0\t\t-2\n`);
|
|
307
|
+
// /proc/sys — sysctl virtual tree
|
|
308
|
+
ensureDir(vfs, "/proc/sys");
|
|
309
|
+
ensureDir(vfs, "/proc/sys/kernel");
|
|
310
|
+
ensureDir(vfs, "/proc/sys/net");
|
|
311
|
+
ensureDir(vfs, "/proc/sys/vm");
|
|
312
|
+
ensureFile(vfs, "/proc/sys/kernel/hostname", `${hostname}\n`);
|
|
313
|
+
ensureFile(vfs, "/proc/sys/kernel/ostype", "Linux\n");
|
|
314
|
+
ensureFile(vfs, "/proc/sys/kernel/osrelease", `${props.kernel}\n`);
|
|
315
|
+
ensureFile(vfs, "/proc/sys/kernel/pid_max", "32768\n");
|
|
316
|
+
ensureFile(vfs, "/proc/sys/kernel/threads-max", "65536\n");
|
|
317
|
+
ensureFile(vfs, "/proc/sys/kernel/randomize_va_space", "2\n");
|
|
318
|
+
ensureFile(vfs, "/proc/sys/kernel/dmesg_restrict", "0\n");
|
|
319
|
+
ensureFile(vfs, "/proc/sys/net/ipv4/ip_forward", "0\n");
|
|
320
|
+
ensureFile(vfs, "/proc/sys/vm/swappiness", "60\n");
|
|
321
|
+
ensureFile(vfs, "/proc/sys/vm/overcommit_memory", "0\n");
|
|
322
|
+
// init process (PID 1)
|
|
200
323
|
writeProcPid(vfs, 1, "root", "pts/0", "/sbin/init", new Date(shellStartTime).toISOString(), {});
|
|
201
|
-
//
|
|
202
|
-
const
|
|
203
|
-
for (const session of activeSessions) {
|
|
324
|
+
// per-session processes
|
|
325
|
+
for (const session of sessions) {
|
|
204
326
|
const pid = ttyToPid(session.tty);
|
|
205
327
|
writeProcPid(vfs, pid, session.username, session.tty, "bash", session.startedAt, {
|
|
206
328
|
USER: session.username,
|
|
@@ -209,75 +331,106 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions) {
|
|
|
209
331
|
SHELL: "/bin/bash",
|
|
210
332
|
});
|
|
211
333
|
}
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
// so /proc/self is a directory that mirrors the most recent session,
|
|
215
|
-
// or init if no sessions. Commands that read /proc/self get consistent data.
|
|
216
|
-
const selfPid = activeSessions.length > 0
|
|
217
|
-
? ttyToPid(activeSessions[activeSessions.length - 1].tty)
|
|
218
|
-
: 1;
|
|
219
|
-
// Remove existing /proc/self and recreate as content copy
|
|
334
|
+
// /proc/self — mirror of most recent session, or init
|
|
335
|
+
const selfPid = sessions.length > 0 ? ttyToPid(sessions[sessions.length - 1].tty) : 1;
|
|
220
336
|
if (vfs.exists("/proc/self")) {
|
|
221
337
|
try {
|
|
222
338
|
vfs.remove("/proc/self");
|
|
223
339
|
}
|
|
224
|
-
catch { }
|
|
340
|
+
catch { /* ignore */ }
|
|
225
341
|
}
|
|
226
|
-
// /proc/self is a real directory (not a symlink, which VFS may not support for dirs)
|
|
227
342
|
const selfSrc = `/proc/${selfPid}`;
|
|
343
|
+
ensureDir(vfs, "/proc/self");
|
|
344
|
+
ensureDir(vfs, "/proc/self/fd");
|
|
228
345
|
if (vfs.exists(selfSrc)) {
|
|
229
|
-
ensureDir(vfs, "/proc/self");
|
|
230
|
-
ensureDir(vfs, "/proc/self/fd");
|
|
231
346
|
for (const entry of vfs.list(selfSrc)) {
|
|
232
347
|
const srcPath = `${selfSrc}/${entry}`;
|
|
233
348
|
const dstPath = `/proc/self/${entry}`;
|
|
234
349
|
try {
|
|
235
350
|
const st = vfs.stat(srcPath);
|
|
236
|
-
if (st.type === "file")
|
|
237
|
-
vfs
|
|
238
|
-
}
|
|
351
|
+
if (st.type === "file")
|
|
352
|
+
write(vfs, dstPath, vfs.readFile(srcPath));
|
|
239
353
|
}
|
|
240
|
-
catch { }
|
|
354
|
+
catch { /* skip unreadable entries */ }
|
|
241
355
|
}
|
|
242
|
-
vfs.writeFile("/proc/self/status", vfs.exists(`${selfSrc}/status`) ? vfs.readFile(`${selfSrc}/status`) : "");
|
|
243
356
|
}
|
|
244
357
|
else {
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
vfs
|
|
248
|
-
vfs
|
|
249
|
-
vfs
|
|
250
|
-
vfs
|
|
251
|
-
vfs
|
|
252
|
-
vfs.writeFile("/proc/self/exe", "/bin/bash\0");
|
|
358
|
+
// Minimal fallback
|
|
359
|
+
write(vfs, "/proc/self/cmdline", "bash\0");
|
|
360
|
+
write(vfs, "/proc/self/comm", "bash");
|
|
361
|
+
write(vfs, "/proc/self/status", "Name:\tbash\nState:\tS (sleeping)\nPid:\t1\nPPid:\t0\n");
|
|
362
|
+
write(vfs, "/proc/self/environ", "");
|
|
363
|
+
write(vfs, "/proc/self/cwd", "/root\0");
|
|
364
|
+
write(vfs, "/proc/self/exe", "/bin/bash\0");
|
|
253
365
|
}
|
|
254
366
|
}
|
|
255
367
|
// ─── /sys ─────────────────────────────────────────────────────────────────────
|
|
256
|
-
function bootstrapSys(vfs, props) {
|
|
368
|
+
function bootstrapSys(vfs, hostname, props) {
|
|
257
369
|
ensureDir(vfs, "/sys");
|
|
258
370
|
ensureDir(vfs, "/sys/devices");
|
|
259
371
|
ensureDir(vfs, "/sys/devices/virtual");
|
|
260
372
|
ensureDir(vfs, "/sys/devices/virtual/dmi");
|
|
261
373
|
ensureDir(vfs, "/sys/devices/virtual/dmi/id");
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
374
|
+
const seed = fnv1a(hostname);
|
|
375
|
+
const product = `VirtualNode-${(seed % 10000).toString().padStart(4, "0")}`;
|
|
376
|
+
// Full DMI table — deterministic, seeded from hostname
|
|
377
|
+
const dmi = {
|
|
378
|
+
bios_vendor: "Virtual BIOS",
|
|
379
|
+
bios_version: "1.0",
|
|
380
|
+
bios_date: "01/01/2025",
|
|
381
|
+
sys_vendor: "Fortune Systems",
|
|
382
|
+
product_name: product,
|
|
383
|
+
product_family: "VirtualContainer",
|
|
384
|
+
product_version: "v1",
|
|
385
|
+
product_uuid: `${seed.toString(16).padStart(8, "0")}-0000-0000-0000-000000000000`,
|
|
386
|
+
product_serial: `SN-${seed}`,
|
|
387
|
+
chassis_type: "3",
|
|
388
|
+
chassis_vendor: "Virtual",
|
|
389
|
+
chassis_version: "v1",
|
|
390
|
+
board_name: "fortune-board",
|
|
391
|
+
modalias: `dmi:bvnVirtual:bvr1.0:svnFortune:pn${product}`,
|
|
392
|
+
};
|
|
393
|
+
for (const [k, v] of Object.entries(dmi)) {
|
|
394
|
+
ensureFile(vfs, `/sys/devices/virtual/dmi/id/${k}`, `${v}\n`);
|
|
395
|
+
}
|
|
265
396
|
ensureDir(vfs, "/sys/class");
|
|
266
397
|
ensureDir(vfs, "/sys/class/net");
|
|
267
398
|
ensureDir(vfs, "/sys/kernel");
|
|
268
|
-
ensureFile(vfs, "/sys/kernel/hostname",
|
|
399
|
+
ensureFile(vfs, "/sys/kernel/hostname", `${hostname}\n`);
|
|
269
400
|
ensureFile(vfs, "/sys/kernel/osrelease", `${props.kernel}\n`);
|
|
270
401
|
ensureFile(vfs, "/sys/kernel/ostype", "Linux\n");
|
|
271
402
|
}
|
|
272
403
|
// ─── /dev ─────────────────────────────────────────────────────────────────────
|
|
273
404
|
function bootstrapDev(vfs) {
|
|
274
405
|
ensureDir(vfs, "/dev");
|
|
406
|
+
// character devices
|
|
275
407
|
ensureFile(vfs, "/dev/null", "", 0o666);
|
|
276
408
|
ensureFile(vfs, "/dev/zero", "", 0o666);
|
|
409
|
+
ensureFile(vfs, "/dev/full", "", 0o666);
|
|
277
410
|
ensureFile(vfs, "/dev/random", "", 0o444);
|
|
278
411
|
ensureFile(vfs, "/dev/urandom", "", 0o444);
|
|
412
|
+
ensureFile(vfs, "/dev/mem", "", 0o640);
|
|
413
|
+
// terminal devices
|
|
414
|
+
ensureFile(vfs, "/dev/console", "", 0o600);
|
|
415
|
+
ensureFile(vfs, "/dev/tty", "", 0o666);
|
|
416
|
+
ensureFile(vfs, "/dev/tty0", "", 0o620);
|
|
417
|
+
ensureFile(vfs, "/dev/tty1", "", 0o620);
|
|
418
|
+
ensureFile(vfs, "/dev/ttyS0", "", 0o660);
|
|
419
|
+
// loop devices
|
|
420
|
+
for (let i = 0; i < 8; i++) {
|
|
421
|
+
ensureFile(vfs, `/dev/loop${i}`, "", 0o660);
|
|
422
|
+
}
|
|
423
|
+
ensureDir(vfs, "/dev/loop-control");
|
|
424
|
+
// block device stubs (sda + partitions)
|
|
425
|
+
ensureFile(vfs, "/dev/sda", "", 0o660);
|
|
426
|
+
ensureFile(vfs, "/dev/sda1", "", 0o660);
|
|
427
|
+
ensureFile(vfs, "/dev/sda2", "", 0o660);
|
|
428
|
+
// misc
|
|
279
429
|
ensureDir(vfs, "/dev/pts");
|
|
280
430
|
ensureDir(vfs, "/dev/shm");
|
|
431
|
+
ensureFile(vfs, "/dev/stdin", "", 0o666);
|
|
432
|
+
ensureFile(vfs, "/dev/stdout", "", 0o666);
|
|
433
|
+
ensureFile(vfs, "/dev/stderr", "", 0o666);
|
|
281
434
|
}
|
|
282
435
|
// ─── /usr ─────────────────────────────────────────────────────────────────────
|
|
283
436
|
function bootstrapUsr(vfs) {
|
|
@@ -293,66 +446,26 @@ function bootstrapUsr(vfs) {
|
|
|
293
446
|
ensureDir(vfs, "/usr/share/man");
|
|
294
447
|
ensureDir(vfs, "/usr/share/man/man1");
|
|
295
448
|
ensureDir(vfs, "/usr/lib");
|
|
296
|
-
//
|
|
449
|
+
// Stubs so `which` can resolve built-in commands
|
|
297
450
|
const builtins = [
|
|
298
|
-
"sh",
|
|
299
|
-
"
|
|
300
|
-
"
|
|
301
|
-
"
|
|
302
|
-
"
|
|
303
|
-
"
|
|
304
|
-
"find",
|
|
305
|
-
"sort",
|
|
306
|
-
"head",
|
|
307
|
-
"tail",
|
|
308
|
-
"cut",
|
|
309
|
-
"tr",
|
|
310
|
-
"sed",
|
|
311
|
-
"awk",
|
|
312
|
-
"wc",
|
|
313
|
-
"tee",
|
|
314
|
-
"tar",
|
|
315
|
-
"gzip",
|
|
316
|
-
"gunzip",
|
|
317
|
-
"touch",
|
|
318
|
-
"mkdir",
|
|
319
|
-
"rm",
|
|
320
|
-
"mv",
|
|
321
|
-
"cp",
|
|
322
|
-
"chmod",
|
|
323
|
-
"ln",
|
|
324
|
-
"pwd",
|
|
325
|
-
"env",
|
|
326
|
-
"date",
|
|
327
|
-
"sleep",
|
|
328
|
-
"id",
|
|
329
|
-
"whoami",
|
|
330
|
-
"hostname",
|
|
331
|
-
"uname",
|
|
332
|
-
"ps",
|
|
333
|
-
"kill",
|
|
334
|
-
"df",
|
|
335
|
-
"du",
|
|
336
|
-
"curl",
|
|
337
|
-
"wget",
|
|
338
|
-
"nano",
|
|
339
|
-
"diff",
|
|
340
|
-
"uniq",
|
|
341
|
-
"xargs",
|
|
342
|
-
"base64",
|
|
451
|
+
"sh", "bash", "ls", "cat", "echo", "grep", "find", "sort",
|
|
452
|
+
"head", "tail", "cut", "tr", "sed", "awk", "wc", "tee",
|
|
453
|
+
"tar", "gzip", "gunzip", "touch", "mkdir", "rm", "mv", "cp",
|
|
454
|
+
"chmod", "ln", "pwd", "env", "date", "sleep", "id", "whoami",
|
|
455
|
+
"hostname", "uname", "ps", "kill", "df", "du", "curl", "wget",
|
|
456
|
+
"nano", "diff", "uniq", "xargs", "base64",
|
|
343
457
|
];
|
|
344
458
|
for (const bin of builtins) {
|
|
345
459
|
ensureFile(vfs, `/usr/bin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
|
|
346
460
|
}
|
|
347
|
-
// lsb_release script
|
|
348
461
|
ensureFile(vfs, "/usr/bin/lsb_release", '#!/bin/sh\nexec lsb_release "$@"\n', 0o755);
|
|
349
462
|
}
|
|
350
463
|
// ─── /var ─────────────────────────────────────────────────────────────────────
|
|
351
464
|
function bootstrapVar(vfs) {
|
|
352
465
|
ensureDir(vfs, "/var");
|
|
353
466
|
ensureDir(vfs, "/var/log");
|
|
467
|
+
ensureDir(vfs, "/var/log/apt");
|
|
354
468
|
ensureDir(vfs, "/var/tmp");
|
|
355
|
-
ensureDir(vfs, "/var/run");
|
|
356
469
|
ensureDir(vfs, "/var/cache");
|
|
357
470
|
ensureDir(vfs, "/var/cache/apt");
|
|
358
471
|
ensureDir(vfs, "/var/cache/apt/archives");
|
|
@@ -361,80 +474,147 @@ function bootstrapVar(vfs) {
|
|
|
361
474
|
ensureDir(vfs, "/var/lib/apt/lists");
|
|
362
475
|
ensureDir(vfs, "/var/lib/dpkg");
|
|
363
476
|
ensureDir(vfs, "/var/lib/dpkg/info");
|
|
364
|
-
|
|
477
|
+
ensureDir(vfs, "/var/lib/misc");
|
|
478
|
+
ensureDir(vfs, "/var/spool");
|
|
479
|
+
ensureDir(vfs, "/var/spool/cron");
|
|
480
|
+
ensureDir(vfs, "/var/mail");
|
|
481
|
+
// dpkg status — starts empty, VirtualPackageManager populates it
|
|
365
482
|
ensureFile(vfs, "/var/lib/dpkg/status", "");
|
|
366
483
|
ensureFile(vfs, "/var/lib/dpkg/available", "");
|
|
367
|
-
// syslog
|
|
484
|
+
// syslog stubs
|
|
368
485
|
ensureFile(vfs, "/var/log/syslog", `${new Date().toUTCString()} fortune kernel: Virtual container started\n`);
|
|
369
486
|
ensureFile(vfs, "/var/log/auth.log", "");
|
|
487
|
+
ensureFile(vfs, "/var/log/kern.log", "");
|
|
370
488
|
ensureFile(vfs, "/var/log/dpkg.log", "");
|
|
371
489
|
ensureFile(vfs, "/var/log/apt/history.log", "");
|
|
372
490
|
ensureFile(vfs, "/var/log/apt/term.log", "");
|
|
491
|
+
// /run — systemd tmpfs runtime dir (canonical on modern Debian)
|
|
492
|
+
// /var/run is a legacy symlink to /run
|
|
493
|
+
ensureDir(vfs, "/run");
|
|
494
|
+
ensureDir(vfs, "/run/lock");
|
|
495
|
+
ensureDir(vfs, "/run/systemd");
|
|
496
|
+
ensureDir(vfs, "/run/user");
|
|
497
|
+
ensureFile(vfs, "/run/utmp", "");
|
|
373
498
|
}
|
|
374
499
|
// ─── /bin + /sbin symlinks ────────────────────────────────────────────────────
|
|
375
500
|
function bootstrapBin(vfs) {
|
|
376
|
-
//
|
|
377
|
-
if (!vfs.exists("/bin"))
|
|
501
|
+
// Modern Debian: /bin and /sbin are symlinks to /usr/bin and /usr/sbin
|
|
502
|
+
if (!vfs.exists("/bin"))
|
|
378
503
|
vfs.symlink("/usr/bin", "/bin");
|
|
379
|
-
|
|
380
|
-
if (!vfs.exists("/sbin")) {
|
|
504
|
+
if (!vfs.exists("/sbin"))
|
|
381
505
|
vfs.symlink("/usr/sbin", "/sbin");
|
|
382
|
-
|
|
383
|
-
if (!vfs.exists("/
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
506
|
+
// /var/run → /run (systemd compat)
|
|
507
|
+
if (!vfs.exists("/var/run"))
|
|
508
|
+
vfs.symlink("/run", "/var/run");
|
|
509
|
+
ensureDir(vfs, "/lib");
|
|
510
|
+
ensureDir(vfs, "/lib64");
|
|
511
|
+
ensureDir(vfs, "/lib/x86_64-linux-gnu");
|
|
512
|
+
ensureDir(vfs, "/lib/modules");
|
|
389
513
|
}
|
|
390
514
|
// ─── /tmp ─────────────────────────────────────────────────────────────────────
|
|
391
515
|
function bootstrapTmp(vfs) {
|
|
392
516
|
ensureDir(vfs, "/tmp", 0o1777);
|
|
393
517
|
}
|
|
394
|
-
// ─── /root
|
|
518
|
+
// ─── /root home ───────────────────────────────────────────────────────────────
|
|
395
519
|
function bootstrapRoot(vfs) {
|
|
396
520
|
ensureDir(vfs, "/root", 0o700);
|
|
397
|
-
ensureFile(vfs, "/
|
|
521
|
+
ensureFile(vfs, "/root/.bashrc", `${[
|
|
398
522
|
"# root .bashrc",
|
|
399
523
|
"export PS1='\\[\\033[0;31m\\]\\u@\\h\\[\\033[0m\\]:\\[\\033[0;34m\\]\\w\\[\\033[0m\\]# '",
|
|
400
524
|
"export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
401
525
|
"alias ll='ls -la'",
|
|
402
526
|
"alias la='ls -A'",
|
|
403
527
|
].join("\n")}\n`);
|
|
404
|
-
ensureFile(vfs, "/
|
|
405
|
-
// Fix: /home/root should map to /root for root user
|
|
406
|
-
// if (!vfs.exists("/home/root")) {
|
|
407
|
-
// vfs.symlink("/root", "/home/root");
|
|
408
|
-
// }
|
|
528
|
+
ensureFile(vfs, "/root/.profile", "[ -f ~/.bashrc ] && . ~/.bashrc\n");
|
|
409
529
|
}
|
|
410
|
-
// ─── /opt
|
|
411
|
-
function bootstrapMisc(vfs) {
|
|
530
|
+
// ─── /opt /srv /mnt /media /home ─────────────────────────────────────────────
|
|
531
|
+
function bootstrapMisc(vfs, props) {
|
|
412
532
|
ensureDir(vfs, "/opt");
|
|
413
533
|
ensureDir(vfs, "/srv");
|
|
414
534
|
ensureDir(vfs, "/mnt");
|
|
415
535
|
ensureDir(vfs, "/media");
|
|
536
|
+
ensureDir(vfs, "/home");
|
|
537
|
+
// /boot — grub + kernel images
|
|
538
|
+
ensureDir(vfs, "/boot");
|
|
539
|
+
ensureDir(vfs, "/boot/grub");
|
|
540
|
+
ensureDir(vfs, "/boot/grub/grub.cfg.d");
|
|
541
|
+
ensureFile(vfs, "/boot/grub/grub.cfg", `${[
|
|
542
|
+
"# GRUB configuration (virtual)",
|
|
543
|
+
"set default=0",
|
|
544
|
+
"set timeout=5",
|
|
545
|
+
"",
|
|
546
|
+
`menuentry "Fortune GNU/Linux" {`,
|
|
547
|
+
` linux /vmlinuz root=/dev/sda2 ro quiet splash`,
|
|
548
|
+
` initrd /initrd.img`,
|
|
549
|
+
`}`,
|
|
550
|
+
].join("\n")}\n`);
|
|
551
|
+
// kernel + initrd stubs in /boot
|
|
552
|
+
const kver = props.kernel;
|
|
553
|
+
ensureFile(vfs, `/boot/vmlinuz-${kver}`, "", 0o644);
|
|
554
|
+
ensureFile(vfs, `/boot/initrd.img-${kver}`, "", 0o644);
|
|
555
|
+
ensureFile(vfs, `/boot/System.map-${kver}`, `${kver} virtual\n`, 0o644);
|
|
556
|
+
ensureFile(vfs, `/boot/config-${kver}`, `# Linux kernel config ${kver}\n`, 0o644);
|
|
557
|
+
// root-level symlinks (Debian convention)
|
|
558
|
+
if (!vfs.exists("/vmlinuz"))
|
|
559
|
+
vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz");
|
|
560
|
+
if (!vfs.exists("/vmlinuz.old"))
|
|
561
|
+
vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz.old");
|
|
562
|
+
if (!vfs.exists("/initrd.img"))
|
|
563
|
+
vfs.symlink(`/boot/initrd.img-${kver}`, "/initrd.img");
|
|
564
|
+
if (!vfs.exists("/initrd.img.old"))
|
|
565
|
+
vfs.symlink(`/boot/initrd.img-${kver}`, "/initrd.img.old");
|
|
566
|
+
// /snap — snapd mount namespace root
|
|
567
|
+
ensureDir(vfs, "/snap");
|
|
568
|
+
ensureDir(vfs, "/snap/bin");
|
|
569
|
+
// /lost+found — ext4 fsck recovery dir (mode 0o700, root only)
|
|
570
|
+
ensureDir(vfs, "/lost+found", 0o700);
|
|
416
571
|
}
|
|
417
572
|
// ─── main entry point ─────────────────────────────────────────────────────────
|
|
418
573
|
/**
|
|
419
574
|
* Bootstraps the full Linux rootfs hierarchy in the VFS.
|
|
420
575
|
* Safe to call multiple times — idempotent.
|
|
421
576
|
*
|
|
422
|
-
* @param vfs
|
|
423
|
-
* @param users
|
|
424
|
-
* @param hostname
|
|
425
|
-
* @param props
|
|
577
|
+
* @param vfs Target virtual filesystem.
|
|
578
|
+
* @param users User manager (for /etc/passwd sync).
|
|
579
|
+
* @param hostname Virtual hostname.
|
|
580
|
+
* @param props Shell properties (kernel, os, arch).
|
|
426
581
|
* @param shellStartTime Unix ms of shell creation (for uptime).
|
|
582
|
+
* @param sessions Active sessions (for /proc/<pid> population).
|
|
427
583
|
*/
|
|
428
|
-
export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime) {
|
|
584
|
+
export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime, sessions = []) {
|
|
429
585
|
bootstrapEtc(vfs, hostname, props);
|
|
430
|
-
bootstrapSys(vfs, props);
|
|
586
|
+
bootstrapSys(vfs, hostname, props);
|
|
431
587
|
bootstrapDev(vfs);
|
|
432
588
|
bootstrapUsr(vfs);
|
|
433
589
|
bootstrapVar(vfs);
|
|
434
590
|
bootstrapBin(vfs);
|
|
435
591
|
bootstrapTmp(vfs);
|
|
436
592
|
bootstrapRoot(vfs);
|
|
437
|
-
bootstrapMisc(vfs);
|
|
438
|
-
|
|
593
|
+
bootstrapMisc(vfs, props);
|
|
594
|
+
bootProcLog(vfs, props);
|
|
595
|
+
refreshProc(vfs, props, hostname, shellStartTime, sessions);
|
|
439
596
|
syncEtcPasswd(vfs, users);
|
|
440
597
|
}
|
|
598
|
+
// ─── optional live engine ─────────────────────────────────────────────────────
|
|
599
|
+
/**
|
|
600
|
+
* Engine for runtimes that want periodic /proc refresh (e.g. web shell
|
|
601
|
+
* with live `top`/`ps` output). Call `.boot()` once, then `.tick()` on
|
|
602
|
+
* each session change or on a timer.
|
|
603
|
+
*
|
|
604
|
+
* ```ts
|
|
605
|
+
* const engine = createLinuxRootfsEngine(vfs, props, hostname, Date.now());
|
|
606
|
+
* engine.boot(users, sessions);
|
|
607
|
+
* setInterval(() => engine.tick(shell.listActiveSessions()), 5000);
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
export function createLinuxRootfsEngine(vfs, props, hostname, startTime) {
|
|
611
|
+
return {
|
|
612
|
+
boot(users, sessions = []) {
|
|
613
|
+
bootstrapLinuxRootfs(vfs, users, hostname, props, startTime, sessions);
|
|
614
|
+
},
|
|
615
|
+
tick(sessions = []) {
|
|
616
|
+
refreshProc(vfs, props, hostname, startTime, sessions);
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
//# sourceMappingURL=linuxRootfs.js.map
|