typescript-virtual-container 1.4.9 → 1.5.1
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 +1 -1
- package/README.md +141 -89
- package/builds/fortune-nyx-v1.5.1-directbash-k6.1.0.mjs +1768 -0
- package/builds/fortune-nyx-v1.5.1-ssh-nosftp.js +1768 -0
- package/builds/fortune-nyx-v1.5.1-ssh.cjs +1769 -0
- package/bun.lock +3 -3
- package/dist/SSHMimic/exec.js +2 -2
- package/dist/SSHMimic/exec.js.map +1 -1
- package/dist/SSHMimic/index.d.ts.map +1 -1
- package/dist/SSHMimic/index.js +2 -1
- package/dist/SSHMimic/index.js.map +1 -1
- package/dist/SSHMimic/sftp.d.ts.map +1 -1
- package/dist/SSHMimic/sftp.js +4 -3
- package/dist/SSHMimic/sftp.js.map +1 -1
- package/dist/VirtualFileSystem/index.d.ts +14 -0
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +51 -3
- package/dist/VirtualFileSystem/index.js.map +1 -1
- package/dist/VirtualFileSystem/journal.d.ts.map +1 -1
- package/dist/VirtualFileSystem/journal.js +13 -5
- package/dist/VirtualFileSystem/journal.js.map +1 -1
- package/dist/VirtualShell/shell.js +12 -12
- package/dist/VirtualShell/shell.js.map +1 -1
- package/dist/VirtualUserManager/index.js +8 -11
- package/dist/VirtualUserManager/index.js.map +1 -1
- package/dist/commands/apt.js +3 -3
- package/dist/commands/apt.js.map +1 -1
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +2 -1
- package/dist/commands/cd.js.map +1 -1
- package/dist/commands/helpers.d.ts +1 -1
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +3 -2
- package/dist/commands/helpers.js.map +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/lsb-release.js +1 -1
- package/dist/commands/lsb-release.js.map +1 -1
- package/dist/commands/runtime.d.ts +2 -0
- package/dist/commands/runtime.d.ts.map +1 -1
- package/dist/commands/runtime.js +5 -1
- package/dist/commands/runtime.js.map +1 -1
- package/dist/modules/linuxRootfs.d.ts +9 -5
- package/dist/modules/linuxRootfs.d.ts.map +1 -1
- package/dist/modules/linuxRootfs.js +1079 -148
- package/dist/modules/linuxRootfs.js.map +1 -1
- package/dist/self-standalone.js +22 -12
- package/dist/self-standalone.js.map +1 -1
- package/docs/assets/hierarchy.js +1 -1
- package/docs/classes/HoneyPot.html +9 -9
- package/docs/classes/IdleManager.html +8 -8
- package/docs/classes/SshClient.html +18 -18
- package/docs/classes/VirtualFileSystem.html +34 -34
- package/docs/classes/VirtualPackageManager.html +13 -13
- package/docs/classes/VirtualSftpServer.html +3 -3
- package/docs/classes/VirtualShell.html +28 -28
- package/docs/classes/VirtualSshServer.html +5 -5
- package/docs/classes/VirtualUserManager.html +27 -27
- package/docs/functions/assertDiff.html +2 -2
- package/docs/functions/diffSnapshots.html +2 -2
- package/docs/functions/formatDiff.html +2 -2
- package/docs/functions/getArg.html +2 -2
- package/docs/functions/getFlag.html +2 -2
- package/docs/functions/ifFlag.html +2 -2
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +37 -31
- package/docs/interfaces/AuditLogEntry.html +3 -3
- package/docs/interfaces/CommandContext.html +12 -12
- package/docs/interfaces/CommandResult.html +13 -13
- package/docs/interfaces/ExecStream.html +6 -6
- package/docs/interfaces/HoneyPotStats.html +3 -3
- package/docs/interfaces/IdleManagerOptions.html +3 -3
- package/docs/interfaces/InstalledPackage.html +11 -11
- package/docs/interfaces/NanoEditorSession.html +5 -5
- package/docs/interfaces/PackageDefinition.html +14 -14
- package/docs/interfaces/PackageFile.html +5 -5
- package/docs/interfaces/PasswordChallenge.html +9 -9
- package/docs/interfaces/RemoveOptions.html +3 -3
- package/docs/interfaces/ShellEnv.html +4 -4
- package/docs/interfaces/ShellModule.html +8 -8
- package/docs/interfaces/ShellProperties.html +5 -5
- package/docs/interfaces/ShellStream.html +7 -7
- package/docs/interfaces/SudoChallenge.html +9 -9
- package/docs/interfaces/VfsBaseNode.html +7 -7
- package/docs/interfaces/VfsDiff.html +6 -6
- package/docs/interfaces/VfsDiffEntry.html +4 -4
- package/docs/interfaces/VfsDiffModified.html +6 -6
- package/docs/interfaces/VfsDirectoryNode.html +8 -8
- package/docs/interfaces/VfsFileNode.html +9 -9
- package/docs/interfaces/VfsOptions.html +6 -6
- package/docs/interfaces/VfsSnapshot.html +3 -3
- package/docs/interfaces/VfsSnapshotBaseNode.html +4 -4
- package/docs/interfaces/VfsSnapshotDirectoryNode.html +5 -5
- package/docs/interfaces/VfsSnapshotFileNode.html +6 -6
- package/docs/interfaces/VirtualActiveSession.html +7 -7
- package/docs/interfaces/VirtualSftpServerOptions.html +3 -3
- package/docs/interfaces/VirtualShellVfsLike.html +3 -3
- package/docs/interfaces/VirtualShellVfsOptions.html +3 -3
- package/docs/interfaces/WriteFileOptions.html +4 -4
- package/docs/modules.html +1 -1
- package/docs/types/ArgParseOptions.html +3 -3
- package/docs/types/CommandMode.html +2 -2
- package/docs/types/CommandOutcome.html +2 -2
- package/docs/types/IdleState.html +1 -1
- package/docs/types/VfsNodeStats.html +2 -2
- package/docs/types/VfsNodeType.html +2 -2
- package/docs/types/VfsPersistenceMode.html +2 -2
- package/docs/types/VfsSnapshotNode.html +2 -2
- package/package.json +6 -5
- package/scripts/build-all.mjs +198 -0
- package/scripts/build-names.mjs +44 -0
- package/src/SSHMimic/exec.ts +2 -2
- package/src/SSHMimic/index.ts +2 -1
- package/src/SSHMimic/sftp.ts +4 -3
- package/src/VirtualFileSystem/index.ts +46 -3
- package/src/VirtualFileSystem/journal.ts +12 -5
- package/src/VirtualShell/shell.ts +12 -12
- package/src/VirtualUserManager/index.ts +11 -11
- package/src/commands/apt.ts +3 -3
- package/src/commands/cd.ts +2 -1
- package/src/commands/helpers.ts +3 -2
- package/src/commands/index.ts +1 -1
- package/src/commands/lsb-release.ts +1 -1
- package/src/commands/runtime.ts +6 -1
- package/src/modules/linuxRootfs.ts +1293 -207
- package/src/self-standalone.ts +26 -12
- package/tests/new-features.test.ts +2 -2
- package/tests/sftp.test.ts +13 -13
- package/builds/self-standalone.js +0 -1299
- package/builds/standalone-wo-sftp.js +0 -1300
- package/builds/standalone.cjs +0 -1301
- /package/builds/{web-full-api.min.js → fortune-nyx-v1.5.1-web-full.min.js} +0 -0
- /package/builds/{web.min.js → fortune-nyx-v1.5.1-web.min.js} +0 -0
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
* Called once during VirtualShell initialization. Idempotent — skips
|
|
7
7
|
* paths that already exist so FS-mode snapshots survive restarts.
|
|
8
8
|
*
|
|
9
|
+
* Emulation fidelity: modelled after a Fortune GNU/Linux 1.0 (Nyx)
|
|
10
|
+
* container with Firecracker MicroVM kernel 6.x, virtio block devices
|
|
11
|
+
* (vda/vdb/vdc/vdd), cgroups v1 hierarchy, Node.js 22, Python 3.12,
|
|
12
|
+
* GCC 13, OpenJDK 21, and a Fortune-style package database.
|
|
13
|
+
*
|
|
9
14
|
* Public API:
|
|
10
|
-
* - bootstrapLinuxRootfs()
|
|
11
|
-
* - refreshProc()
|
|
12
|
-
* - syncEtcPasswd()
|
|
13
|
-
* - createLinuxRootfsEngine()
|
|
14
|
-
* runtimes that want a live refresh loop
|
|
15
|
+
* - bootstrapLinuxRootfs() one-shot boot (VirtualShell calls this)
|
|
16
|
+
* - refreshProc() refresh /proc/* (call on session changes)
|
|
17
|
+
* - syncEtcPasswd() sync /etc/passwd|group|shadow from UserManager
|
|
18
|
+
* - createLinuxRootfsEngine() engine with .boot() + .tick() for live loops
|
|
15
19
|
*/
|
|
16
20
|
import * as os from "node:os";
|
|
17
21
|
import VirtualFileSystem from "../VirtualFileSystem";
|
|
@@ -22,7 +26,6 @@ function ensureDir(vfs, path, mode = 0o755) {
|
|
|
22
26
|
vfs.mkdir(path, mode);
|
|
23
27
|
}
|
|
24
28
|
function ensureFile(vfs, path, content, mode = 0o644) {
|
|
25
|
-
// Use lazy stub — no Buffer allocated until the file is actually read or overwritten
|
|
26
29
|
vfs.writeStub(path, content, mode);
|
|
27
30
|
}
|
|
28
31
|
function write(vfs, path, content) {
|
|
@@ -40,33 +43,45 @@ function fnv1a(str) {
|
|
|
40
43
|
// ─── /etc ────────────────────────────────────────────────────────────────────
|
|
41
44
|
function bootstrapEtc(vfs, hostname, props) {
|
|
42
45
|
ensureDir(vfs, "/etc");
|
|
43
|
-
// os-release —
|
|
46
|
+
// os-release — Fortune Nyx identity
|
|
44
47
|
ensureFile(vfs, "/etc/os-release", `${[
|
|
45
48
|
`NAME="Fortune GNU/Linux"`,
|
|
46
49
|
`PRETTY_NAME="${props.os}"`,
|
|
47
50
|
`ID=fortune`,
|
|
48
51
|
`ID_LIKE=debian`,
|
|
49
52
|
`HOME_URL="https://github.com/itsrealfortune/typescript-virtual-container"`,
|
|
50
|
-
`VERSION_CODENAME=
|
|
51
|
-
`VERSION_ID="
|
|
53
|
+
`VERSION_CODENAME=nyx`,
|
|
54
|
+
`VERSION_ID="24.04"`,
|
|
55
|
+
`FORTUNE_CODENAME=nyx`,
|
|
52
56
|
].join("\n")}\n`);
|
|
53
|
-
ensureFile(vfs, "/etc/debian_version", "
|
|
57
|
+
ensureFile(vfs, "/etc/debian_version", "nyx/stable\n");
|
|
54
58
|
ensureFile(vfs, "/etc/hostname", `${hostname}\n`);
|
|
55
|
-
ensureFile(vfs, "/etc/shells", "/bin/sh\n/bin/bash\n/usr/bin/bash\n");
|
|
59
|
+
ensureFile(vfs, "/etc/shells", "/bin/sh\n/bin/bash\n/usr/bin/bash\n/bin/dash\n/usr/bin/dash\n");
|
|
56
60
|
ensureFile(vfs, "/etc/profile", `${[
|
|
57
|
-
"export PATH=/usr/local/bin:/usr/bin:/bin",
|
|
61
|
+
"export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
58
62
|
"export PS1='\\u@\\h:\\w\\$ '",
|
|
59
63
|
].join("\n")}\n`);
|
|
60
|
-
ensureFile(vfs, "/etc/issue", "Fortune GNU/Linux
|
|
64
|
+
ensureFile(vfs, "/etc/issue", "Fortune GNU/Linux 24.04 LTS \\n \\l\n");
|
|
65
|
+
ensureFile(vfs, "/etc/issue.net", "Fortune GNU/Linux 24.04 LTS\n");
|
|
61
66
|
ensureFile(vfs, "/etc/motd", ["", `Welcome to ${props.os}`, `Kernel: ${props.kernel}`, ""].join("\n"));
|
|
62
|
-
|
|
67
|
+
ensureFile(vfs, "/etc/lsb-release", `${[
|
|
68
|
+
"DISTRIB_ID=Fortune",
|
|
69
|
+
"DISTRIB_RELEASE=24.04",
|
|
70
|
+
"DISTRIB_CODENAME=nyx",
|
|
71
|
+
`DISTRIB_DESCRIPTION="${props.os}"`,
|
|
72
|
+
].join("\n")}\n`);
|
|
73
|
+
// APT — Fortune Nyx sources
|
|
63
74
|
ensureDir(vfs, "/etc/apt");
|
|
64
75
|
ensureDir(vfs, "/etc/apt/sources.list.d");
|
|
76
|
+
ensureDir(vfs, "/etc/apt/trusted.gpg.d");
|
|
77
|
+
ensureDir(vfs, "/etc/apt/keyrings");
|
|
65
78
|
ensureFile(vfs, "/etc/apt/sources.list", `${[
|
|
66
|
-
"# Fortune GNU/Linux package sources",
|
|
67
|
-
"deb [virtual] fortune://packages.fortune.local
|
|
68
|
-
"deb [virtual] fortune://
|
|
79
|
+
"# Fortune GNU/Linux package sources (Fortune 1.0 Nyx)",
|
|
80
|
+
"deb [virtual] fortune://packages.fortune.local nyx main contrib non-free",
|
|
81
|
+
"deb [virtual] fortune://packages.fortune.local nyx-updates main contrib non-free",
|
|
82
|
+
"deb [virtual] fortune://security.fortune.local nyx-security main",
|
|
69
83
|
].join("\n")}\n`);
|
|
84
|
+
ensureFile(vfs, "/etc/apt/apt.conf.d/70debconf", `// debconf config\n`);
|
|
70
85
|
// network
|
|
71
86
|
ensureDir(vfs, "/etc/network");
|
|
72
87
|
ensureFile(vfs, "/etc/network/interfaces", `${[
|
|
@@ -76,26 +91,56 @@ function bootstrapEtc(vfs, hostname, props) {
|
|
|
76
91
|
"auto eth0",
|
|
77
92
|
"iface eth0 inet dhcp",
|
|
78
93
|
].join("\n")}\n`);
|
|
94
|
+
ensureDir(vfs, "/etc/netplan");
|
|
95
|
+
ensureFile(vfs, "/etc/netplan/01-eth0.yaml", `${[
|
|
96
|
+
"network:",
|
|
97
|
+
" version: 2",
|
|
98
|
+
" ethernets:",
|
|
99
|
+
" eth0:",
|
|
100
|
+
" dhcp4: true",
|
|
101
|
+
].join("\n")}\n`);
|
|
79
102
|
ensureFile(vfs, "/etc/resolv.conf", "nameserver 1.1.1.1\nnameserver 8.8.8.8\n");
|
|
80
103
|
ensureFile(vfs, "/etc/hosts", `${[
|
|
81
104
|
"127.0.0.1 localhost",
|
|
82
105
|
`127.0.1.1 ${hostname}`,
|
|
83
106
|
"::1 localhost ip6-localhost ip6-loopback",
|
|
107
|
+
"fe00::0 ip6-localnet",
|
|
108
|
+
"ff00::0 ip6-mcastprefix",
|
|
109
|
+
"ff02::1 ip6-allnodes",
|
|
110
|
+
"ff02::2 ip6-allrouters",
|
|
111
|
+
].join("\n")}\n`);
|
|
112
|
+
ensureFile(vfs, "/etc/nsswitch.conf", `${[
|
|
113
|
+
"passwd: files systemd",
|
|
114
|
+
"group: files systemd",
|
|
115
|
+
"shadow: files",
|
|
116
|
+
"hosts: files dns",
|
|
117
|
+
"networks: files",
|
|
118
|
+
"protocols: db files",
|
|
119
|
+
"services: db files",
|
|
120
|
+
"ethers: db files",
|
|
121
|
+
"rpc: db files",
|
|
84
122
|
].join("\n")}\n`);
|
|
85
123
|
ensureDir(vfs, "/etc/cron.d");
|
|
124
|
+
ensureDir(vfs, "/etc/cron.daily");
|
|
125
|
+
ensureDir(vfs, "/etc/cron.hourly");
|
|
126
|
+
ensureDir(vfs, "/etc/cron.weekly");
|
|
127
|
+
ensureDir(vfs, "/etc/cron.monthly");
|
|
86
128
|
ensureDir(vfs, "/etc/init.d");
|
|
87
129
|
ensureDir(vfs, "/etc/systemd");
|
|
88
130
|
ensureDir(vfs, "/etc/systemd/system");
|
|
89
|
-
|
|
131
|
+
ensureDir(vfs, "/etc/systemd/system/multi-user.target.wants");
|
|
132
|
+
ensureDir(vfs, "/etc/systemd/network");
|
|
133
|
+
ensureFile(vfs, "/etc/systemd/system.conf", "[Manager]\nDefaultTimeoutStartSec=90s\nDefaultTimeoutStopSec=90s\n");
|
|
134
|
+
// fstab — virtio block devices matching Firecracker layout
|
|
90
135
|
ensureFile(vfs, "/etc/fstab", `${[
|
|
91
|
-
"# <file system> <mount point> <type>
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"tmpfs
|
|
96
|
-
"tmpfs
|
|
136
|
+
"# <file system> <mount point> <type> <options> <dump> <pass>",
|
|
137
|
+
"/dev/vda / ext4 rw,relatime,resuid=65534,resgid=65534 0 1",
|
|
138
|
+
"/dev/vdb /opt/rclone squashfs ro,relatime,errors=continue 0 0",
|
|
139
|
+
"tmpfs /tmp tmpfs defaults,noatime 0 0",
|
|
140
|
+
"tmpfs /run tmpfs defaults,noatime 0 0",
|
|
141
|
+
"tmpfs /dev/shm tmpfs rw,relatime 0 0",
|
|
97
142
|
].join("\n")}\n`);
|
|
98
|
-
// login.defs
|
|
143
|
+
// login.defs
|
|
99
144
|
ensureFile(vfs, "/etc/login.defs", `${[
|
|
100
145
|
"MAIL_DIR /var/mail",
|
|
101
146
|
"PASS_MAX_DAYS 99999",
|
|
@@ -112,25 +157,108 @@ function bootstrapEtc(vfs, hostname, props) {
|
|
|
112
157
|
].join("\n")}\n`);
|
|
113
158
|
// security + pam
|
|
114
159
|
ensureDir(vfs, "/etc/security");
|
|
115
|
-
ensureFile(vfs, "/etc/security/limits.conf", "# /etc/security/limits.conf\n");
|
|
160
|
+
ensureFile(vfs, "/etc/security/limits.conf", "# /etc/security/limits.conf\n* soft nofile 1024\n* hard nofile 65536\n");
|
|
116
161
|
ensureFile(vfs, "/etc/security/access.conf", "# /etc/security/access.conf\n");
|
|
117
162
|
ensureDir(vfs, "/etc/pam.d");
|
|
118
|
-
ensureFile(vfs, "/etc/pam.d/common-auth", "auth
|
|
119
|
-
ensureFile(vfs, "/etc/pam.d/common-account", "account
|
|
120
|
-
ensureFile(vfs, "/etc/pam.d/common-password", "password
|
|
121
|
-
ensureFile(vfs, "/etc/pam.d/common-session", "session required pam_unix.so\n");
|
|
163
|
+
ensureFile(vfs, "/etc/pam.d/common-auth", "auth [success=1 default=ignore] pam_unix.so nullok\nauth requisite pam_deny.so\nauth required pam_permit.so\n");
|
|
164
|
+
ensureFile(vfs, "/etc/pam.d/common-account", "account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so\naccount requisite pam_deny.so\naccount required pam_permit.so\n");
|
|
165
|
+
ensureFile(vfs, "/etc/pam.d/common-password", "password [success=1 default=ignore] pam_unix.so obscure sha512\npassword requisite pam_deny.so\npassword required pam_permit.so\n");
|
|
166
|
+
ensureFile(vfs, "/etc/pam.d/common-session", "session [default=1] pam_permit.so\nsession requisite pam_deny.so\nsession required pam_permit.so\nsession optional pam_umask.so\nsession required pam_unix.so\n");
|
|
122
167
|
ensureFile(vfs, "/etc/pam.d/sshd", "@include common-auth\n@include common-account\n@include common-session\n");
|
|
123
|
-
|
|
168
|
+
ensureFile(vfs, "/etc/pam.d/login", "@include common-auth\n@include common-account\n@include common-session\n");
|
|
169
|
+
ensureFile(vfs, "/etc/pam.d/sudo", "@include common-auth\n@include common-account\n@include common-session\n");
|
|
170
|
+
// sudo
|
|
124
171
|
ensureDir(vfs, "/etc/sudoers.d");
|
|
125
|
-
ensureFile(vfs, "/etc/sudoers", "
|
|
172
|
+
ensureFile(vfs, "/etc/sudoers", "Defaults\tenv_reset\nDefaults\tmail_badpass\nDefaults\tsecure_path=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"\nroot ALL=(ALL:ALL) ALL\n%sudo ALL=(ALL:ALL) ALL\n", 0o440);
|
|
173
|
+
ensureFile(vfs, "/etc/sudoers.d/README", "# Files in this directory are parsed by sudo, if the file is not a backup.\n", 0o440);
|
|
126
174
|
// ld
|
|
127
175
|
ensureFile(vfs, "/etc/ld.so.conf", "include /etc/ld.so.conf.d/*.conf\n");
|
|
128
176
|
ensureDir(vfs, "/etc/ld.so.conf.d");
|
|
129
177
|
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");
|
|
178
|
+
ensureFile(vfs, "/etc/ld.so.conf.d/fakeroot.conf", "/usr/lib/x86_64-linux-gnu/libfakeroot\n");
|
|
130
179
|
// locale + timezone
|
|
131
180
|
ensureFile(vfs, "/etc/locale.conf", "LANG=en_US.UTF-8\n");
|
|
181
|
+
ensureFile(vfs, "/etc/locale.gen", "en_US.UTF-8 UTF-8\n");
|
|
182
|
+
ensureFile(vfs, "/etc/default/locale", "LANG=en_US.UTF-8\n");
|
|
132
183
|
ensureFile(vfs, "/etc/timezone", "UTC\n");
|
|
133
184
|
ensureFile(vfs, "/etc/localtime", "UTC\n");
|
|
185
|
+
// environment
|
|
186
|
+
ensureFile(vfs, "/etc/environment", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n");
|
|
187
|
+
// adduser.conf
|
|
188
|
+
ensureFile(vfs, "/etc/adduser.conf", `${[
|
|
189
|
+
"DSHELL=/bin/bash",
|
|
190
|
+
"DHOME=/home",
|
|
191
|
+
"GROUPHOMES=no",
|
|
192
|
+
"LETTERHOMES=no",
|
|
193
|
+
"SKEL=/etc/skel",
|
|
194
|
+
"FIRST_SYSTEM_UID=100",
|
|
195
|
+
"LAST_SYSTEM_UID=999",
|
|
196
|
+
"FIRST_SYSTEM_GID=100",
|
|
197
|
+
"LAST_SYSTEM_GID=999",
|
|
198
|
+
"FIRST_UID=1000",
|
|
199
|
+
"LAST_UID=59999",
|
|
200
|
+
"FIRST_GID=1000",
|
|
201
|
+
"LAST_GID=59999",
|
|
202
|
+
"USERGROUPS=yes",
|
|
203
|
+
"NAME_REGEX=\"^[a-z][-a-z0-9_]*$\"",
|
|
204
|
+
"SYS_NAME_REGEX=\"^[a-z_][-a-z0-9_]*$\"",
|
|
205
|
+
].join("\n")}\n`);
|
|
206
|
+
// /etc/skel
|
|
207
|
+
ensureDir(vfs, "/etc/skel");
|
|
208
|
+
ensureFile(vfs, "/etc/skel/.bashrc", `${[
|
|
209
|
+
"# ~/.bashrc: executed by bash(1) for non-login shells.",
|
|
210
|
+
"case $- in",
|
|
211
|
+
" *i*) ;;",
|
|
212
|
+
" *) return;;",
|
|
213
|
+
"esac",
|
|
214
|
+
"HISTCONTROL=ignoreboth",
|
|
215
|
+
"HISTSIZE=1000",
|
|
216
|
+
"HISTFILESIZE=2000",
|
|
217
|
+
"shopt -s histappend",
|
|
218
|
+
"shopt -s checkwinsize",
|
|
219
|
+
"alias ll='ls -alF'",
|
|
220
|
+
"alias la='ls -A'",
|
|
221
|
+
"alias l='ls -CF'",
|
|
222
|
+
].join("\n")}\n`);
|
|
223
|
+
ensureFile(vfs, "/etc/skel/.bash_logout", "# ~/.bash_logout\n");
|
|
224
|
+
ensureFile(vfs, "/etc/skel/.profile", "# ~/.profile\n[ -n \"$BASH_VERSION\" ] && [ -f \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\"\n");
|
|
225
|
+
// alternatives
|
|
226
|
+
ensureDir(vfs, "/etc/alternatives");
|
|
227
|
+
const alternatives = [
|
|
228
|
+
["python3", "/usr/bin/python3.12"],
|
|
229
|
+
["python", "/usr/bin/python3.12"],
|
|
230
|
+
["editor", "/usr/bin/nano"],
|
|
231
|
+
["vi", "/usr/bin/nano"],
|
|
232
|
+
["cc", "/usr/bin/gcc"],
|
|
233
|
+
["gcc", "/usr/bin/gcc-13"],
|
|
234
|
+
["g++", "/usr/bin/g++-13"],
|
|
235
|
+
["java", "/usr/lib/jvm/java-21-openjdk-amd64/bin/java"],
|
|
236
|
+
["node", "/usr/bin/node"],
|
|
237
|
+
["npm", "/usr/bin/npm"],
|
|
238
|
+
["awk", "/usr/bin/mawk"],
|
|
239
|
+
["pager", "/usr/bin/less"],
|
|
240
|
+
];
|
|
241
|
+
for (const [name, target] of alternatives) {
|
|
242
|
+
ensureFile(vfs, `/etc/alternatives/${name}`, target);
|
|
243
|
+
}
|
|
244
|
+
// java
|
|
245
|
+
ensureDir(vfs, "/etc/java-21-openjdk");
|
|
246
|
+
ensureDir(vfs, "/etc/java-21-openjdk/security");
|
|
247
|
+
ensureFile(vfs, "/etc/java-21-openjdk/security/java.security", "# java.security\n");
|
|
248
|
+
ensureFile(vfs, "/etc/java-21-openjdk/logging.properties", "# logging.properties\n");
|
|
249
|
+
// misc
|
|
250
|
+
ensureFile(vfs, "/etc/bash.bashrc", "# System-wide .bashrc\n[ -z \"$PS1\" ] && return\n");
|
|
251
|
+
ensureFile(vfs, "/etc/inputrc", "# /etc/inputrc\n$include /etc/inputrc.d\nset bell-style none\n");
|
|
252
|
+
ensureFile(vfs, "/etc/magic", "# magic\n");
|
|
253
|
+
ensureFile(vfs, "/etc/magic.mime", "# magic.mime\n");
|
|
254
|
+
ensureFile(vfs, "/etc/papersize", "a4\n");
|
|
255
|
+
ensureFile(vfs, "/etc/ucf.conf", "# ucf.conf\n");
|
|
256
|
+
ensureFile(vfs, "/etc/gai.conf", "# getaddrinfo() configuration\nlabel ::1/128 0\nprecedence ::1/128 50\n");
|
|
257
|
+
ensureFile(vfs, "/etc/services", "# Network services\nftp 21/tcp\nssh 22/tcp\nsmtp 25/tcp\nhttp 80/tcp\nhttps 443/tcp\n");
|
|
258
|
+
ensureFile(vfs, "/etc/protocols", "# protocols\nip 0 IP\nicmp 1 ICMP\ntcp 6 TCP\nudp 17 UDP\n");
|
|
259
|
+
ensureDir(vfs, "/etc/profile.d");
|
|
260
|
+
ensureFile(vfs, "/etc/profile.d/01-locale-fix.sh", "[ -z \"$LANG\" ] && export LANG=en_US.UTF-8\n");
|
|
261
|
+
ensureFile(vfs, "/etc/profile.d/apps-bin-path.sh", "export PATH=\"$PATH:/snap/bin\"\n");
|
|
134
262
|
}
|
|
135
263
|
// ─── /etc/passwd + /etc/group + /etc/shadow ─────────────────────────────────
|
|
136
264
|
/**
|
|
@@ -142,8 +270,26 @@ export function syncEtcPasswd(vfs, users) {
|
|
|
142
270
|
const passwdLines = [
|
|
143
271
|
"root:x:0:0:root:/root:/bin/bash",
|
|
144
272
|
"daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin",
|
|
273
|
+
"bin:x:2:2:bin:/bin:/usr/sbin/nologin",
|
|
274
|
+
"sys:x:3:3:sys:/dev:/usr/sbin/nologin",
|
|
275
|
+
"sync:x:4:65534:sync:/bin:/bin/sync",
|
|
276
|
+
"games:x:5:60:games:/usr/games:/usr/sbin/nologin",
|
|
277
|
+
"man:x:6:12:man:/var/cache/man:/usr/sbin/nologin",
|
|
278
|
+
"lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin",
|
|
279
|
+
"mail:x:8:8:mail:/var/mail:/usr/sbin/nologin",
|
|
280
|
+
"news:x:9:9:news:/var/spool/news:/usr/sbin/nologin",
|
|
281
|
+
"uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin",
|
|
282
|
+
"proxy:x:13:13:proxy:/bin:/usr/sbin/nologin",
|
|
145
283
|
"www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin",
|
|
284
|
+
"backup:x:34:34:backup:/var/backups:/usr/sbin/nologin",
|
|
285
|
+
"list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin",
|
|
286
|
+
"irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin",
|
|
287
|
+
"_apt:x:42:65534::/nonexistent:/usr/sbin/nologin",
|
|
146
288
|
"nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin",
|
|
289
|
+
"messagebus:x:100:106::/nonexistent:/usr/sbin/nologin",
|
|
290
|
+
"systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin",
|
|
291
|
+
"systemd-resolve:x:999:999:systemd Resolver:/:/usr/sbin/nologin",
|
|
292
|
+
"polkitd:x:997:997:polkit:/nonexistent:/usr/sbin/nologin",
|
|
147
293
|
];
|
|
148
294
|
let uid = 1000;
|
|
149
295
|
for (const u of userList) {
|
|
@@ -153,18 +299,63 @@ export function syncEtcPasswd(vfs, users) {
|
|
|
153
299
|
uid++;
|
|
154
300
|
}
|
|
155
301
|
vfs.writeFile("/etc/passwd", `${passwdLines.join("\n")}\n`);
|
|
302
|
+
const sudoers = userList.filter((u) => users.isSudoer(u)).join(",");
|
|
303
|
+
const nonRootUsers = userList.filter((u) => u !== "root").join(",");
|
|
156
304
|
const groupLines = [
|
|
157
305
|
"root:x:0:",
|
|
158
306
|
"daemon:x:1:",
|
|
159
|
-
|
|
160
|
-
|
|
307
|
+
"bin:x:2:",
|
|
308
|
+
"sys:x:3:",
|
|
309
|
+
"adm:x:4:syslog",
|
|
310
|
+
"tty:x:5:",
|
|
311
|
+
"disk:x:6:",
|
|
312
|
+
"lp:x:7:",
|
|
313
|
+
"mail:x:8:",
|
|
314
|
+
"news:x:9:",
|
|
315
|
+
"uucp:x:10:",
|
|
316
|
+
"man:x:12:",
|
|
317
|
+
"proxy:x:13:",
|
|
318
|
+
"kmem:x:15:",
|
|
319
|
+
"dialout:x:20:",
|
|
320
|
+
"fax:x:21:",
|
|
321
|
+
"voice:x:22:",
|
|
322
|
+
"cdrom:x:24:",
|
|
323
|
+
"floppy:x:25:",
|
|
324
|
+
"tape:x:26:",
|
|
325
|
+
`sudo:x:27:${sudoers}`,
|
|
326
|
+
"audio:x:29:",
|
|
327
|
+
"dip:x:30:",
|
|
328
|
+
"www-data:x:33:",
|
|
329
|
+
"backup:x:34:",
|
|
330
|
+
"operator:x:37:",
|
|
331
|
+
"list:x:38:",
|
|
332
|
+
"irc:x:39:",
|
|
333
|
+
"src:x:40:",
|
|
334
|
+
"_apt:x:42:",
|
|
335
|
+
"shadow:x:42:",
|
|
336
|
+
"utmp:x:43:",
|
|
337
|
+
"video:x:44:",
|
|
338
|
+
"sasl:x:45:",
|
|
339
|
+
"plugdev:x:46:",
|
|
340
|
+
"staff:x:50:",
|
|
341
|
+
"games:x:60:",
|
|
342
|
+
`users:x:100:${nonRootUsers}`,
|
|
161
343
|
"nogroup:x:65534:",
|
|
344
|
+
"messagebus:x:106:",
|
|
345
|
+
"systemd-network:x:998:",
|
|
346
|
+
"systemd-resolve:x:999:",
|
|
347
|
+
"polkitd:x:997:",
|
|
162
348
|
];
|
|
163
349
|
vfs.writeFile("/etc/group", `${groupLines.join("\n")}\n`);
|
|
164
|
-
// shadow — fake hashes, never real credentials
|
|
165
350
|
const shadowLines = [
|
|
166
351
|
"root:*:19000:0:99999:7:::",
|
|
167
352
|
"daemon:*:19000:0:99999:7:::",
|
|
353
|
+
"nobody:*:19000:0:99999:7:::",
|
|
354
|
+
"messagebus:*:19000:0:99999:7:::",
|
|
355
|
+
"_apt:*:19000:0:99999:7:::",
|
|
356
|
+
"systemd-network:!:19000:::::::",
|
|
357
|
+
"systemd-resolve:!:19000:::::::",
|
|
358
|
+
"polkitd:!:19000:::::::",
|
|
168
359
|
];
|
|
169
360
|
for (const u of userList) {
|
|
170
361
|
if (u === "root")
|
|
@@ -174,50 +365,131 @@ export function syncEtcPasswd(vfs, users) {
|
|
|
174
365
|
vfs.writeFile("/etc/shadow", `${shadowLines.join("\n")}\n`, { mode: 0o640 });
|
|
175
366
|
}
|
|
176
367
|
// ─── /proc helpers ───────────────────────────────────────────────────────────
|
|
177
|
-
/** Derive a stable virtual PID from a tty string e.g. "pts/0" → 1000 */
|
|
178
368
|
function ttyToPid(tty) {
|
|
179
369
|
const match = tty.match(/(\d+)$/);
|
|
180
370
|
return 1000 + (match?.[1] ? parseInt(match[1], 10) : 0);
|
|
181
371
|
}
|
|
182
|
-
/** Write /proc/<pid>/ subtree for a single virtual process */
|
|
183
372
|
function writeProcPid(vfs, pid, username, _tty, cmdline, startedAt, env) {
|
|
184
373
|
const dir = `/proc/${pid}`;
|
|
185
374
|
ensureDir(vfs, dir);
|
|
186
375
|
ensureDir(vfs, `${dir}/fd`);
|
|
187
376
|
ensureDir(vfs, `${dir}/fdinfo`);
|
|
377
|
+
ensureDir(vfs, `${dir}/net`);
|
|
188
378
|
const uptimeSec = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000);
|
|
189
379
|
const comm = cmdline.split(/\s+/)[0] ?? "bash";
|
|
190
380
|
write(vfs, `${dir}/cmdline`, `${cmdline.replace(/\s+/g, "\0")}\0`);
|
|
191
381
|
write(vfs, `${dir}/comm`, comm);
|
|
192
382
|
write(vfs, `${dir}/status`, `${[
|
|
193
383
|
`Name: ${comm}`,
|
|
384
|
+
`Umask: 0022`,
|
|
194
385
|
`State: S (sleeping)`,
|
|
386
|
+
`Tgid: ${pid}`,
|
|
195
387
|
`Pid: ${pid}`,
|
|
196
388
|
`PPid: 1`,
|
|
389
|
+
`TracerPid: 0`,
|
|
197
390
|
`Uid: 0\t0\t0\t0`,
|
|
198
391
|
`Gid: 0\t0\t0\t0`,
|
|
199
|
-
`
|
|
200
|
-
`
|
|
392
|
+
`FDSize: 64`,
|
|
393
|
+
`Groups:`,
|
|
394
|
+
`VmPeak: 20480 kB`,
|
|
395
|
+
`VmSize: 16384 kB`,
|
|
396
|
+
`VmLck: 0 kB`,
|
|
397
|
+
`VmPin: 0 kB`,
|
|
398
|
+
`VmHWM: 4096 kB`,
|
|
399
|
+
`VmRSS: 4096 kB`,
|
|
400
|
+
`RssAnon: 512 kB`,
|
|
401
|
+
`RssFile: 3584 kB`,
|
|
402
|
+
`RssShmem: 0 kB`,
|
|
403
|
+
`VmData: 2048 kB`,
|
|
404
|
+
`VmStk: 132 kB`,
|
|
405
|
+
`VmExe: 924 kB`,
|
|
406
|
+
`VmLib: 2744 kB`,
|
|
407
|
+
`VmPTE: 52 kB`,
|
|
408
|
+
`VmSwap: 0 kB`,
|
|
201
409
|
`Threads: 1`,
|
|
410
|
+
`SigQ: 0/31968`,
|
|
411
|
+
`SigPnd: 0000000000000000`,
|
|
412
|
+
`SigBlk: 0000000000010000`,
|
|
413
|
+
`SigIgn: 0000000000380004`,
|
|
414
|
+
`SigCgt: 000000004b817efb`,
|
|
415
|
+
`CapInh: 0000000000000000`,
|
|
416
|
+
`CapPrm: 000001ffffffffff`,
|
|
417
|
+
`CapEff: 000001ffffffffff`,
|
|
418
|
+
`CapBnd: 000001ffffffffff`,
|
|
419
|
+
`CapAmb: 0000000000000000`,
|
|
420
|
+
`NoNewPrivs: 0`,
|
|
421
|
+
`Seccomp: 0`,
|
|
422
|
+
`voluntary_ctxt_switches: 100`,
|
|
423
|
+
`nonvoluntary_ctxt_switches: 10`,
|
|
202
424
|
].join("\n")}\n`);
|
|
203
|
-
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
|
|
425
|
+
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 16777216 4096 18446744073709551615 93824992235520 93824992290000 140737488347024 0 0 0 65536 3686404 1266761467 1 0 0 17 0 0 0 0 0 0\n`);
|
|
426
|
+
write(vfs, `${dir}/statm`, `4096 1024 768 231 0 512 0\n`);
|
|
204
427
|
write(vfs, `${dir}/environ`, `${Object.entries(env).map(([k, v]) => `${k}=${v}`).join("\0")}\0`);
|
|
205
428
|
write(vfs, `${dir}/cwd`, `/home/${username}\0`);
|
|
206
429
|
write(vfs, `${dir}/exe`, "/bin/bash\0");
|
|
207
|
-
|
|
430
|
+
write(vfs, `${dir}/maps`, "00400000-004e7000 r-xp 00000000 fd:00 131073 /bin/bash\n" +
|
|
431
|
+
"006e7000-006e8000 r--p 000e7000 fd:00 131073 /bin/bash\n" +
|
|
432
|
+
"006e8000-006f1000 rw-p 000e8000 fd:00 131073 /bin/bash\n" +
|
|
433
|
+
"7fff00000000-7fff00001000 rw-p 00000000 00:00 0 [stack]\n" +
|
|
434
|
+
"7fff00000000-7fff00001000 r-xp 00000000 00:00 0 [vdso]\n");
|
|
435
|
+
write(vfs, `${dir}/limits`, `${[
|
|
436
|
+
"Limit Soft Limit Hard Limit Units",
|
|
437
|
+
"Max cpu time unlimited unlimited seconds",
|
|
438
|
+
"Max file size unlimited unlimited bytes",
|
|
439
|
+
"Max data size unlimited unlimited bytes",
|
|
440
|
+
"Max stack size 8388608 unlimited bytes",
|
|
441
|
+
"Max core file size 0 unlimited bytes",
|
|
442
|
+
"Max resident set unlimited unlimited bytes",
|
|
443
|
+
"Max processes 31968 31968 processes",
|
|
444
|
+
"Max open files 1048576 1048576 files",
|
|
445
|
+
"Max locked memory 8388608 8388608 bytes",
|
|
446
|
+
"Max address space unlimited unlimited bytes",
|
|
447
|
+
"Max file locks unlimited unlimited locks",
|
|
448
|
+
"Max pending signals 31968 31968 signals",
|
|
449
|
+
"Max msgqueue size 819200 819200 bytes",
|
|
450
|
+
"Max nice priority 0 0",
|
|
451
|
+
"Max realtime priority 0 0",
|
|
452
|
+
"Max realtime timeout unlimited unlimited us",
|
|
453
|
+
].join("\n")}\n`);
|
|
454
|
+
write(vfs, `${dir}/io`, "rchar: 1048576\nwchar: 65536\nsyscr: 512\nsyscw: 64\nread_bytes: 0\nwrite_bytes: 0\ncancelled_write_bytes: 0\n");
|
|
455
|
+
write(vfs, `${dir}/oom_score`, "0\n");
|
|
456
|
+
write(vfs, `${dir}/oom_score_adj`, "0\n");
|
|
457
|
+
write(vfs, `${dir}/loginuid`, "0\n");
|
|
458
|
+
write(vfs, `${dir}/wchan`, "poll_schedule_timeout\n");
|
|
459
|
+
write(vfs, `${dir}/schedstat`, "1000000 0 1\n");
|
|
208
460
|
for (const fd of ["0", "1", "2"]) {
|
|
209
461
|
ensureFile(vfs, `${dir}/fd/${fd}`, "");
|
|
462
|
+
ensureFile(vfs, `${dir}/fdinfo/${fd}`, `pos:\t0\nflags:\t0${fd === "0" ? "2" : fd === "1" ? "1" : "1"}02\nmnt_id:\t13\n`);
|
|
210
463
|
}
|
|
211
464
|
}
|
|
212
465
|
// ─── /proc boot log ──────────────────────────────────────────────────────────
|
|
213
466
|
function bootProcLog(vfs, props) {
|
|
214
467
|
ensureDir(vfs, "/proc/boot");
|
|
215
468
|
ensureFile(vfs, "/proc/boot/log", `${[
|
|
216
|
-
|
|
217
|
-
"[ 0.
|
|
218
|
-
"[ 0.
|
|
219
|
-
"[ 0.
|
|
220
|
-
"[ 0.
|
|
469
|
+
`[ 0.000000] Linux version ${props.kernel} (fortune@build) #1 SMP PREEMPT_DYNAMIC`,
|
|
470
|
+
"[ 0.000000] Command line: console=ttyS0 reboot=k panic=1 nomodule random.trust_cpu=1 ipv6.disable=1",
|
|
471
|
+
"[ 0.000060] BIOS-provided physical RAM map:",
|
|
472
|
+
"[ 0.000070] ACPI: RSDP 0x00000000000F05B0 000014 (v00 BOCHS )",
|
|
473
|
+
"[ 0.000120] PCI: Using configuration type 1 for base access",
|
|
474
|
+
"[ 0.000240] clocksource: tsc-early: mask: 0xffffffffffffffff",
|
|
475
|
+
"[ 0.000320] ACPI: IRQ0 used by override",
|
|
476
|
+
"[ 0.000420] Initializing cgroup subsys cpuset",
|
|
477
|
+
"[ 0.000440] Initializing cgroup subsys cpu",
|
|
478
|
+
"[ 0.000450] Initializing cgroup subsys cpuacct",
|
|
479
|
+
"[ 0.000460] Linux agpgart interface v0.103",
|
|
480
|
+
"[ 0.000480] PCI: pci_cache_line_size set to 64 bytes",
|
|
481
|
+
"[ 0.000510] virtio-pci 0000:00:01.0: runtime IRQs not yet assigned",
|
|
482
|
+
"[ 0.000680] NET: Registered PF_INET6 protocol family",
|
|
483
|
+
"[ 0.000720] Freeing unused kernel image (initmem) memory",
|
|
484
|
+
"[ 0.000760] Write protecting the kernel read-only data",
|
|
485
|
+
"[ 0.000800] Run /sbin/init as init process",
|
|
486
|
+
"[ 0.001200] systemd[1]: Detected virtualization kvm",
|
|
487
|
+
"[ 0.001300] systemd[1]: Detected architecture x86-64",
|
|
488
|
+
"[ 0.002000] systemd[1]: Starting Fortune GNU/Linux...",
|
|
489
|
+
"[ 0.003000] systemd[1]: Started Journal Service",
|
|
490
|
+
"[ 0.010000] EXT4-fs (vda): mounted filesystem",
|
|
491
|
+
"[ 0.020000] systemd[1]: Reached target Basic System",
|
|
492
|
+
"[ 0.030000] systemd[1]: Started Login Service",
|
|
221
493
|
].join("\n")}\n`);
|
|
222
494
|
ensureFile(vfs, "/proc/boot/version", `Linux ${props.kernel} (virtual)\n`);
|
|
223
495
|
}
|
|
@@ -238,89 +510,243 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [])
|
|
|
238
510
|
const totalMemKb = Math.floor(os.totalmem() / 1024);
|
|
239
511
|
const freeMemKb = Math.floor(os.freemem() / 1024);
|
|
240
512
|
const availMemKb = Math.floor(freeMemKb * 0.95);
|
|
513
|
+
const buffersKb = Math.floor(totalMemKb * 0.03);
|
|
514
|
+
const cachedKb = Math.floor(totalMemKb * 0.08);
|
|
515
|
+
const shmemKb = Math.floor(totalMemKb * 0.005);
|
|
516
|
+
const slabKb = Math.floor(totalMemKb * 0.02);
|
|
517
|
+
const pageTablesKb = Math.floor(totalMemKb * 0.001);
|
|
241
518
|
write(vfs, "/proc/meminfo", `${[
|
|
242
519
|
`MemTotal: ${String(totalMemKb).padStart(10)} kB`,
|
|
243
520
|
`MemFree: ${String(freeMemKb).padStart(10)} kB`,
|
|
244
521
|
`MemAvailable: ${String(availMemKb).padStart(10)} kB`,
|
|
245
|
-
`Buffers: ${String(
|
|
246
|
-
`Cached: ${String(
|
|
247
|
-
`
|
|
248
|
-
`
|
|
522
|
+
`Buffers: ${String(buffersKb).padStart(10)} kB`,
|
|
523
|
+
`Cached: ${String(cachedKb).padStart(10)} kB`,
|
|
524
|
+
`SwapCached: ${String(0).padStart(10)} kB`,
|
|
525
|
+
`Active: ${String(Math.floor((buffersKb + cachedKb) * 1.2)).padStart(10)} kB`,
|
|
526
|
+
`Inactive: ${String(Math.floor(cachedKb * 0.6)).padStart(10)} kB`,
|
|
527
|
+
`Active(anon): ${String(Math.floor(totalMemKb * 0.001)).padStart(10)} kB`,
|
|
528
|
+
`Inactive(anon): ${String(Math.floor(totalMemKb * 0.006)).padStart(10)} kB`,
|
|
529
|
+
`Active(file): ${String(Math.floor(cachedKb * 1.2)).padStart(10)} kB`,
|
|
530
|
+
`Inactive(file): ${String(Math.floor(cachedKb * 0.6)).padStart(10)} kB`,
|
|
531
|
+
`Unevictable: ${String(0).padStart(10)} kB`,
|
|
532
|
+
`Mlocked: ${String(0).padStart(10)} kB`,
|
|
533
|
+
`SwapTotal: ${String(0).padStart(10)} kB`,
|
|
534
|
+
`SwapFree: ${String(0).padStart(10)} kB`,
|
|
535
|
+
`Zswap: ${String(0).padStart(10)} kB`,
|
|
536
|
+
`Zswapped: ${String(0).padStart(10)} kB`,
|
|
537
|
+
`Dirty: ${String(Math.floor(Math.random() * 64)).padStart(10)} kB`,
|
|
538
|
+
`Writeback: ${String(0).padStart(10)} kB`,
|
|
539
|
+
`AnonPages: ${String(Math.floor(totalMemKb * 0.001)).padStart(10)} kB`,
|
|
540
|
+
`Mapped: ${String(Math.floor(cachedKb * 0.4)).padStart(10)} kB`,
|
|
541
|
+
`Shmem: ${String(shmemKb).padStart(10)} kB`,
|
|
542
|
+
`KReclaimable: ${String(Math.floor(slabKb * 0.6)).padStart(10)} kB`,
|
|
543
|
+
`Slab: ${String(slabKb).padStart(10)} kB`,
|
|
544
|
+
`SReclaimable: ${String(Math.floor(slabKb * 0.6)).padStart(10)} kB`,
|
|
545
|
+
`SUnreclaim: ${String(Math.floor(slabKb * 0.4)).padStart(10)} kB`,
|
|
546
|
+
`KernelStack: ${String(Math.floor(totalMemKb * 0.0005)).padStart(10)} kB`,
|
|
547
|
+
`PageTables: ${String(pageTablesKb).padStart(10)} kB`,
|
|
548
|
+
`NFS_Unstable: ${String(0).padStart(10)} kB`,
|
|
549
|
+
`Bounce: ${String(0).padStart(10)} kB`,
|
|
550
|
+
`WritebackTmp: ${String(0).padStart(10)} kB`,
|
|
551
|
+
`CommitLimit: ${String(Math.floor(totalMemKb * 0.5)).padStart(10)} kB`,
|
|
552
|
+
`Committed_AS: ${String(Math.floor(totalMemKb * 0.05)).padStart(10)} kB`,
|
|
553
|
+
`VmallocTotal: ${String(35184372087808 / 1024).padStart(10)} kB`,
|
|
554
|
+
`VmallocUsed: ${String(Math.floor(totalMemKb * 0.01)).padStart(10)} kB`,
|
|
555
|
+
`VmallocChunk: ${String(0).padStart(10)} kB`,
|
|
556
|
+
`Percpu: ${String(Math.floor(totalMemKb * 0.0001)).padStart(10)} kB`,
|
|
557
|
+
`HardwareCorrupted: ${String(0).padStart(6)} kB`,
|
|
558
|
+
`AnonHugePages: ${String(0).padStart(10)} kB`,
|
|
559
|
+
`ShmemHugePages: ${String(0).padStart(10)} kB`,
|
|
560
|
+
`ShmemPmdMapped: ${String(0).padStart(10)} kB`,
|
|
561
|
+
`FileHugePages: ${String(0).padStart(10)} kB`,
|
|
562
|
+
`FilePmdMapped: ${String(0).padStart(10)} kB`,
|
|
563
|
+
`HugePages_Total: ${String(0).padStart(8)}`,
|
|
564
|
+
`HugePages_Free: ${String(0).padStart(8)}`,
|
|
565
|
+
`HugePages_Rsvd: ${String(0).padStart(8)}`,
|
|
566
|
+
`HugePages_Surp: ${String(0).padStart(8)}`,
|
|
567
|
+
`Hugepagesize: ${String(2048).padStart(10)} kB`,
|
|
568
|
+
`Hugetlb: ${String(0).padStart(10)} kB`,
|
|
569
|
+
`DirectMap4k: ${String(Math.floor(totalMemKb * 0.02)).padStart(10)} kB`,
|
|
570
|
+
`DirectMap2M: ${String(Math.floor(totalMemKb * 0.98)).padStart(10)} kB`,
|
|
249
571
|
].join("\n")}\n`);
|
|
250
|
-
// cpuinfo — real host CPU passthrough
|
|
572
|
+
// cpuinfo — real host CPU passthrough + x86 feature flags matching Firecracker Xeon
|
|
251
573
|
const cpus = os.cpus();
|
|
252
574
|
const cpuLines = [];
|
|
253
575
|
for (let i = 0; i < cpus.length; i++) {
|
|
254
576
|
const c = cpus[i];
|
|
255
577
|
if (!c)
|
|
256
578
|
continue;
|
|
257
|
-
cpuLines.push(`processor\t: ${i}`, `model name\t: ${c.model}`, `cpu MHz\t\t: ${c.speed.toFixed(3)}`, `cache size\t:
|
|
579
|
+
cpuLines.push(`processor\t: ${i}`, `vendor_id\t: GenuineIntel`, `cpu family\t: 6`, `model\t\t: 85`, `model name\t: ${c.model}`, `stepping\t: 7`, `microcode\t: 0x1`, `cpu MHz\t\t: ${c.speed.toFixed(3)}`, `cache size\t: 33792 KB`, `physical id\t: 0`, `siblings\t: ${cpus.length}`, `core id\t\t: ${i}`, `cpu cores\t: ${cpus.length}`, `apicid\t\t: ${i}`, `initial apicid\t: ${i}`, `fpu\t\t: yes`, `fpu_exception\t: yes`, `cpuid level\t: 13`, `wp\t\t: yes`, `flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat umip avx512_vnni md_clear arch_capabilities`, `bugs\t\t: spectre_v1 spectre_v2 spec_store_bypass swapgs taa mmio_stale_data retbleed eibrs_pbrsb bhi ibpb_no_ret spectre_v2_user its`, `bogomips\t: ${(c.speed * 2 / 1000).toFixed(2)}`, `clflush size\t: 64`, `cache_alignment\t: 64`, `address sizes\t: 46 bits physical, 48 bits virtual`, `power management:`, "");
|
|
258
580
|
}
|
|
259
581
|
write(vfs, "/proc/cpuinfo", `${cpuLines.join("\n")}\n`);
|
|
260
|
-
write(vfs, "/proc/version", `Linux version ${props.kernel} (fortune@build) (gcc
|
|
582
|
+
write(vfs, "/proc/version", `Linux version ${props.kernel} (fortune@nyx-build) (gcc (Fortune 13.3.0-nyx1) 13.3.0, GNU ld (GNU Binutils for Fortune) 2.42) #2 SMP PREEMPT_DYNAMIC\n`);
|
|
261
583
|
write(vfs, "/proc/hostname", `${hostname}\n`);
|
|
262
|
-
// loadavg
|
|
263
|
-
const load = (Math.random() * 0.
|
|
584
|
+
// loadavg
|
|
585
|
+
const load = (Math.random() * 0.3).toFixed(2);
|
|
264
586
|
const numProcs = 1 + sessions.length;
|
|
265
587
|
write(vfs, "/proc/loadavg", `${load} ${load} ${load} ${numProcs}/${numProcs} 1\n`);
|
|
266
|
-
// /proc/
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
"
|
|
271
|
-
"
|
|
272
|
-
"
|
|
588
|
+
// /proc/cmdline — Firecracker boot args
|
|
589
|
+
write(vfs, "/proc/cmdline", `console=ttyS0 reboot=k panic=1 nomodule random.trust_cpu=1 ipv6.disable=1 swiotlb=noforce rdinit=/process_api init_on_free=1 -- --firecracker-init --addr 0.0.0.0:2024 --max-ws-buffer-size 32768 --block-local-connections\n`);
|
|
590
|
+
// /proc/filesystems — matching real container
|
|
591
|
+
write(vfs, "/proc/filesystems", `${[
|
|
592
|
+
"nodev\tsysfs",
|
|
593
|
+
"nodev\ttmpfs",
|
|
594
|
+
"nodev\tbdev",
|
|
595
|
+
"nodev\tproc",
|
|
596
|
+
"nodev\tcgroup",
|
|
597
|
+
"nodev\tcgroup2",
|
|
598
|
+
"nodev\tcpuset",
|
|
599
|
+
"nodev\tdevtmpfs",
|
|
600
|
+
"nodev\tbinfmt_misc",
|
|
601
|
+
"nodev\tdebugfs",
|
|
602
|
+
"nodev\tsecurityfs",
|
|
603
|
+
"nodev\tsockfs",
|
|
604
|
+
"nodev\tbpf",
|
|
605
|
+
"nodev\tpipefs",
|
|
606
|
+
"nodev\tramfs",
|
|
607
|
+
"nodev\thugetlbfs",
|
|
608
|
+
"nodev\trpc_pipefs",
|
|
609
|
+
"nodev\tdevpts",
|
|
610
|
+
"\text3",
|
|
611
|
+
"\text2",
|
|
612
|
+
"\text4",
|
|
613
|
+
"\tsquashfs",
|
|
614
|
+
"nodev\tnfs",
|
|
615
|
+
"nodev\tnfs4",
|
|
616
|
+
"nodev\tautofs",
|
|
617
|
+
"\tfuseblk",
|
|
618
|
+
"nodev\tfuse",
|
|
619
|
+
"nodev\tfusectl",
|
|
620
|
+
"nodev\toverlay",
|
|
621
|
+
"\txfs",
|
|
622
|
+
"nodev\tmqueue",
|
|
623
|
+
"nodev\tselinuxfs",
|
|
624
|
+
"nodev\tpstore",
|
|
273
625
|
].join("\n")}\n`);
|
|
274
|
-
|
|
275
|
-
ensureFile(vfs, "/proc/net/tcp", " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
276
|
-
ensureFile(vfs, "/proc/net/tcp6", " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
277
|
-
// /proc/cmdline — kernel boot args
|
|
278
|
-
write(vfs, "/proc/cmdline", `BOOT_IMAGE=/boot/vmlinuz-${props.kernel} root=/dev/sda2 ro quiet splash\n`);
|
|
279
|
-
// /proc/filesystems
|
|
280
|
-
ensureFile(vfs, "/proc/filesystems", `${["nodev\tsysfs", "nodev\ttmpfs", "nodev\tproc", "nodev\tdevtmpfs", "\text4", "\tvfat", "nodev\tsquashfs", "nodev\toverlay"].join("\n")}\n`);
|
|
281
|
-
// /proc/mounts (= /proc/self/mounts)
|
|
626
|
+
// /proc/mounts — virtio block device layout
|
|
282
627
|
const mountsContent = `${[
|
|
283
|
-
"
|
|
284
|
-
"
|
|
285
|
-
"devtmpfs /dev devtmpfs rw,
|
|
628
|
+
"proc /proc proc rw,relatime 0 0",
|
|
629
|
+
"sysfs /sys sysfs rw,relatime 0 0",
|
|
630
|
+
"devtmpfs /dev devtmpfs rw,relatime,size=2045672k,nr_inodes=511418,mode=755 0 0",
|
|
631
|
+
"tmpfs /dev/shm tmpfs rw,relatime 0 0",
|
|
632
|
+
"devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0",
|
|
633
|
+
"tmpfs /sys/fs/cgroup tmpfs rw,relatime 0 0",
|
|
634
|
+
"cgroup /sys/fs/cgroup/cpu cgroup rw,relatime,cpu 0 0",
|
|
635
|
+
"cgroup /sys/fs/cgroup/cpuacct cgroup rw,relatime,cpuacct 0 0",
|
|
636
|
+
"cgroup /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0",
|
|
637
|
+
"cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0",
|
|
638
|
+
"cgroup /sys/fs/cgroup/freezer cgroup rw,relatime,freezer 0 0",
|
|
639
|
+
"cgroup /sys/fs/cgroup/blkio cgroup rw,relatime,blkio 0 0",
|
|
640
|
+
"cgroup /sys/fs/cgroup/pids cgroup rw,relatime,pids 0 0",
|
|
641
|
+
"cgroup2 /sys/fs/cgroup/unified cgroup2 rw,relatime 0 0",
|
|
642
|
+
"/dev/vda / ext4 rw,relatime,resuid=65534,resgid=65534 0 0",
|
|
643
|
+
"/dev/vdb /opt/rclone squashfs ro,relatime,errors=continue 0 0",
|
|
286
644
|
"tmpfs /run tmpfs rw,nosuid,nodev,noexec,relatime,size=204800k,mode=755 0 0",
|
|
287
645
|
"tmpfs /tmp tmpfs rw,nosuid,nodev,noatime 0 0",
|
|
288
|
-
"/dev/sda2 / ext4 rw,relatime,errors=remount-ro 0 0",
|
|
289
|
-
"/dev/sda1 /boot ext4 rw,relatime 0 0",
|
|
290
|
-
"tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0",
|
|
291
|
-
"devpts /dev/pts devpts rw,nosuid,noexec,relatime,mode=620,ptmxmode=000 0 0",
|
|
292
|
-
"squashfs /snap/core squashfs ro,nodev,relatime 0 0",
|
|
293
646
|
].join("\n")}\n`;
|
|
294
647
|
write(vfs, "/proc/mounts", mountsContent);
|
|
295
648
|
ensureDir(vfs, "/proc/self");
|
|
296
649
|
write(vfs, "/proc/self/mounts", mountsContent);
|
|
297
|
-
// /proc/
|
|
650
|
+
// /proc/net
|
|
651
|
+
ensureDir(vfs, "/proc/net");
|
|
652
|
+
write(vfs, "/proc/net/dev", `${[
|
|
653
|
+
"Inter-| Receive | Transmit",
|
|
654
|
+
" face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed",
|
|
655
|
+
" lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
|
|
656
|
+
" eth0: 128628 1230 0 19 0 0 0 0 52027469 2045 0 0 0 0 0 0",
|
|
657
|
+
].join("\n")}\n`);
|
|
658
|
+
write(vfs, "/proc/net/if_inet6", "");
|
|
659
|
+
write(vfs, "/proc/net/tcp", " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
660
|
+
write(vfs, "/proc/net/tcp6", " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
661
|
+
write(vfs, "/proc/net/udp", " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
662
|
+
write(vfs, "/proc/net/route", "Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n" +
|
|
663
|
+
"eth0\t00000000\t0101A8C0\t0003\t0\t0\t100\t00000000\t0\t0\t0\n" +
|
|
664
|
+
"eth0\t0000A8C0\t00000000\t0001\t0\t0\t100\t00FFFFFF\t0\t0\t0\n");
|
|
665
|
+
write(vfs, "/proc/net/arp", "IP address HW type Flags HW address Mask Device\n");
|
|
666
|
+
write(vfs, "/proc/net/fib_trie", "Local:\n +-- 0.0.0.0/0 3 0 5\n");
|
|
667
|
+
write(vfs, "/proc/net/unix", "Num RefCount Protocol Flags Type St Inode Path\n" +
|
|
668
|
+
"0000000000000000: 00000002 00000000 00010000 0001 01 10000 /run/dbus/system_bus_socket\n");
|
|
669
|
+
write(vfs, "/proc/net/sockstat", "sockets: used 8\nTCP: inuse 0 orphan 0 tw 0 alloc 0 mem 0\nUDP: inuse 0 mem 0\nUDPLITE: inuse 0\nRAW: inuse 0\nFRAG: inuse 0 memory 0\n");
|
|
670
|
+
// /proc/partitions — virtio block devices
|
|
298
671
|
write(vfs, "/proc/partitions", `${[
|
|
299
672
|
"major minor #blocks name",
|
|
300
673
|
"",
|
|
301
|
-
"
|
|
302
|
-
"
|
|
303
|
-
"
|
|
304
|
-
"
|
|
674
|
+
" 254 0 268435456 vda",
|
|
675
|
+
" 254 16 9664 vdb",
|
|
676
|
+
" 254 32 656 vdc",
|
|
677
|
+
" 254 48 5464 vdd",
|
|
678
|
+
].join("\n")}\n`);
|
|
679
|
+
// /proc/swaps — no swap (matches real env: SwapTotal 0)
|
|
680
|
+
write(vfs, "/proc/swaps", "Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority\n");
|
|
681
|
+
// /proc/diskstats — virtio block device I/O counters
|
|
682
|
+
write(vfs, "/proc/diskstats", `${[
|
|
683
|
+
" 254 0 vda 1000 0 8000 500 200 0 1600 100 0 600 600 0 0 0 0",
|
|
684
|
+
" 254 16 vdb 100 0 800 50 0 0 0 0 0 50 50 0 0 0 0",
|
|
685
|
+
" 254 32 vdc 50 0 400 25 0 0 0 0 0 25 25 0 0 0 0",
|
|
686
|
+
" 254 48 vdd 80 0 640 40 0 0 0 0 0 40 40 0 0 0 0",
|
|
305
687
|
].join("\n")}\n`);
|
|
306
|
-
// /proc/
|
|
307
|
-
write(vfs, "/proc/
|
|
308
|
-
|
|
309
|
-
// /proc/sys — sysctl virtual tree
|
|
688
|
+
// /proc/interrupts
|
|
689
|
+
write(vfs, "/proc/interrupts", ` CPU0\n 0: ${Math.floor(uptimeSec * 250)} IO-APIC 2-edge timer\n 1: 9 IO-APIC 1-edge i8042\n NMI: 0 Non-maskable interrupts\n ERR: 0\n MIS: 0\n PIN: 0 Posted-interrupt notification event\n NPI: 0 Nested posted-interrupt event\n PIW: 0 Posted-interrupt wakeup event\n`);
|
|
690
|
+
// /proc/sys — sysctl virtual tree (real values)
|
|
310
691
|
ensureDir(vfs, "/proc/sys");
|
|
311
692
|
ensureDir(vfs, "/proc/sys/kernel");
|
|
312
693
|
ensureDir(vfs, "/proc/sys/net");
|
|
694
|
+
ensureDir(vfs, "/proc/sys/net/ipv4");
|
|
695
|
+
ensureDir(vfs, "/proc/sys/net/ipv6");
|
|
696
|
+
ensureDir(vfs, "/proc/sys/net/core");
|
|
313
697
|
ensureDir(vfs, "/proc/sys/vm");
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
698
|
+
ensureDir(vfs, "/proc/sys/fs");
|
|
699
|
+
ensureDir(vfs, "/proc/sys/fs/inotify");
|
|
700
|
+
write(vfs, "/proc/sys/kernel/hostname", `${hostname}\n`);
|
|
701
|
+
write(vfs, "/proc/sys/kernel/ostype", "Linux\n");
|
|
702
|
+
write(vfs, "/proc/sys/kernel/osrelease", `${props.kernel}\n`);
|
|
703
|
+
write(vfs, "/proc/sys/kernel/pid_max", "32768\n");
|
|
704
|
+
write(vfs, "/proc/sys/kernel/threads-max", "31968\n");
|
|
705
|
+
write(vfs, "/proc/sys/kernel/randomize_va_space", "2\n");
|
|
706
|
+
write(vfs, "/proc/sys/kernel/dmesg_restrict", "0\n");
|
|
707
|
+
write(vfs, "/proc/sys/kernel/kptr_restrict", "0\n");
|
|
708
|
+
write(vfs, "/proc/sys/kernel/perf_event_paranoid", "2\n");
|
|
709
|
+
write(vfs, "/proc/sys/kernel/printk", "4\t4\t1\t7\n");
|
|
710
|
+
write(vfs, "/proc/sys/kernel/sysrq", "176\n");
|
|
711
|
+
write(vfs, "/proc/sys/kernel/panic", "1\n");
|
|
712
|
+
write(vfs, "/proc/sys/kernel/panic_on_oops", "1\n");
|
|
713
|
+
write(vfs, "/proc/sys/kernel/core_pattern", "core\n");
|
|
714
|
+
write(vfs, "/proc/sys/kernel/core_uses_pid", "0\n");
|
|
715
|
+
write(vfs, "/proc/sys/kernel/ngroups_max", "65536\n");
|
|
716
|
+
write(vfs, "/proc/sys/kernel/cap_last_cap", "40\n");
|
|
717
|
+
write(vfs, "/proc/sys/kernel/unprivileged_userns_clone", "1\n");
|
|
718
|
+
write(vfs, "/proc/sys/net/ipv4/ip_forward", "0\n");
|
|
719
|
+
write(vfs, "/proc/sys/net/ipv4/tcp_syncookies", "1\n");
|
|
720
|
+
write(vfs, "/proc/sys/net/ipv4/tcp_fin_timeout", "60\n");
|
|
721
|
+
write(vfs, "/proc/sys/net/ipv4/tcp_keepalive_time", "7200\n");
|
|
722
|
+
write(vfs, "/proc/sys/net/ipv4/conf/all/rp_filter", "2\n");
|
|
723
|
+
write(vfs, "/proc/sys/net/ipv6/conf/all/disable_ipv6", "1\n");
|
|
724
|
+
write(vfs, "/proc/sys/net/core/somaxconn", "4096\n");
|
|
725
|
+
write(vfs, "/proc/sys/net/core/rmem_max", "212992\n");
|
|
726
|
+
write(vfs, "/proc/sys/net/core/wmem_max", "212992\n");
|
|
727
|
+
write(vfs, "/proc/sys/vm/swappiness", "60\n");
|
|
728
|
+
write(vfs, "/proc/sys/vm/overcommit_memory", "0\n");
|
|
729
|
+
write(vfs, "/proc/sys/vm/overcommit_ratio", "50\n");
|
|
730
|
+
write(vfs, "/proc/sys/vm/dirty_ratio", "20\n");
|
|
731
|
+
write(vfs, "/proc/sys/vm/dirty_background_ratio", "10\n");
|
|
732
|
+
write(vfs, "/proc/sys/vm/min_free_kbytes", "65536\n");
|
|
733
|
+
write(vfs, "/proc/sys/vm/vfs_cache_pressure", "100\n");
|
|
734
|
+
write(vfs, "/proc/sys/fs/file-max", "1048576\n");
|
|
735
|
+
write(vfs, "/proc/sys/fs/inotify/max_user_watches", "524288\n");
|
|
736
|
+
write(vfs, "/proc/sys/fs/inotify/max_user_instances", "512\n");
|
|
737
|
+
write(vfs, "/proc/sys/fs/inotify/max_queued_events", "16384\n");
|
|
738
|
+
// /proc/cgroups — v1 hierarchy
|
|
739
|
+
write(vfs, "/proc/cgroups", `${[
|
|
740
|
+
"#subsys_name\thierarchy\tnum_cgroups\tenabled",
|
|
741
|
+
"cpuset\t5\t1\t1",
|
|
742
|
+
"cpu\t1\t1\t1",
|
|
743
|
+
"cpuacct\t2\t1\t1",
|
|
744
|
+
"blkio\t6\t1\t1",
|
|
745
|
+
"memory\t3\t1\t1",
|
|
746
|
+
"devices\t4\t1\t1",
|
|
747
|
+
"freezer\t7\t1\t1",
|
|
748
|
+
"pids\t8\t1\t1",
|
|
749
|
+
].join("\n")}\n`);
|
|
324
750
|
// init process (PID 1)
|
|
325
751
|
writeProcPid(vfs, 1, "root", "pts/0", "/sbin/init", new Date(shellStartTime).toISOString(), {});
|
|
326
752
|
// per-session processes
|
|
@@ -331,19 +757,22 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [])
|
|
|
331
757
|
HOME: `/home/${session.username}`,
|
|
332
758
|
TERM: "xterm-256color",
|
|
333
759
|
SHELL: "/bin/bash",
|
|
760
|
+
LANG: "en_US.UTF-8",
|
|
761
|
+
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
762
|
+
LOGNAME: session.username,
|
|
334
763
|
});
|
|
335
764
|
}
|
|
336
|
-
// /proc/self — mirror of most recent session
|
|
765
|
+
// /proc/self — mirror of most recent session
|
|
337
766
|
const selfPid = sessions.length > 0 ? ttyToPid(sessions[sessions.length - 1].tty) : 1;
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
vfs.remove("/proc/self");
|
|
341
|
-
}
|
|
342
|
-
catch { /* ignore */ }
|
|
767
|
+
try {
|
|
768
|
+
vfs.remove("/proc/self");
|
|
343
769
|
}
|
|
770
|
+
catch { /* ignore */ }
|
|
344
771
|
const selfSrc = `/proc/${selfPid}`;
|
|
345
772
|
ensureDir(vfs, "/proc/self");
|
|
346
773
|
ensureDir(vfs, "/proc/self/fd");
|
|
774
|
+
ensureDir(vfs, "/proc/self/fdinfo");
|
|
775
|
+
ensureDir(vfs, "/proc/self/net");
|
|
347
776
|
if (vfs.exists(selfSrc)) {
|
|
348
777
|
for (const entry of vfs.list(selfSrc)) {
|
|
349
778
|
const srcPath = `${selfSrc}/${entry}`;
|
|
@@ -353,11 +782,10 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [])
|
|
|
353
782
|
if (st.type === "file")
|
|
354
783
|
write(vfs, dstPath, vfs.readFile(srcPath));
|
|
355
784
|
}
|
|
356
|
-
catch { /* skip
|
|
785
|
+
catch { /* skip */ }
|
|
357
786
|
}
|
|
358
787
|
}
|
|
359
788
|
else {
|
|
360
|
-
// Minimal fallback
|
|
361
789
|
write(vfs, "/proc/self/cmdline", "bash\0");
|
|
362
790
|
write(vfs, "/proc/self/comm", "bash");
|
|
363
791
|
write(vfs, "/proc/self/status", "Name:\tbash\nState:\tS (sleeping)\nPid:\t1\nPPid:\t0\n");
|
|
@@ -369,11 +797,70 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [])
|
|
|
369
797
|
// ─── /sys ─────────────────────────────────────────────────────────────────────
|
|
370
798
|
function bootstrapSys(vfs, hostname, props) {
|
|
371
799
|
ensureDir(vfs, "/sys");
|
|
800
|
+
// No real DMI in Firecracker — /sys/devices/virtual/dmi/id does not exist.
|
|
801
|
+
// Expose /sys/class/net, /sys/fs/cgroup, /sys/kernel only.
|
|
372
802
|
ensureDir(vfs, "/sys/devices");
|
|
373
803
|
ensureDir(vfs, "/sys/devices/virtual");
|
|
804
|
+
ensureDir(vfs, "/sys/devices/system");
|
|
805
|
+
ensureDir(vfs, "/sys/devices/system/cpu");
|
|
806
|
+
ensureDir(vfs, "/sys/devices/system/cpu/cpu0");
|
|
807
|
+
ensureFile(vfs, "/sys/devices/system/cpu/cpu0/online", "1\n");
|
|
808
|
+
ensureFile(vfs, "/sys/devices/system/cpu/online", "0\n");
|
|
809
|
+
ensureFile(vfs, "/sys/devices/system/cpu/possible", "0\n");
|
|
810
|
+
ensureFile(vfs, "/sys/devices/system/cpu/present", "0\n");
|
|
811
|
+
ensureDir(vfs, "/sys/devices/system/node");
|
|
812
|
+
ensureDir(vfs, "/sys/devices/system/node/node0");
|
|
813
|
+
ensureFile(vfs, "/sys/devices/system/node/node0/cpumap", "1\n");
|
|
814
|
+
ensureDir(vfs, "/sys/class");
|
|
815
|
+
ensureDir(vfs, "/sys/class/net");
|
|
816
|
+
ensureDir(vfs, "/sys/class/net/eth0");
|
|
817
|
+
ensureFile(vfs, "/sys/class/net/eth0/operstate", "up\n");
|
|
818
|
+
ensureFile(vfs, "/sys/class/net/eth0/carrier", "1\n");
|
|
819
|
+
ensureFile(vfs, "/sys/class/net/eth0/mtu", "1500\n");
|
|
820
|
+
ensureFile(vfs, "/sys/class/net/eth0/speed", "10000\n");
|
|
821
|
+
ensureFile(vfs, "/sys/class/net/eth0/duplex", "full\n");
|
|
822
|
+
ensureFile(vfs, "/sys/class/net/eth0/address", "aa:bb:cc:dd:ee:ff\n");
|
|
823
|
+
ensureFile(vfs, "/sys/class/net/eth0/tx_queue_len", "1000\n");
|
|
824
|
+
const seed = fnv1a(hostname);
|
|
825
|
+
const macSeed = seed.toString(16).padStart(8, "0");
|
|
826
|
+
ensureFile(vfs, "/sys/class/net/eth0/address", `52:54:00:${macSeed.slice(0, 2)}:${macSeed.slice(2, 4)}:${macSeed.slice(4, 6)}\n`);
|
|
827
|
+
ensureDir(vfs, "/sys/class/net/lo");
|
|
828
|
+
ensureFile(vfs, "/sys/class/net/lo/operstate", "unknown\n");
|
|
829
|
+
ensureFile(vfs, "/sys/class/net/lo/carrier", "1\n");
|
|
830
|
+
ensureFile(vfs, "/sys/class/net/lo/mtu", "65536\n");
|
|
831
|
+
ensureFile(vfs, "/sys/class/net/lo/address", "00:00:00:00:00:00\n");
|
|
832
|
+
ensureDir(vfs, "/sys/class/block");
|
|
833
|
+
ensureDir(vfs, "/sys/class/block/vda");
|
|
834
|
+
ensureFile(vfs, "/sys/class/block/vda/size", "536870912\n");
|
|
835
|
+
ensureFile(vfs, "/sys/class/block/vda/ro", "0\n");
|
|
836
|
+
ensureFile(vfs, "/sys/class/block/vda/removable", "0\n");
|
|
837
|
+
// cgroup fs
|
|
838
|
+
ensureDir(vfs, "/sys/fs");
|
|
839
|
+
ensureDir(vfs, "/sys/fs/cgroup");
|
|
840
|
+
for (const subsys of ["cpu", "cpuacct", "memory", "devices", "blkio", "pids", "freezer", "unified"]) {
|
|
841
|
+
ensureDir(vfs, `/sys/fs/cgroup/${subsys}`);
|
|
842
|
+
if (subsys !== "unified") {
|
|
843
|
+
ensureFile(vfs, `/sys/fs/cgroup/${subsys}/tasks`, "1\n");
|
|
844
|
+
ensureFile(vfs, `/sys/fs/cgroup/${subsys}/notify_on_release`, "0\n");
|
|
845
|
+
ensureFile(vfs, `/sys/fs/cgroup/${subsys}/release_agent`, "");
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
ensureFile(vfs, "/sys/fs/cgroup/memory/memory.limit_in_bytes", `${os.totalmem()}\n`);
|
|
849
|
+
ensureFile(vfs, "/sys/fs/cgroup/memory/memory.usage_in_bytes", `${os.totalmem() - os.freemem()}\n`);
|
|
850
|
+
ensureFile(vfs, "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes", `${os.totalmem()}\n`);
|
|
851
|
+
ensureFile(vfs, "/sys/fs/cgroup/cpu/cpu.cfs_period_us", "100000\n");
|
|
852
|
+
ensureFile(vfs, "/sys/fs/cgroup/cpu/cpu.cfs_quota_us", "-1\n");
|
|
853
|
+
ensureFile(vfs, "/sys/fs/cgroup/cpu/cpu.shares", "1024\n");
|
|
854
|
+
ensureDir(vfs, "/sys/kernel");
|
|
855
|
+
ensureFile(vfs, "/sys/kernel/hostname", `${hostname}\n`);
|
|
856
|
+
ensureFile(vfs, "/sys/kernel/osrelease", `${props.kernel}\n`);
|
|
857
|
+
ensureFile(vfs, "/sys/kernel/ostype", "Linux\n");
|
|
858
|
+
// security
|
|
859
|
+
ensureDir(vfs, "/sys/kernel/security");
|
|
860
|
+
// Still; we will create virtual dmi
|
|
861
|
+
ensureDir(vfs, "/sys/devices/virtual");
|
|
374
862
|
ensureDir(vfs, "/sys/devices/virtual/dmi");
|
|
375
863
|
ensureDir(vfs, "/sys/devices/virtual/dmi/id");
|
|
376
|
-
const seed = fnv1a(hostname);
|
|
377
864
|
const product = `VirtualNode-${(seed % 10000).toString().padStart(4, "0")}`;
|
|
378
865
|
// Full DMI table — deterministic, seeded from hostname
|
|
379
866
|
const dmi = {
|
|
@@ -405,34 +892,62 @@ function bootstrapSys(vfs, hostname, props) {
|
|
|
405
892
|
// ─── /dev ─────────────────────────────────────────────────────────────────────
|
|
406
893
|
function bootstrapDev(vfs) {
|
|
407
894
|
ensureDir(vfs, "/dev");
|
|
408
|
-
// character devices
|
|
895
|
+
// character devices — matching real Firecracker container
|
|
409
896
|
ensureFile(vfs, "/dev/null", "", 0o666);
|
|
410
897
|
ensureFile(vfs, "/dev/zero", "", 0o666);
|
|
411
898
|
ensureFile(vfs, "/dev/full", "", 0o666);
|
|
412
899
|
ensureFile(vfs, "/dev/random", "", 0o444);
|
|
413
900
|
ensureFile(vfs, "/dev/urandom", "", 0o444);
|
|
414
901
|
ensureFile(vfs, "/dev/mem", "", 0o640);
|
|
902
|
+
ensureFile(vfs, "/dev/port", "", 0o640);
|
|
903
|
+
ensureFile(vfs, "/dev/kmsg", "", 0o660);
|
|
904
|
+
ensureFile(vfs, "/dev/hwrng", "", 0o660);
|
|
905
|
+
ensureFile(vfs, "/dev/fuse", "", 0o660);
|
|
906
|
+
ensureFile(vfs, "/dev/autofs", "", 0o660);
|
|
907
|
+
ensureFile(vfs, "/dev/userfaultfd", "", 0o660);
|
|
908
|
+
ensureFile(vfs, "/dev/cpu_dma_latency", "", 0o660);
|
|
909
|
+
ensureFile(vfs, "/dev/ptp0", "", 0o660);
|
|
910
|
+
// snapshot (KVM-specific)
|
|
911
|
+
ensureFile(vfs, "/dev/snapshot", "", 0o660);
|
|
415
912
|
// terminal devices
|
|
416
913
|
ensureFile(vfs, "/dev/console", "", 0o600);
|
|
417
914
|
ensureFile(vfs, "/dev/tty", "", 0o666);
|
|
418
|
-
ensureFile(vfs, "/dev/tty0", "", 0o620);
|
|
419
|
-
ensureFile(vfs, "/dev/tty1", "", 0o620);
|
|
420
915
|
ensureFile(vfs, "/dev/ttyS0", "", 0o660);
|
|
421
|
-
|
|
916
|
+
ensureFile(vfs, "/dev/ptmx", "", 0o666);
|
|
917
|
+
// tty0–63 (like real env)
|
|
918
|
+
for (let i = 0; i <= 63; i++) {
|
|
919
|
+
ensureFile(vfs, `/dev/tty${i}`, "", 0o620);
|
|
920
|
+
}
|
|
921
|
+
// vcs devices
|
|
922
|
+
ensureFile(vfs, "/dev/vcs", "", 0o620);
|
|
923
|
+
ensureFile(vfs, "/dev/vcs1", "", 0o620);
|
|
924
|
+
ensureFile(vfs, "/dev/vcsa", "", 0o620);
|
|
925
|
+
ensureFile(vfs, "/dev/vcsa1", "", 0o620);
|
|
926
|
+
ensureFile(vfs, "/dev/vcsu", "", 0o620);
|
|
927
|
+
ensureFile(vfs, "/dev/vcsu1", "", 0o620);
|
|
928
|
+
// loop devices (0–7)
|
|
422
929
|
for (let i = 0; i < 8; i++) {
|
|
423
930
|
ensureFile(vfs, `/dev/loop${i}`, "", 0o660);
|
|
424
931
|
}
|
|
425
932
|
ensureDir(vfs, "/dev/loop-control");
|
|
426
|
-
// block
|
|
427
|
-
ensureFile(vfs, "/dev/
|
|
428
|
-
ensureFile(vfs, "/dev/
|
|
429
|
-
ensureFile(vfs, "/dev/
|
|
933
|
+
// virtio block devices (vda–vdd matching mounts)
|
|
934
|
+
ensureFile(vfs, "/dev/vda", "", 0o660);
|
|
935
|
+
ensureFile(vfs, "/dev/vdb", "", 0o660);
|
|
936
|
+
ensureFile(vfs, "/dev/vdc", "", 0o660);
|
|
937
|
+
ensureFile(vfs, "/dev/vdd", "", 0o660);
|
|
938
|
+
// network tun
|
|
939
|
+
ensureDir(vfs, "/dev/net");
|
|
940
|
+
ensureFile(vfs, "/dev/net/tun", "", 0o660);
|
|
430
941
|
// misc
|
|
431
942
|
ensureDir(vfs, "/dev/pts");
|
|
432
943
|
ensureDir(vfs, "/dev/shm");
|
|
944
|
+
ensureDir(vfs, "/dev/cpu");
|
|
433
945
|
ensureFile(vfs, "/dev/stdin", "", 0o666);
|
|
434
946
|
ensureFile(vfs, "/dev/stdout", "", 0o666);
|
|
435
947
|
ensureFile(vfs, "/dev/stderr", "", 0o666);
|
|
948
|
+
ensureDir(vfs, "/dev/fd");
|
|
949
|
+
ensureFile(vfs, "/dev/vga_arbiter", "", 0o660);
|
|
950
|
+
ensureFile(vfs, "/dev/vsock", "", 0o660);
|
|
436
951
|
}
|
|
437
952
|
// ─── /usr ─────────────────────────────────────────────────────────────────────
|
|
438
953
|
function bootstrapUsr(vfs) {
|
|
@@ -443,12 +958,30 @@ function bootstrapUsr(vfs) {
|
|
|
443
958
|
ensureDir(vfs, "/usr/local/bin");
|
|
444
959
|
ensureDir(vfs, "/usr/local/lib");
|
|
445
960
|
ensureDir(vfs, "/usr/local/share");
|
|
961
|
+
ensureDir(vfs, "/usr/local/include");
|
|
962
|
+
ensureDir(vfs, "/usr/local/sbin");
|
|
446
963
|
ensureDir(vfs, "/usr/share");
|
|
447
964
|
ensureDir(vfs, "/usr/share/doc");
|
|
448
965
|
ensureDir(vfs, "/usr/share/man");
|
|
449
966
|
ensureDir(vfs, "/usr/share/man/man1");
|
|
967
|
+
ensureDir(vfs, "/usr/share/man/man5");
|
|
968
|
+
ensureDir(vfs, "/usr/share/man/man8");
|
|
969
|
+
ensureDir(vfs, "/usr/share/common-licenses");
|
|
970
|
+
ensureDir(vfs, "/usr/share/ca-certificates");
|
|
971
|
+
ensureDir(vfs, "/usr/share/zoneinfo");
|
|
450
972
|
ensureDir(vfs, "/usr/lib");
|
|
451
|
-
|
|
973
|
+
ensureDir(vfs, "/usr/lib/x86_64-linux-gnu");
|
|
974
|
+
ensureDir(vfs, "/usr/lib/python3");
|
|
975
|
+
ensureDir(vfs, "/usr/lib/python3/dist-packages");
|
|
976
|
+
ensureDir(vfs, "/usr/lib/python3.12");
|
|
977
|
+
ensureDir(vfs, "/usr/lib/jvm");
|
|
978
|
+
ensureDir(vfs, "/usr/lib/jvm/java-21-openjdk-amd64");
|
|
979
|
+
ensureDir(vfs, "/usr/lib/jvm/java-21-openjdk-amd64/bin");
|
|
980
|
+
ensureDir(vfs, "/usr/lib/node_modules");
|
|
981
|
+
ensureDir(vfs, "/usr/lib/node_modules/npm");
|
|
982
|
+
ensureDir(vfs, "/usr/include");
|
|
983
|
+
ensureDir(vfs, "/usr/src");
|
|
984
|
+
// builtins — all bins present in the real container
|
|
452
985
|
const builtins = [
|
|
453
986
|
"sh", "bash", "ls", "cat", "echo", "grep", "find", "sort",
|
|
454
987
|
"head", "tail", "cut", "tr", "sed", "awk", "wc", "tee",
|
|
@@ -457,50 +990,433 @@ function bootstrapUsr(vfs) {
|
|
|
457
990
|
"hostname", "uname", "ps", "kill", "df", "du", "curl", "wget",
|
|
458
991
|
"nano", "diff", "uniq", "xargs", "base64",
|
|
459
992
|
];
|
|
993
|
+
// From a real container
|
|
994
|
+
// const builtins = [
|
|
995
|
+
// // core
|
|
996
|
+
// "sh", "bash", "dash",
|
|
997
|
+
// "ls", "cat", "echo", "grep", "find", "sort",
|
|
998
|
+
// "head", "tail", "cut", "tr", "sed", "awk", "mawk", "gawk",
|
|
999
|
+
// "wc", "tee", "tar", "gzip", "gunzip", "bzip2", "xz",
|
|
1000
|
+
// "touch", "mkdir", "rm", "mv", "cp", "ln", "pwd",
|
|
1001
|
+
// "chmod", "chown", "chgrp", "env", "date", "sleep",
|
|
1002
|
+
// "id", "whoami", "hostname", "uname", "ps", "kill",
|
|
1003
|
+
// "df", "du", "dd", "stat", "file",
|
|
1004
|
+
// // net
|
|
1005
|
+
// "curl", "wget", "nc", "netcat", "ss", "ip",
|
|
1006
|
+
// // editors
|
|
1007
|
+
// "nano", "vi",
|
|
1008
|
+
// // text
|
|
1009
|
+
// "diff", "uniq", "xargs", "base64", "md5sum", "sha256sum",
|
|
1010
|
+
// "strings", "hexdump", "od", "column", "fmt", "paste",
|
|
1011
|
+
// "join", "comm", "split", "csplit", "fold", "expand",
|
|
1012
|
+
// // archive
|
|
1013
|
+
// "zip", "unzip",
|
|
1014
|
+
// // process
|
|
1015
|
+
// "top", "htop", "free", "uptime", "dmesg", "lsof",
|
|
1016
|
+
// "strace", "ltrace", "pgrep", "pkill", "nohup", "nice",
|
|
1017
|
+
// // fs
|
|
1018
|
+
// "mount", "umount", "lsblk", "fdisk", "blkid", "e2fsck",
|
|
1019
|
+
// // misc
|
|
1020
|
+
// "bc", "expr", "seq", "yes", "true", "false", "test",
|
|
1021
|
+
// "readlink", "realpath", "dirname", "basename", "mktemp",
|
|
1022
|
+
// "install", "make",
|
|
1023
|
+
// // dev tools
|
|
1024
|
+
// "gcc", "gcc-13", "g++", "g++-13", "cpp", "as", "ld",
|
|
1025
|
+
// "ar", "nm", "objdump", "objcopy", "strip", "size",
|
|
1026
|
+
// "cc", "c++", "pkg-config",
|
|
1027
|
+
// // package
|
|
1028
|
+
// "apt", "apt-get", "apt-cache", "dpkg", "dpkg-query",
|
|
1029
|
+
// "lsb_release", "add-apt-repository",
|
|
1030
|
+
// // scripting
|
|
1031
|
+
// "perl", "python3", "python3.12", "pipx",
|
|
1032
|
+
// // node/npm
|
|
1033
|
+
// "node", "npm", "npx",
|
|
1034
|
+
// // java
|
|
1035
|
+
// "java", "javac", "jar", "javadoc",
|
|
1036
|
+
// // security
|
|
1037
|
+
// "openssl", "gpg", "gpg2", "gpgv", "ssh", "ssh-keygen",
|
|
1038
|
+
// "sudo", "su", "passwd", "adduser", "useradd",
|
|
1039
|
+
// // misc system
|
|
1040
|
+
// "systemctl", "journalctl", "loginctl",
|
|
1041
|
+
// "timedatectl", "localectl",
|
|
1042
|
+
// "lshw", "lscpu", "lsusb", "lspci",
|
|
1043
|
+
// // text proc
|
|
1044
|
+
// "jq", "xmllint", "pandoc",
|
|
1045
|
+
// // multimedia
|
|
1046
|
+
// "ffmpeg",
|
|
1047
|
+
// ];
|
|
460
1048
|
for (const bin of builtins) {
|
|
461
1049
|
ensureFile(vfs, `/usr/bin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
|
|
462
1050
|
}
|
|
463
|
-
|
|
1051
|
+
// sbin equivalents
|
|
1052
|
+
const sbins = [
|
|
1053
|
+
"nologin", "useradd", "userdel", "groupadd", "groupdel",
|
|
1054
|
+
"adduser", "deluser", "shutdown", "reboot", "halt",
|
|
1055
|
+
"init", "service", "update-alternatives", "update-rc.d",
|
|
1056
|
+
"depmod", "modprobe", "insmod", "rmmod", "lsmod",
|
|
1057
|
+
"ifconfig", "route", "iptables", "ip6tables",
|
|
1058
|
+
"arp", "iwconfig", "ethtool",
|
|
1059
|
+
"fdisk", "parted", "mkfs.ext4", "fsck",
|
|
1060
|
+
"ldconfig", "ldconfig.real",
|
|
1061
|
+
];
|
|
1062
|
+
for (const bin of sbins) {
|
|
1063
|
+
ensureFile(vfs, `/usr/sbin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
|
|
1064
|
+
}
|
|
1065
|
+
// versioned python symlink stubs
|
|
1066
|
+
ensureFile(vfs, "/usr/bin/python3.12", `#!/bin/sh\nexec python3 "$@"\n`, 0o755);
|
|
1067
|
+
ensureFile(vfs, "/usr/bin/python3", `#!/bin/sh\nexec python3.12 "$@"\n`, 0o755);
|
|
1068
|
+
// node version stubs
|
|
1069
|
+
ensureFile(vfs, "/usr/bin/node", `#!/bin/sh\nexec node "$@"\n`, 0o755);
|
|
1070
|
+
ensureFile(vfs, "/usr/bin/npm", `#!/bin/sh\nexec npm "$@"\n`, 0o755);
|
|
1071
|
+
ensureFile(vfs, "/usr/bin/npx", `#!/bin/sh\nexec npx "$@"\n`, 0o755);
|
|
1072
|
+
// java stubs
|
|
1073
|
+
ensureFile(vfs, "/usr/lib/jvm/java-21-openjdk-amd64/bin/java", `#!/bin/sh\nexec java "$@"\n`, 0o755);
|
|
1074
|
+
ensureFile(vfs, "/usr/lib/jvm/java-21-openjdk-amd64/bin/javac", `#!/bin/sh\nexec javac "$@"\n`, 0o755);
|
|
1075
|
+
// /usr/share/common-licenses stubs
|
|
1076
|
+
ensureFile(vfs, "/usr/share/common-licenses/GPL-2", "GNU General Public License v2\n");
|
|
1077
|
+
ensureFile(vfs, "/usr/share/common-licenses/GPL-3", "GNU General Public License v3\n");
|
|
1078
|
+
ensureFile(vfs, "/usr/share/common-licenses/LGPL-2.1", "GNU Lesser General Public License v2.1\n");
|
|
1079
|
+
ensureFile(vfs, "/usr/share/common-licenses/Apache-2.0", "Apache License 2.0\n");
|
|
1080
|
+
ensureFile(vfs, "/usr/share/common-licenses/MIT", "MIT License\n");
|
|
464
1081
|
}
|
|
465
1082
|
// ─── /var ─────────────────────────────────────────────────────────────────────
|
|
1083
|
+
/** Realistic dpkg status database from real container package list */
|
|
1084
|
+
const DPKG_STATUS = `\
|
|
1085
|
+
Package: bash
|
|
1086
|
+
Status: install ok installed
|
|
1087
|
+
Priority: required
|
|
1088
|
+
Section: shells
|
|
1089
|
+
Installed-Size: 7012
|
|
1090
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1091
|
+
Architecture: amd64
|
|
1092
|
+
Version: 5.2.21-2nyx1
|
|
1093
|
+
Depends: base-files (>= 2.1.12), fortune-utils (>= 1.0)
|
|
1094
|
+
Description: GNU Bourne Again SHell
|
|
1095
|
+
bash is an sh-compatible command language interpreter that executes commands
|
|
1096
|
+
read from the standard input or from a file.
|
|
1097
|
+
|
|
1098
|
+
Package: coreutils
|
|
1099
|
+
Status: install ok installed
|
|
1100
|
+
Priority: required
|
|
1101
|
+
Section: utils
|
|
1102
|
+
Installed-Size: 18272
|
|
1103
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1104
|
+
Architecture: amd64
|
|
1105
|
+
Version: 9.4-3nyx1
|
|
1106
|
+
Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.44), libc6 (>= 2.17)
|
|
1107
|
+
Description: GNU core utilities
|
|
1108
|
+
This package contains the basic file, shell and text manipulation utilities.
|
|
1109
|
+
|
|
1110
|
+
Package: nodejs
|
|
1111
|
+
Status: install ok installed
|
|
1112
|
+
Priority: optional
|
|
1113
|
+
Section: web
|
|
1114
|
+
Installed-Size: 107120
|
|
1115
|
+
Maintainer: NodeSource <nodejs@nodesource.com>
|
|
1116
|
+
Architecture: amd64
|
|
1117
|
+
Version: 22.22.2-1nyx1
|
|
1118
|
+
Depends: libc6 (>= 2.17), libgcc-s1 (>= 3.0), libstdc++6 (>= 9.0)
|
|
1119
|
+
Description: Node.js event-based server-side javascript engine
|
|
1120
|
+
Node.js is similar in design to and influenced by systems like Ruby's Twisted.
|
|
1121
|
+
|
|
1122
|
+
Package: python3
|
|
1123
|
+
Status: install ok installed
|
|
1124
|
+
Priority: important
|
|
1125
|
+
Section: python
|
|
1126
|
+
Installed-Size: 68
|
|
1127
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1128
|
+
Architecture: amd64
|
|
1129
|
+
Version: 3.12.3-0nyx1
|
|
1130
|
+
Depends: python3.12 (>= 3.12.3-0nyx1)
|
|
1131
|
+
Description: interactive high-level object-oriented language (default python3)
|
|
1132
|
+
Python, the high-level, interactive object oriented language, includes an
|
|
1133
|
+
extensive class library with lots of goodies for network programming.
|
|
1134
|
+
|
|
1135
|
+
Package: python3.12
|
|
1136
|
+
Status: install ok installed
|
|
1137
|
+
Priority: optional
|
|
1138
|
+
Section: python
|
|
1139
|
+
Installed-Size: 36
|
|
1140
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1141
|
+
Architecture: amd64
|
|
1142
|
+
Version: 3.12.3-1nyx1
|
|
1143
|
+
Depends: python3.12-minimal (= 3.12.3-1nyx1), libpython3.12-stdlib
|
|
1144
|
+
Description: Interactive high-level object-oriented language (version 3.12)
|
|
1145
|
+
Python is a high-level, interactive, object-oriented language. Its 3.12 version
|
|
1146
|
+
includes an extensive class library.
|
|
1147
|
+
|
|
1148
|
+
Package: gcc-13
|
|
1149
|
+
Status: install ok installed
|
|
1150
|
+
Priority: optional
|
|
1151
|
+
Section: devel
|
|
1152
|
+
Installed-Size: 70460
|
|
1153
|
+
Maintainer: Fortune GCC Maintainers <gcc@fortune.local>
|
|
1154
|
+
Architecture: amd64
|
|
1155
|
+
Version: 13.3.0-6nyx1
|
|
1156
|
+
Depends: cpp-13 (= 13.3.0-6nyx1), gcc-13-base (= 13.3.0-6nyx1)
|
|
1157
|
+
Description: GNU C compiler
|
|
1158
|
+
This is the GNU C compiler, a fairly portable optimizing compiler for C.
|
|
1159
|
+
|
|
1160
|
+
Package: openjdk-21-jre-headless
|
|
1161
|
+
Status: install ok installed
|
|
1162
|
+
Priority: optional
|
|
1163
|
+
Section: java
|
|
1164
|
+
Installed-Size: 174488
|
|
1165
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1166
|
+
Architecture: amd64
|
|
1167
|
+
Version: 21.0.10+7-1~nyx
|
|
1168
|
+
Depends: libc6 (>= 2.17), libgcc-s1 (>= 3.4)
|
|
1169
|
+
Description: OpenJDK Java runtime, using Hotspot JIT (headless)
|
|
1170
|
+
Minimal Java runtime - needed for executing non-graphical Java programs.
|
|
1171
|
+
|
|
1172
|
+
Package: curl
|
|
1173
|
+
Status: install ok installed
|
|
1174
|
+
Priority: standard
|
|
1175
|
+
Section: web
|
|
1176
|
+
Installed-Size: 544
|
|
1177
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1178
|
+
Architecture: amd64
|
|
1179
|
+
Version: 8.5.0-2nyx1
|
|
1180
|
+
Depends: libcurl4 (= 8.5.0-2nyx1), zlib1g (>= 1:1.1.4)
|
|
1181
|
+
Description: command line tool for transferring data with URL syntax
|
|
1182
|
+
curl is a command line tool for transferring data with URL syntax, supporting
|
|
1183
|
+
DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3,
|
|
1184
|
+
POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, TELNET and TFTP.
|
|
1185
|
+
|
|
1186
|
+
Package: git
|
|
1187
|
+
Status: install ok installed
|
|
1188
|
+
Priority: optional
|
|
1189
|
+
Section: vcs
|
|
1190
|
+
Installed-Size: 36552
|
|
1191
|
+
Maintainer: Fortune VCS Team <vcs@fortune.local>
|
|
1192
|
+
Architecture: amd64
|
|
1193
|
+
Version: 1:2.43.0-1nyx1
|
|
1194
|
+
Depends: liberror-perl, git-man, libc6 (>= 2.34), libcurl3-gnutls
|
|
1195
|
+
Description: fast, scalable, distributed revision control system
|
|
1196
|
+
Git is popular version control system designed to handle very large projects
|
|
1197
|
+
with speed and efficiency; it is used mainly for various open source projects.
|
|
1198
|
+
|
|
1199
|
+
Package: openssl
|
|
1200
|
+
Status: install ok installed
|
|
1201
|
+
Priority: optional
|
|
1202
|
+
Section: utils
|
|
1203
|
+
Installed-Size: 1320
|
|
1204
|
+
Maintainer: Fortune Security Team <security@fortune.local>
|
|
1205
|
+
Architecture: amd64
|
|
1206
|
+
Version: 3.0.13-0nyx1
|
|
1207
|
+
Depends: libssl3 (>= 3.0.13)
|
|
1208
|
+
Description: Secure Sockets Layer toolkit - cryptographic utility
|
|
1209
|
+
This package is part of the OpenSSL project's implementation of the SSL and TLS
|
|
1210
|
+
cryptographic protocols and related technologies.
|
|
1211
|
+
|
|
1212
|
+
Package: wget
|
|
1213
|
+
Status: install ok installed
|
|
1214
|
+
Priority: standard
|
|
1215
|
+
Section: web
|
|
1216
|
+
Installed-Size: 1100
|
|
1217
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1218
|
+
Architecture: amd64
|
|
1219
|
+
Version: 1.21.4-1nyx1
|
|
1220
|
+
Depends: libc6 (>= 2.17), libgnutls30 (>= 3.7.9), libidn2-0 (>= 2.0.0)
|
|
1221
|
+
Description: retrieves files from the web
|
|
1222
|
+
GNU Wget is a program for retrieving files from the web, supporting the HTTP,
|
|
1223
|
+
HTTPS and FTP protocols.
|
|
1224
|
+
|
|
1225
|
+
Package: make
|
|
1226
|
+
Status: install ok installed
|
|
1227
|
+
Priority: optional
|
|
1228
|
+
Section: devel
|
|
1229
|
+
Installed-Size: 556
|
|
1230
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1231
|
+
Architecture: amd64
|
|
1232
|
+
Version: 4.3-4.1nyx1
|
|
1233
|
+
Depends: libc6 (>= 2.17)
|
|
1234
|
+
Description: utility for directing compilation
|
|
1235
|
+
GNU Make is a utility which controls the generation of executables and other
|
|
1236
|
+
target files of a program from the program's source files.
|
|
1237
|
+
|
|
1238
|
+
Package: ffmpeg
|
|
1239
|
+
Status: install ok installed
|
|
1240
|
+
Priority: optional
|
|
1241
|
+
Section: video
|
|
1242
|
+
Installed-Size: 2184
|
|
1243
|
+
Maintainer: Fortune Multimedia Team <multimedia@fortune.local>
|
|
1244
|
+
Architecture: amd64
|
|
1245
|
+
Version: 7:6.1.1-3nyx1
|
|
1246
|
+
Depends: libavcodec60, libavdevice60, libavfilter9, libavformat60, libavutil58
|
|
1247
|
+
Description: Tools for transcoding, streaming and playing of multimedia files
|
|
1248
|
+
FFmpeg is a complete, cross-platform solution to record, convert and stream
|
|
1249
|
+
audio and video.
|
|
1250
|
+
|
|
1251
|
+
Package: pandoc
|
|
1252
|
+
Status: install ok installed
|
|
1253
|
+
Priority: optional
|
|
1254
|
+
Section: text
|
|
1255
|
+
Installed-Size: 96248
|
|
1256
|
+
Maintainer: Fortune Haskell Group <haskell@fortune.local>
|
|
1257
|
+
Architecture: amd64
|
|
1258
|
+
Version: 3.1.3+ds-2
|
|
1259
|
+
Depends: libgmp10, libgcc-s1, libffi8
|
|
1260
|
+
Description: general markup converter
|
|
1261
|
+
Pandoc is a Haskell library for converting from one markup format to another.
|
|
1262
|
+
|
|
1263
|
+
Package: tesseract-ocr
|
|
1264
|
+
Status: install ok installed
|
|
1265
|
+
Priority: optional
|
|
1266
|
+
Section: graphics
|
|
1267
|
+
Installed-Size: 1736
|
|
1268
|
+
Maintainer: Fortune OCR Team <ocr@fortune.local>
|
|
1269
|
+
Architecture: amd64
|
|
1270
|
+
Version: 5.3.4-1build5
|
|
1271
|
+
Depends: libc6 (>= 2.14), libleptonica-dev
|
|
1272
|
+
Description: Tesseract Open Source OCR Engine
|
|
1273
|
+
Tesseract is an Open Source OCR Engine, originally developed by HP and now
|
|
1274
|
+
sponsored by Google.
|
|
1275
|
+
|
|
1276
|
+
Package: dpkg
|
|
1277
|
+
Status: install ok installed
|
|
1278
|
+
Priority: required
|
|
1279
|
+
Section: admin
|
|
1280
|
+
Installed-Size: 6800
|
|
1281
|
+
Maintainer: Fortune Package Team <dpkg@fortune.local>
|
|
1282
|
+
Architecture: amd64
|
|
1283
|
+
Version: 1.22.6nyx1
|
|
1284
|
+
Depends: libc6 (>= 2.17), libzstd1 (>= 1.5.5)
|
|
1285
|
+
Description: Fortune package management system
|
|
1286
|
+
This package provides the low-level infrastructure for handling the
|
|
1287
|
+
installation and removal of Fortune software packages.
|
|
1288
|
+
|
|
1289
|
+
Package: apt
|
|
1290
|
+
Status: install ok installed
|
|
1291
|
+
Priority: important
|
|
1292
|
+
Section: admin
|
|
1293
|
+
Installed-Size: 4236
|
|
1294
|
+
Maintainer: Fortune Package Team <apt@fortune.local>
|
|
1295
|
+
Architecture: amd64
|
|
1296
|
+
Version: 2.8.3nyx1
|
|
1297
|
+
Depends: libapt-pkg6.0 (>= 2.8.3), adduser, gpgv
|
|
1298
|
+
Description: commandline package manager
|
|
1299
|
+
This package provides commandline tools for searching and managing as well as
|
|
1300
|
+
querying information about packages as a low-level access to all features of
|
|
1301
|
+
the libapt-pkg library.
|
|
1302
|
+
|
|
1303
|
+
Package: systemd
|
|
1304
|
+
Status: install ok installed
|
|
1305
|
+
Priority: optional
|
|
1306
|
+
Section: admin
|
|
1307
|
+
Installed-Size: 36476
|
|
1308
|
+
Maintainer: Fortune System Team <systemd@fortune.local>
|
|
1309
|
+
Architecture: amd64
|
|
1310
|
+
Version: 255.4-1nyx1
|
|
1311
|
+
Depends: libacl1 (>= 2.2.23), libblkid1 (>= 2.24), libc6 (>= 2.39)
|
|
1312
|
+
Description: system and service manager
|
|
1313
|
+
systemd is a system and service manager for Linux. It provides aggressive
|
|
1314
|
+
parallelization capabilities, uses socket and D-Bus activation for starting
|
|
1315
|
+
services, offers on-demand starting of daemons, keeps track of processes using
|
|
1316
|
+
Linux cgroups, maintains mount and automount points, and implements an
|
|
1317
|
+
elaborate transactional dependency-based service control logic.
|
|
1318
|
+
|
|
1319
|
+
Package: nano
|
|
1320
|
+
Status: install ok installed
|
|
1321
|
+
Priority: important
|
|
1322
|
+
Section: editors
|
|
1323
|
+
Installed-Size: 888
|
|
1324
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1325
|
+
Architecture: amd64
|
|
1326
|
+
Version: 7.2-2
|
|
1327
|
+
Depends: libc6 (>= 2.17), libncursesw6 (>= 6)
|
|
1328
|
+
Description: small, friendly text editor inspired by Pico
|
|
1329
|
+
GNU nano is an easy-to-use text editor originally designed as a replacement
|
|
1330
|
+
for Pico, the ncurses-based editor from the non-free mailer package Pine.
|
|
1331
|
+
|
|
1332
|
+
Package: less
|
|
1333
|
+
Status: install ok installed
|
|
1334
|
+
Priority: important
|
|
1335
|
+
Section: text
|
|
1336
|
+
Installed-Size: 344
|
|
1337
|
+
Maintainer: Fortune Maintainers <maintainers@fortune.local>
|
|
1338
|
+
Architecture: amd64
|
|
1339
|
+
Version: 1:640-2build2
|
|
1340
|
+
Depends: libc6 (>= 2.17), libtinfo6 (>= 6)
|
|
1341
|
+
Description: pager program similar to more
|
|
1342
|
+
This package provides the \`less\` command, which is similar to more but allows
|
|
1343
|
+
you to move backwards through the file.
|
|
1344
|
+
|
|
1345
|
+
`;
|
|
466
1346
|
function bootstrapVar(vfs) {
|
|
467
1347
|
ensureDir(vfs, "/var");
|
|
468
1348
|
ensureDir(vfs, "/var/log");
|
|
469
1349
|
ensureDir(vfs, "/var/log/apt");
|
|
1350
|
+
ensureDir(vfs, "/var/log/journal");
|
|
1351
|
+
ensureDir(vfs, "/var/log/private");
|
|
470
1352
|
ensureDir(vfs, "/var/tmp");
|
|
471
1353
|
ensureDir(vfs, "/var/cache");
|
|
472
1354
|
ensureDir(vfs, "/var/cache/apt");
|
|
473
1355
|
ensureDir(vfs, "/var/cache/apt/archives");
|
|
1356
|
+
ensureDir(vfs, "/var/cache/apt/archives/partial");
|
|
1357
|
+
ensureDir(vfs, "/var/cache/debconf");
|
|
1358
|
+
ensureDir(vfs, "/var/cache/ldconfig");
|
|
1359
|
+
ensureDir(vfs, "/var/cache/fontconfig");
|
|
1360
|
+
ensureDir(vfs, "/var/cache/PackageKit");
|
|
474
1361
|
ensureDir(vfs, "/var/lib");
|
|
475
1362
|
ensureDir(vfs, "/var/lib/apt");
|
|
476
1363
|
ensureDir(vfs, "/var/lib/apt/lists");
|
|
1364
|
+
ensureDir(vfs, "/var/lib/apt/lists/partial");
|
|
477
1365
|
ensureDir(vfs, "/var/lib/dpkg");
|
|
478
1366
|
ensureDir(vfs, "/var/lib/dpkg/info");
|
|
1367
|
+
ensureDir(vfs, "/var/lib/dpkg/updates");
|
|
1368
|
+
ensureDir(vfs, "/var/lib/dpkg/alternatives");
|
|
479
1369
|
ensureDir(vfs, "/var/lib/misc");
|
|
1370
|
+
ensureDir(vfs, "/var/lib/systemd");
|
|
1371
|
+
ensureDir(vfs, "/var/lib/systemd/coredump");
|
|
1372
|
+
ensureDir(vfs, "/var/lib/pam");
|
|
1373
|
+
ensureDir(vfs, "/var/lib/git");
|
|
1374
|
+
ensureDir(vfs, "/var/lib/PackageKit");
|
|
1375
|
+
ensureDir(vfs, "/var/lib/python");
|
|
480
1376
|
ensureDir(vfs, "/var/spool");
|
|
481
1377
|
ensureDir(vfs, "/var/spool/cron");
|
|
1378
|
+
ensureDir(vfs, "/var/spool/mail");
|
|
482
1379
|
ensureDir(vfs, "/var/mail");
|
|
483
|
-
|
|
484
|
-
|
|
1380
|
+
ensureDir(vfs, "/var/backups");
|
|
1381
|
+
ensureDir(vfs, "/var/www");
|
|
1382
|
+
// dpkg status — realistic package database
|
|
1383
|
+
ensureFile(vfs, "/var/lib/dpkg/status", DPKG_STATUS);
|
|
485
1384
|
ensureFile(vfs, "/var/lib/dpkg/available", "");
|
|
1385
|
+
ensureFile(vfs, "/var/lib/dpkg/lock", "");
|
|
1386
|
+
ensureFile(vfs, "/var/lib/dpkg/lock-frontend", "");
|
|
1387
|
+
// apt state
|
|
1388
|
+
ensureFile(vfs, "/var/lib/apt/lists/lock", "");
|
|
1389
|
+
ensureFile(vfs, "/var/cache/apt/pkgcache.bin", "");
|
|
1390
|
+
ensureFile(vfs, "/var/cache/apt/srcpkgcache.bin", "");
|
|
486
1391
|
// syslog stubs
|
|
487
|
-
ensureFile(vfs, "/var/log/syslog", `${new Date().toUTCString()}
|
|
1392
|
+
ensureFile(vfs, "/var/log/syslog", `${new Date().toUTCString()} ${""} kernel: Virtual container started\n`);
|
|
488
1393
|
ensureFile(vfs, "/var/log/auth.log", "");
|
|
489
1394
|
ensureFile(vfs, "/var/log/kern.log", "");
|
|
490
1395
|
ensureFile(vfs, "/var/log/dpkg.log", "");
|
|
491
1396
|
ensureFile(vfs, "/var/log/apt/history.log", "");
|
|
492
1397
|
ensureFile(vfs, "/var/log/apt/term.log", "");
|
|
493
|
-
|
|
494
|
-
|
|
1398
|
+
ensureFile(vfs, "/var/log/faillog", "");
|
|
1399
|
+
ensureFile(vfs, "/var/log/lastlog", "");
|
|
1400
|
+
ensureFile(vfs, "/var/log/wtmp", "");
|
|
1401
|
+
ensureFile(vfs, "/var/log/btmp", "");
|
|
1402
|
+
ensureFile(vfs, "/var/log/alternatives.log", "");
|
|
1403
|
+
// /run
|
|
495
1404
|
ensureDir(vfs, "/run");
|
|
496
1405
|
ensureDir(vfs, "/run/lock");
|
|
1406
|
+
ensureDir(vfs, "/run/lock/subsys");
|
|
497
1407
|
ensureDir(vfs, "/run/systemd");
|
|
1408
|
+
ensureDir(vfs, "/run/systemd/ask-password");
|
|
1409
|
+
ensureDir(vfs, "/run/systemd/sessions");
|
|
1410
|
+
ensureDir(vfs, "/run/systemd/users");
|
|
498
1411
|
ensureDir(vfs, "/run/user");
|
|
1412
|
+
ensureDir(vfs, "/run/dbus");
|
|
1413
|
+
ensureDir(vfs, "/run/adduser");
|
|
499
1414
|
ensureFile(vfs, "/run/utmp", "");
|
|
1415
|
+
ensureFile(vfs, "/run/dbus/system_bus_socket", "");
|
|
500
1416
|
}
|
|
501
1417
|
// ─── /bin + /sbin symlinks ────────────────────────────────────────────────────
|
|
502
1418
|
function bootstrapBin(vfs) {
|
|
503
|
-
// Modern
|
|
1419
|
+
// Modern Fortune Nyx: /bin and /sbin are symlinks to /usr/bin and /usr/sbin
|
|
504
1420
|
if (!vfs.exists("/bin"))
|
|
505
1421
|
vfs.symlink("/usr/bin", "/bin");
|
|
506
1422
|
if (!vfs.exists("/sbin"))
|
|
@@ -512,51 +1428,64 @@ function bootstrapBin(vfs) {
|
|
|
512
1428
|
ensureDir(vfs, "/lib64");
|
|
513
1429
|
ensureDir(vfs, "/lib/x86_64-linux-gnu");
|
|
514
1430
|
ensureDir(vfs, "/lib/modules");
|
|
1431
|
+
// lib64 symlink (standard on x86_64)
|
|
1432
|
+
if (!vfs.exists("/lib64/ld-linux-x86-64.so.2")) {
|
|
1433
|
+
ensureFile(vfs, "/lib64/ld-linux-x86-64.so.2", "", 0o755);
|
|
1434
|
+
}
|
|
515
1435
|
}
|
|
516
1436
|
// ─── /tmp ─────────────────────────────────────────────────────────────────────
|
|
517
1437
|
function bootstrapTmp(vfs) {
|
|
518
1438
|
ensureDir(vfs, "/tmp", 0o1777);
|
|
1439
|
+
// node compile cache dir (present in real env)
|
|
1440
|
+
ensureDir(vfs, "/tmp/node-compile-cache", 0o1777);
|
|
519
1441
|
}
|
|
520
1442
|
// ─── /root home ───────────────────────────────────────────────────────────────
|
|
521
1443
|
function bootstrapRoot(vfs) {
|
|
522
1444
|
ensureDir(vfs, "/root", 0o700);
|
|
1445
|
+
ensureDir(vfs, "/root/.ssh", 0o700);
|
|
1446
|
+
ensureDir(vfs, "/root/.config", 0o755);
|
|
1447
|
+
ensureDir(vfs, "/root/.config/pip", 0o755);
|
|
1448
|
+
ensureDir(vfs, "/root/.local", 0o755);
|
|
1449
|
+
ensureDir(vfs, "/root/.local/share", 0o755);
|
|
523
1450
|
ensureFile(vfs, "/root/.bashrc", `${[
|
|
524
1451
|
"# root .bashrc",
|
|
525
1452
|
"export PS1='\\[\\033[0;31m\\]\\u@\\h\\[\\033[0m\\]:\\[\\033[0;34m\\]\\w\\[\\033[0m\\]# '",
|
|
526
1453
|
"export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
1454
|
+
"export LANG=en_US.UTF-8",
|
|
527
1455
|
"alias ll='ls -la'",
|
|
528
1456
|
"alias la='ls -A'",
|
|
1457
|
+
"alias l='ls -CF'",
|
|
529
1458
|
].join("\n")}\n`);
|
|
530
1459
|
ensureFile(vfs, "/root/.profile", "[ -f ~/.bashrc ] && . ~/.bashrc\n");
|
|
1460
|
+
ensureFile(vfs, "/root/.bash_logout", "# ~/.bash_logout\n");
|
|
1461
|
+
ensureFile(vfs, "/root/.config/pip/pip.conf", "[global]\nbreak-system-packages = true\n");
|
|
531
1462
|
}
|
|
532
|
-
// ─── /opt /srv /mnt /media
|
|
1463
|
+
// ─── /opt /srv /mnt /media ────────────────────────────────────────────────────
|
|
533
1464
|
function bootstrapMisc(vfs, props) {
|
|
534
1465
|
ensureDir(vfs, "/opt");
|
|
1466
|
+
ensureDir(vfs, "/opt/rclone");
|
|
535
1467
|
ensureDir(vfs, "/srv");
|
|
536
1468
|
ensureDir(vfs, "/mnt");
|
|
537
1469
|
ensureDir(vfs, "/media");
|
|
538
|
-
|
|
539
|
-
//
|
|
1470
|
+
// /boot — no kernel images in Firecracker containers (kernel is external),
|
|
1471
|
+
// but maintain the directory structure for tool compatibility
|
|
540
1472
|
ensureDir(vfs, "/boot");
|
|
541
1473
|
ensureDir(vfs, "/boot/grub");
|
|
542
|
-
ensureDir(vfs, "/boot/grub/grub.cfg.d");
|
|
543
1474
|
ensureFile(vfs, "/boot/grub/grub.cfg", `${[
|
|
544
1475
|
"# GRUB configuration (virtual)",
|
|
545
1476
|
"set default=0",
|
|
546
|
-
"set timeout=
|
|
1477
|
+
"set timeout=0",
|
|
547
1478
|
"",
|
|
548
1479
|
`menuentry "Fortune GNU/Linux" {`,
|
|
549
|
-
` linux /vmlinuz root=/dev/
|
|
550
|
-
` initrd /initrd.img`,
|
|
1480
|
+
` linux /vmlinuz-${props.kernel} root=/dev/vda rw console=ttyS0`,
|
|
1481
|
+
` initrd /initrd.img-${props.kernel}`,
|
|
551
1482
|
`}`,
|
|
552
1483
|
].join("\n")}\n`);
|
|
553
|
-
// kernel + initrd stubs in /boot
|
|
554
1484
|
const kver = props.kernel;
|
|
555
1485
|
ensureFile(vfs, `/boot/vmlinuz-${kver}`, "", 0o644);
|
|
556
1486
|
ensureFile(vfs, `/boot/initrd.img-${kver}`, "", 0o644);
|
|
557
1487
|
ensureFile(vfs, `/boot/System.map-${kver}`, `${kver} virtual\n`, 0o644);
|
|
558
|
-
ensureFile(vfs, `/boot/config-${kver}`, `# Linux kernel config ${kver}\n`, 0o644);
|
|
559
|
-
// root-level symlinks (Debian convention)
|
|
1488
|
+
ensureFile(vfs, `/boot/config-${kver}`, `# Linux kernel config ${kver}\nCONFIG_VIRTIO=y\nCONFIG_VIRTIO_BLK=y\nCONFIG_VIRTIO_NET=y\nCONFIG_KVM_GUEST=y\n`, 0o644);
|
|
560
1489
|
if (!vfs.exists("/vmlinuz"))
|
|
561
1490
|
vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz");
|
|
562
1491
|
if (!vfs.exists("/vmlinuz.old"))
|
|
@@ -565,17 +1494,14 @@ function bootstrapMisc(vfs, props) {
|
|
|
565
1494
|
vfs.symlink(`/boot/initrd.img-${kver}`, "/initrd.img");
|
|
566
1495
|
if (!vfs.exists("/initrd.img.old"))
|
|
567
1496
|
vfs.symlink(`/boot/initrd.img-${kver}`, "/initrd.img.old");
|
|
568
|
-
// /snap —
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
// /lost+found — ext4 fsck recovery dir (mode 0o700, root only)
|
|
1497
|
+
// No /snap — not present in Firecracker container
|
|
1498
|
+
// /proc/cmdline confirms: no snapd boot args
|
|
1499
|
+
// /lost+found — ext4 recovery
|
|
572
1500
|
ensureDir(vfs, "/lost+found", 0o700);
|
|
1501
|
+
// /home — users managed by bootstrapRoot + syncEtcPasswd
|
|
1502
|
+
ensureDir(vfs, "/home");
|
|
573
1503
|
}
|
|
574
1504
|
// ── Static rootfs snapshot cache ─────────────────────────────────────────────
|
|
575
|
-
// The static parts of the rootfs (dev, usr, var, bin, tmp, etc, sys, misc)
|
|
576
|
-
// are identical for all shells sharing the same hostname+props.
|
|
577
|
-
// We build them once, serialise to VFSB binary, and clone via decodeVfs()
|
|
578
|
-
// for each new shell — avoiding ~250 JS object allocations per instance.
|
|
579
1505
|
const _staticRootfsCache = new Map();
|
|
580
1506
|
function _staticCacheKey(hostname, props) {
|
|
581
1507
|
return `${hostname}|${props.kernel}|${props.os}|${props.arch}`;
|
|
@@ -591,7 +1517,6 @@ export function getStaticRootfsSnapshot(hostname, props) {
|
|
|
591
1517
|
const cached = _staticRootfsCache.get(key);
|
|
592
1518
|
if (cached)
|
|
593
1519
|
return cached;
|
|
594
|
-
// Build the static subset into a temporary VFS
|
|
595
1520
|
const tmp = new VirtualFileSystem({ mode: "memory" });
|
|
596
1521
|
bootstrapEtc(tmp, hostname, props);
|
|
597
1522
|
bootstrapSys(tmp, hostname, props);
|
|
@@ -619,14 +1544,20 @@ export function getStaticRootfsSnapshot(hostname, props) {
|
|
|
619
1544
|
* @param sessions Active sessions (for /proc/<pid> population).
|
|
620
1545
|
*/
|
|
621
1546
|
export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime, sessions = []) {
|
|
622
|
-
// Fast path: clone the cached static rootfs snapshot (VFSB decode ~0.07ms)
|
|
623
|
-
// instead of rebuilding ~250 JS objects from scratch each time.
|
|
624
1547
|
const snapshot = getStaticRootfsSnapshot(hostname, props);
|
|
625
|
-
vfs.
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1548
|
+
const hasRestoredData = vfs.getMode() === "fs" && vfs.exists("/home");
|
|
1549
|
+
if (hasRestoredData) {
|
|
1550
|
+
// Snapshot was already restored — merge static rootfs without
|
|
1551
|
+
// clobbering user files and directories.
|
|
1552
|
+
vfs.mergeRootTree(decodeVfs(snapshot));
|
|
1553
|
+
}
|
|
1554
|
+
else {
|
|
1555
|
+
// Fresh start — replace the empty tree with the full static rootfs.
|
|
1556
|
+
vfs.importRootTree(decodeVfs(snapshot));
|
|
1557
|
+
}
|
|
1558
|
+
bootstrapRoot(vfs);
|
|
1559
|
+
refreshProc(vfs, props, hostname, shellStartTime, sessions);
|
|
1560
|
+
syncEtcPasswd(vfs, users);
|
|
630
1561
|
}
|
|
631
1562
|
// ─── optional live engine ─────────────────────────────────────────────────────
|
|
632
1563
|
/**
|