typescript-virtual-container 1.5.11 → 1.6.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/README.md +236 -456
- package/dist/.tsbuildinfo +1 -1
- package/dist/Honeypot/index.d.ts +9 -0
- package/dist/Honeypot/index.js +57 -0
- package/dist/SSHMimic/exec.d.ts +4 -0
- package/dist/SSHMimic/exec.js +4 -0
- package/dist/SSHMimic/executor.d.ts +10 -1
- package/dist/SSHMimic/executor.js +18 -8
- package/dist/SSHMimic/hostKey.d.ts +5 -0
- package/dist/SSHMimic/hostKey.js +5 -0
- package/dist/SSHMimic/loginBanner.d.ts +7 -0
- package/dist/SSHMimic/loginBanner.js +4 -0
- package/dist/SSHMimic/loginFormat.d.ts +4 -0
- package/dist/SSHMimic/loginFormat.js +4 -0
- package/dist/SSHMimic/prompt.d.ts +9 -0
- package/dist/SSHMimic/prompt.js +9 -0
- package/dist/SSHMimic/scp.d.ts +18 -0
- package/dist/SSHMimic/scp.js +14 -0
- package/dist/VirtualFileSystem/binaryPack.d.ts +7 -3
- package/dist/VirtualFileSystem/binaryPack.js +32 -10
- package/dist/VirtualFileSystem/index.d.ts +29 -0
- package/dist/VirtualFileSystem/index.js +126 -5
- package/dist/VirtualFileSystem/internalTypes.d.ts +4 -0
- package/dist/VirtualFileSystem/journal.d.ts +10 -4
- package/dist/VirtualFileSystem/journal.js +12 -2
- package/dist/VirtualFileSystem/path.d.ts +23 -1
- package/dist/VirtualFileSystem/path.js +23 -3
- package/dist/VirtualPackageManager/index.js +1 -1
- package/dist/VirtualShell/index.d.ts +3 -0
- package/dist/VirtualShell/index.js +12 -3
- package/dist/VirtualUserManager/index.d.ts +20 -1
- package/dist/VirtualUserManager/index.js +52 -15
- package/dist/commands/bc.d.ts +5 -0
- package/dist/commands/bc.js +5 -0
- package/dist/commands/cat.js +2 -2
- package/dist/commands/chgrp.d.ts +7 -0
- package/dist/commands/chgrp.js +42 -0
- package/dist/commands/chown.d.ts +7 -0
- package/dist/commands/chown.js +79 -0
- package/dist/commands/cp.js +4 -3
- package/dist/commands/dd.d.ts +7 -0
- package/dist/commands/dd.js +60 -0
- package/dist/commands/declare.js +0 -2
- package/dist/commands/expr.d.ts +7 -0
- package/dist/commands/expr.js +63 -0
- package/dist/commands/fun.d.ts +5 -0
- package/dist/commands/fun.js +5 -1
- package/dist/commands/help.d.ts +5 -0
- package/dist/commands/help.js +5 -0
- package/dist/commands/helpers.d.ts +43 -0
- package/dist/commands/helpers.js +61 -0
- package/dist/commands/id.d.ts +5 -0
- package/dist/commands/id.js +5 -0
- package/dist/commands/ip.d.ts +1 -0
- package/dist/commands/ip.js +50 -23
- package/dist/commands/jobs.js +43 -9
- package/dist/commands/kill.d.ts +1 -0
- package/dist/commands/kill.js +13 -5
- package/dist/commands/last.js +1 -1
- package/dist/commands/ln.d.ts +5 -0
- package/dist/commands/ln.js +5 -0
- package/dist/commands/ls.d.ts +5 -0
- package/dist/commands/ls.js +19 -4
- package/dist/commands/lsb-release.js +1 -1
- package/dist/commands/man.d.ts +5 -0
- package/dist/commands/man.js +5 -0
- package/dist/commands/manuals-bundle.js +242 -0
- package/dist/commands/miscutils.d.ts +43 -0
- package/dist/commands/miscutils.js +233 -0
- package/dist/commands/mkdir.js +3 -2
- package/dist/commands/mv.js +4 -3
- package/dist/commands/netcat.d.ts +7 -0
- package/dist/commands/netcat.js +64 -0
- package/dist/commands/nice.d.ts +7 -0
- package/dist/commands/nice.js +22 -0
- package/dist/commands/nohup.d.ts +7 -0
- package/dist/commands/nohup.js +18 -0
- package/dist/commands/ping.d.ts +2 -1
- package/dist/commands/ping.js +46 -8
- package/dist/commands/procUtils.d.ts +13 -0
- package/dist/commands/procUtils.js +72 -0
- package/dist/commands/pwd.d.ts +5 -0
- package/dist/commands/pwd.js +5 -0
- package/dist/commands/python.js +0 -4
- package/dist/commands/read.js +0 -1
- package/dist/commands/registry.d.ts +37 -0
- package/dist/commands/registry.js +73 -0
- package/dist/commands/rm.js +3 -2
- package/dist/commands/runtime.d.ts +47 -1
- package/dist/commands/runtime.js +60 -5
- package/dist/commands/sh.d.ts +5 -0
- package/dist/commands/sh.js +5 -0
- package/dist/commands/stat.js +3 -2
- package/dist/commands/strace.js +0 -1
- package/dist/commands/sysinfo.d.ts +19 -0
- package/dist/commands/sysinfo.js +73 -0
- package/dist/commands/test.d.ts +5 -0
- package/dist/commands/test.js +5 -0
- package/dist/commands/textutils.d.ts +25 -0
- package/dist/commands/textutils.js +171 -0
- package/dist/commands/top.d.ts +7 -0
- package/dist/commands/top.js +54 -0
- package/dist/commands/touch.js +6 -2
- package/dist/commands/tr.d.ts +5 -0
- package/dist/commands/tr.js +5 -0
- package/dist/commands/w.js +1 -1
- package/dist/commands/which.d.ts +5 -0
- package/dist/commands/which.js +5 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +4 -0
- package/dist/modules/VirtualNetworkManager.d.ts +123 -0
- package/dist/modules/VirtualNetworkManager.js +201 -0
- package/dist/modules/linuxRootfs.d.ts +4 -3
- package/dist/modules/linuxRootfs.js +115 -74
- package/dist/modules/neofetch.d.ts +2 -0
- package/dist/modules/neofetch.js +3 -2
- package/dist/modules/pacmanGame.d.ts +2 -0
- package/dist/modules/pacmanGame.js +1 -0
- package/dist/modules/shellInteractive.d.ts +2 -0
- package/dist/modules/shellInteractive.js +2 -0
- package/dist/modules/shellRuntime.d.ts +7 -0
- package/dist/modules/shellRuntime.js +6 -0
- package/dist/modules/webTermRenderer.js +0 -7
- package/dist/types/commands.d.ts +1 -1
- package/dist/types/vfs.d.ts +8 -0
- package/dist/utils/argv.d.ts +22 -3
- package/dist/utils/argv.js +22 -3
- package/dist/utils/perfLogger.d.ts +10 -2
- package/dist/utils/perfLogger.js +7 -14
- package/dist/utils/shellSession.d.ts +35 -0
- package/dist/utils/shellSession.js +35 -0
- package/package.json +1 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VirtualNetworkManager — a configurable virtual network stack.
|
|
3
|
+
*
|
|
4
|
+
* Maintains a routing table, ARP cache, and interface list with simulated
|
|
5
|
+
* latency and packet loss. Used by the `ip`, `ping`, and `netstat` commands
|
|
6
|
+
* to produce dynamic, deterministic output instead of hardcoded strings.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A virtual network interface, either loopback or ethernet,
|
|
10
|
+
* with MAC address, MTU, IPv4/IPv6 addresses, and link state.
|
|
11
|
+
*/
|
|
12
|
+
export interface VirtualInterface {
|
|
13
|
+
name: string;
|
|
14
|
+
type: "loopback" | "ether";
|
|
15
|
+
mac: string;
|
|
16
|
+
mtu: number;
|
|
17
|
+
state: "UP" | "DOWN";
|
|
18
|
+
ipv4: string;
|
|
19
|
+
ipv4Mask: number;
|
|
20
|
+
ipv6: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A routing table entry mapping a destination subnet
|
|
24
|
+
* to a gateway and outbound device.
|
|
25
|
+
*/
|
|
26
|
+
export interface VirtualRoute {
|
|
27
|
+
destination: string;
|
|
28
|
+
gateway: string;
|
|
29
|
+
netmask: string;
|
|
30
|
+
device: string;
|
|
31
|
+
flags: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* An ARP cache entry mapping an IP address to a MAC address
|
|
35
|
+
* on a specific device, with neighbour reachability state.
|
|
36
|
+
*/
|
|
37
|
+
export interface VirtualArpEntry {
|
|
38
|
+
ip: string;
|
|
39
|
+
mac: string;
|
|
40
|
+
device: string;
|
|
41
|
+
state: "REACHABLE" | "STALE" | "PERMANENT";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Virtual network stack with routing table, ARP cache, and interface management.
|
|
45
|
+
* Provides dynamic data for `ip`, `ping`, and `/proc/net/*`.
|
|
46
|
+
*/
|
|
47
|
+
export declare class VirtualNetworkManager {
|
|
48
|
+
private interfaces;
|
|
49
|
+
private routes;
|
|
50
|
+
private arpCache;
|
|
51
|
+
/**
|
|
52
|
+
* Returns a copy of all configured interfaces.
|
|
53
|
+
* @returns Array of VirtualInterface objects.
|
|
54
|
+
*/
|
|
55
|
+
getInterfaces(): VirtualInterface[];
|
|
56
|
+
/**
|
|
57
|
+
* Returns a copy of the routing table.
|
|
58
|
+
* @returns Array of VirtualRoute objects.
|
|
59
|
+
*/
|
|
60
|
+
getRoutes(): VirtualRoute[];
|
|
61
|
+
/**
|
|
62
|
+
* Returns a copy of the ARP cache.
|
|
63
|
+
* @returns Array of VirtualArpEntry objects.
|
|
64
|
+
*/
|
|
65
|
+
getArpCache(): VirtualArpEntry[];
|
|
66
|
+
/**
|
|
67
|
+
* Adds a new route to the routing table.
|
|
68
|
+
* @param dest Destination network or "default".
|
|
69
|
+
* @param gateway Gateway IP address.
|
|
70
|
+
* @param netmask Subnet mask (e.g. "255.255.255.0").
|
|
71
|
+
* @param device Outbound device name (e.g. "eth0").
|
|
72
|
+
*/
|
|
73
|
+
addRoute(dest: string, gateway: string, netmask: string, device: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Removes a route by destination network.
|
|
76
|
+
* @param dest Destination network to remove.
|
|
77
|
+
* @returns True if a route was removed, false if no match.
|
|
78
|
+
*/
|
|
79
|
+
delRoute(dest: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Sets the administrative state of an interface.
|
|
82
|
+
* @param name Interface name ("lo", "eth0", etc.).
|
|
83
|
+
* @param state Desired state: "UP" or "DOWN".
|
|
84
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
85
|
+
*/
|
|
86
|
+
setInterfaceState(name: string, state: "UP" | "DOWN"): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Sets the IPv4 address and prefix length on an interface.
|
|
89
|
+
* @param name Interface name.
|
|
90
|
+
* @param ipv4 New IPv4 address.
|
|
91
|
+
* @param mask New subnet mask prefix length (e.g. 24).
|
|
92
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
93
|
+
*/
|
|
94
|
+
setInterfaceIp(name: string, ipv4: string, mask: number): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Simulates an ICMP ping to the given host.
|
|
97
|
+
* @param host Target IP address or hostname.
|
|
98
|
+
* @returns Latency in milliseconds if reachable, -1 if unreachable.
|
|
99
|
+
*/
|
|
100
|
+
ping(host: string): number;
|
|
101
|
+
/**
|
|
102
|
+
* Formats all interfaces as `ip addr` output.
|
|
103
|
+
* @returns Formatted string mimicking `ip addr` command.
|
|
104
|
+
*/
|
|
105
|
+
formatIpAddr(): string;
|
|
106
|
+
/**
|
|
107
|
+
* Formats the routing table as `ip route` output.
|
|
108
|
+
* @returns Formatted string mimicking `ip route` command.
|
|
109
|
+
*/
|
|
110
|
+
formatIpRoute(): string;
|
|
111
|
+
/**
|
|
112
|
+
* Formats all interfaces as `ip link` output.
|
|
113
|
+
* @returns Formatted string mimicking `ip link` command.
|
|
114
|
+
*/
|
|
115
|
+
formatIpLink(): string;
|
|
116
|
+
/**
|
|
117
|
+
* Formats the ARP cache as `ip neigh` output.
|
|
118
|
+
* @returns Formatted string mimicking `ip neigh` command.
|
|
119
|
+
*/
|
|
120
|
+
formatIpNeigh(): string;
|
|
121
|
+
private _maskToCidr;
|
|
122
|
+
private _ipForDevice;
|
|
123
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VirtualNetworkManager — a configurable virtual network stack.
|
|
3
|
+
*
|
|
4
|
+
* Maintains a routing table, ARP cache, and interface list with simulated
|
|
5
|
+
* latency and packet loss. Used by the `ip`, `ping`, and `netstat` commands
|
|
6
|
+
* to produce dynamic, deterministic output instead of hardcoded strings.
|
|
7
|
+
*/
|
|
8
|
+
function randomMac() {
|
|
9
|
+
const hex = () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0");
|
|
10
|
+
return `02:42:${hex()}:${hex()}:${hex()}:${hex()}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Virtual network stack with routing table, ARP cache, and interface management.
|
|
14
|
+
* Provides dynamic data for `ip`, `ping`, and `/proc/net/*`.
|
|
15
|
+
*/
|
|
16
|
+
export class VirtualNetworkManager {
|
|
17
|
+
interfaces = [
|
|
18
|
+
{
|
|
19
|
+
name: "lo",
|
|
20
|
+
type: "loopback",
|
|
21
|
+
mac: "00:00:00:00:00:00",
|
|
22
|
+
mtu: 65536,
|
|
23
|
+
state: "UP",
|
|
24
|
+
ipv4: "127.0.0.1",
|
|
25
|
+
ipv4Mask: 8,
|
|
26
|
+
ipv6: "::1",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "eth0",
|
|
30
|
+
type: "ether",
|
|
31
|
+
mac: randomMac(),
|
|
32
|
+
mtu: 1500,
|
|
33
|
+
state: "UP",
|
|
34
|
+
ipv4: "10.0.0.2",
|
|
35
|
+
ipv4Mask: 24,
|
|
36
|
+
ipv6: "fe80::42:aff:fe00:2",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
routes = [
|
|
40
|
+
{ destination: "default", gateway: "10.0.0.1", netmask: "0.0.0.0", device: "eth0", flags: "UG" },
|
|
41
|
+
{ destination: "10.0.0.0", gateway: "0.0.0.0", netmask: "255.255.255.0", device: "eth0", flags: "U" },
|
|
42
|
+
{ destination: "127.0.0.0", gateway: "0.0.0.0", netmask: "255.0.0.0", device: "lo", flags: "U" },
|
|
43
|
+
];
|
|
44
|
+
arpCache = [
|
|
45
|
+
{ ip: "10.0.0.1", mac: "02:42:0a:00:00:01", device: "eth0", state: "REACHABLE" },
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Returns a copy of all configured interfaces.
|
|
49
|
+
* @returns Array of VirtualInterface objects.
|
|
50
|
+
*/
|
|
51
|
+
getInterfaces() {
|
|
52
|
+
return [...this.interfaces];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns a copy of the routing table.
|
|
56
|
+
* @returns Array of VirtualRoute objects.
|
|
57
|
+
*/
|
|
58
|
+
getRoutes() {
|
|
59
|
+
return [...this.routes];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns a copy of the ARP cache.
|
|
63
|
+
* @returns Array of VirtualArpEntry objects.
|
|
64
|
+
*/
|
|
65
|
+
getArpCache() {
|
|
66
|
+
return [...this.arpCache];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Adds a new route to the routing table.
|
|
70
|
+
* @param dest Destination network or "default".
|
|
71
|
+
* @param gateway Gateway IP address.
|
|
72
|
+
* @param netmask Subnet mask (e.g. "255.255.255.0").
|
|
73
|
+
* @param device Outbound device name (e.g. "eth0").
|
|
74
|
+
*/
|
|
75
|
+
addRoute(dest, gateway, netmask, device) {
|
|
76
|
+
this.routes.push({ destination: dest, gateway, netmask, device, flags: "UG" });
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Removes a route by destination network.
|
|
80
|
+
* @param dest Destination network to remove.
|
|
81
|
+
* @returns True if a route was removed, false if no match.
|
|
82
|
+
*/
|
|
83
|
+
delRoute(dest) {
|
|
84
|
+
const idx = this.routes.findIndex((r) => r.destination === dest);
|
|
85
|
+
if (idx === -1)
|
|
86
|
+
return false;
|
|
87
|
+
this.routes.splice(idx, 1);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Sets the administrative state of an interface.
|
|
92
|
+
* @param name Interface name ("lo", "eth0", etc.).
|
|
93
|
+
* @param state Desired state: "UP" or "DOWN".
|
|
94
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
95
|
+
*/
|
|
96
|
+
setInterfaceState(name, state) {
|
|
97
|
+
const iface = this.interfaces.find((i) => i.name === name);
|
|
98
|
+
if (!iface)
|
|
99
|
+
return false;
|
|
100
|
+
iface.state = state;
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Sets the IPv4 address and prefix length on an interface.
|
|
105
|
+
* @param name Interface name.
|
|
106
|
+
* @param ipv4 New IPv4 address.
|
|
107
|
+
* @param mask New subnet mask prefix length (e.g. 24).
|
|
108
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
109
|
+
*/
|
|
110
|
+
setInterfaceIp(name, ipv4, mask) {
|
|
111
|
+
const iface = this.interfaces.find((i) => i.name === name);
|
|
112
|
+
if (!iface)
|
|
113
|
+
return false;
|
|
114
|
+
iface.ipv4 = ipv4;
|
|
115
|
+
iface.ipv4Mask = mask;
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Simulates an ICMP ping to the given host.
|
|
120
|
+
* @param host Target IP address or hostname.
|
|
121
|
+
* @returns Latency in milliseconds if reachable, -1 if unreachable.
|
|
122
|
+
*/
|
|
123
|
+
ping(host) {
|
|
124
|
+
// Loopback always works
|
|
125
|
+
if (host === "127.0.0.1" || host === "localhost" || host === "::1") {
|
|
126
|
+
return 0.05 + Math.random() * 0.1;
|
|
127
|
+
}
|
|
128
|
+
// Check ARP cache / routing
|
|
129
|
+
const arp = this.arpCache.find((e) => e.ip === host);
|
|
130
|
+
if (arp && arp.state === "REACHABLE") {
|
|
131
|
+
return 0.5 + Math.random() * 2;
|
|
132
|
+
}
|
|
133
|
+
// Simulate random packet loss (5%)
|
|
134
|
+
if (Math.random() < 0.05)
|
|
135
|
+
return -1;
|
|
136
|
+
return 0.8 + Math.random() * 5;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Formats all interfaces as `ip addr` output.
|
|
140
|
+
* @returns Formatted string mimicking `ip addr` command.
|
|
141
|
+
*/
|
|
142
|
+
formatIpAddr() {
|
|
143
|
+
const lines = [];
|
|
144
|
+
let idx = 1;
|
|
145
|
+
for (const iface of this.interfaces) {
|
|
146
|
+
const flags = iface.state === "UP"
|
|
147
|
+
? (iface.type === "loopback" ? "LOOPBACK,UP,LOWER_UP" : "BROADCAST,MULTICAST,UP,LOWER_UP")
|
|
148
|
+
: "DOWN";
|
|
149
|
+
lines.push(`${idx}: ${iface.name}: <${flags}> mtu ${iface.mtu} qdisc mq state ${iface.state === "UP" ? "UNKNOWN" : "DOWN"} group default qlen 1000`);
|
|
150
|
+
lines.push(` link/${iface.type === "loopback" ? "loopback" : "ether"} ${iface.mac} brd ff:ff:ff:ff:ff:ff`);
|
|
151
|
+
lines.push(` inet ${iface.ipv4}/${iface.ipv4Mask} scope global ${iface.name}`);
|
|
152
|
+
lines.push(` valid_lft forever preferred_lft forever`);
|
|
153
|
+
lines.push(` inet6 ${iface.ipv6}/64 scope link`);
|
|
154
|
+
lines.push(` valid_lft forever preferred_lft forever`);
|
|
155
|
+
idx++;
|
|
156
|
+
}
|
|
157
|
+
return lines.join("\n");
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Formats the routing table as `ip route` output.
|
|
161
|
+
* @returns Formatted string mimicking `ip route` command.
|
|
162
|
+
*/
|
|
163
|
+
formatIpRoute() {
|
|
164
|
+
return this.routes.map((r) => {
|
|
165
|
+
if (r.destination === "default") {
|
|
166
|
+
return `default via ${r.gateway} dev ${r.device}`;
|
|
167
|
+
}
|
|
168
|
+
return `${r.destination}/${this._maskToCidr(r.netmask)} dev ${r.device} proto kernel scope link src ${this._ipForDevice(r.device)}`;
|
|
169
|
+
}).join("\n");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Formats all interfaces as `ip link` output.
|
|
173
|
+
* @returns Formatted string mimicking `ip link` command.
|
|
174
|
+
*/
|
|
175
|
+
formatIpLink() {
|
|
176
|
+
const lines = [];
|
|
177
|
+
let idx = 1;
|
|
178
|
+
for (const iface of this.interfaces) {
|
|
179
|
+
const flags = iface.state === "UP"
|
|
180
|
+
? (iface.type === "loopback" ? "LOOPBACK,UP,LOWER_UP" : "BROADCAST,MULTICAST,UP,LOWER_UP")
|
|
181
|
+
: "DOWN";
|
|
182
|
+
lines.push(`${idx}: ${iface.name}: <${flags}> mtu ${iface.mtu} qdisc mq state ${iface.state === "UP" ? "UNKNOWN" : "DOWN"} mode DEFAULT group default qlen 1000`);
|
|
183
|
+
lines.push(` link/${iface.type === "loopback" ? "loopback" : "ether"} ${iface.mac} brd ff:ff:ff:ff:ff:ff`);
|
|
184
|
+
idx++;
|
|
185
|
+
}
|
|
186
|
+
return lines.join("\n");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Formats the ARP cache as `ip neigh` output.
|
|
190
|
+
* @returns Formatted string mimicking `ip neigh` command.
|
|
191
|
+
*/
|
|
192
|
+
formatIpNeigh() {
|
|
193
|
+
return this.arpCache.map((e) => `${e.ip} dev ${e.device} lladdr ${e.mac} ${e.state}`).join("\n");
|
|
194
|
+
}
|
|
195
|
+
_maskToCidr(mask) {
|
|
196
|
+
return mask.split(".").reduce((acc, oct) => acc + (parseInt(oct, 10) ? parseInt(oct, 10).toString(2).split("1").length - 1 : 0), 0);
|
|
197
|
+
}
|
|
198
|
+
_ipForDevice(device) {
|
|
199
|
+
return this.interfaces.find((i) => i.name === device)?.ipv4 ?? "0.0.0.0";
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import VirtualFileSystem from "../VirtualFileSystem";
|
|
21
21
|
import type { ShellProperties } from "../VirtualShell";
|
|
22
22
|
import type { VirtualActiveSession, VirtualUserManager } from "../VirtualUserManager";
|
|
23
|
+
import type { VirtualNetworkManager } from "./VirtualNetworkManager";
|
|
23
24
|
/**
|
|
24
25
|
* Sync `/etc/passwd`, `/etc/group`, and `/etc/shadow` from the
|
|
25
26
|
* VirtualUserManager's current user list into the VFS.
|
|
@@ -32,7 +33,7 @@ export declare function syncEtcPasswd(vfs: VirtualFileSystem, users: VirtualUser
|
|
|
32
33
|
*
|
|
33
34
|
* Safe to call repeatedly — acts as a live kernel state snapshot.
|
|
34
35
|
*/
|
|
35
|
-
export declare function refreshProc(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, shellStartTime: number, sessions?: VirtualActiveSession[]): void;
|
|
36
|
+
export declare function refreshProc(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, shellStartTime: number, sessions?: VirtualActiveSession[], network?: VirtualNetworkManager): void;
|
|
36
37
|
/**
|
|
37
38
|
* Build or retrieve the static rootfs VFSB snapshot for the given
|
|
38
39
|
* hostname + ShellProperties combination.
|
|
@@ -51,7 +52,7 @@ export declare function getStaticRootfsSnapshot(hostname: string, props: ShellPr
|
|
|
51
52
|
* @param shellStartTime Unix ms of shell creation (for uptime).
|
|
52
53
|
* @param sessions Active sessions (for /proc/<pid> population).
|
|
53
54
|
*/
|
|
54
|
-
export declare function bootstrapLinuxRootfs(vfs: VirtualFileSystem, users: VirtualUserManager, hostname: string, props: ShellProperties, shellStartTime: number, sessions?: VirtualActiveSession[]): void;
|
|
55
|
+
export declare function bootstrapLinuxRootfs(vfs: VirtualFileSystem, users: VirtualUserManager, hostname: string, props: ShellProperties, shellStartTime: number, sessions?: VirtualActiveSession[], network?: VirtualNetworkManager): void;
|
|
55
56
|
/**
|
|
56
57
|
* Engine for runtimes that want periodic /proc refresh (e.g. web shell
|
|
57
58
|
* with live `top`/`ps` output). Call `.boot()` once, then `.tick()` on
|
|
@@ -63,7 +64,7 @@ export declare function bootstrapLinuxRootfs(vfs: VirtualFileSystem, users: Virt
|
|
|
63
64
|
* setInterval(() => engine.tick(shell.listActiveSessions()), 5000);
|
|
64
65
|
* ```
|
|
65
66
|
*/
|
|
66
|
-
export declare function createLinuxRootfsEngine(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, startTime: number): {
|
|
67
|
+
export declare function createLinuxRootfsEngine(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, startTime: number, network?: VirtualNetworkManager): {
|
|
67
68
|
boot(users: VirtualUserManager, sessions?: VirtualActiveSession[]): void;
|
|
68
69
|
tick(sessions?: VirtualActiveSession[]): void;
|
|
69
70
|
};
|
|
@@ -505,7 +505,7 @@ function bootProcLog(vfs, props) {
|
|
|
505
505
|
*
|
|
506
506
|
* Safe to call repeatedly — acts as a live kernel state snapshot.
|
|
507
507
|
*/
|
|
508
|
-
export function refreshProc(vfs, props, hostname, shellStartTime, sessions = []) {
|
|
508
|
+
export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [], network) {
|
|
509
509
|
ensureDir(vfs, "/proc");
|
|
510
510
|
const uptimeSec = Math.floor((Date.now() - shellStartTime) / 1000);
|
|
511
511
|
const idleSec = Math.floor(uptimeSec * 0.9);
|
|
@@ -589,6 +589,59 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [])
|
|
|
589
589
|
const load = (Math.random() * 0.3).toFixed(2);
|
|
590
590
|
const numProcs = 1 + sessions.length;
|
|
591
591
|
write(vfs, "/proc/loadavg", `${load} ${load} ${load} ${numProcs}/${numProcs} 1\n`);
|
|
592
|
+
// /proc/stat — CPU statistics
|
|
593
|
+
const cpuCount = os.cpus().length;
|
|
594
|
+
const userJiffies = Math.floor(uptimeSec * 100);
|
|
595
|
+
const niceJiffies = Math.floor(uptimeSec * 2);
|
|
596
|
+
const systemJiffies = Math.floor(uptimeSec * 30);
|
|
597
|
+
const idleJiffies = Math.floor(uptimeSec * 800);
|
|
598
|
+
const iowaitJiffies = Math.floor(uptimeSec * 5);
|
|
599
|
+
const irqJiffies = Math.floor(uptimeSec * 1);
|
|
600
|
+
const softirqJiffies = Math.floor(uptimeSec * 2);
|
|
601
|
+
const stealJiffies = Math.floor(uptimeSec * 0);
|
|
602
|
+
const totalJiffies = userJiffies + niceJiffies + systemJiffies + idleJiffies + iowaitJiffies + irqJiffies + softirqJiffies + stealJiffies;
|
|
603
|
+
const cpuStats = `cpu ${userJiffies} ${niceJiffies} ${systemJiffies} ${idleJiffies} ${iowaitJiffies} ${irqJiffies} ${softirqJiffies} ${stealJiffies} 0 0\n`;
|
|
604
|
+
const perCpuStats = Array.from({ length: cpuCount }, (_, i) => `cpu${i} ${Math.floor(userJiffies / cpuCount)} ${Math.floor(niceJiffies / cpuCount)} ${Math.floor(systemJiffies / cpuCount)} ${Math.floor(idleJiffies / cpuCount)} ${Math.floor(iowaitJiffies / cpuCount)} ${Math.floor(irqJiffies / cpuCount)} ${Math.floor(softirqJiffies / cpuCount)} ${Math.floor(stealJiffies / cpuCount)} 0 0`).join("\n");
|
|
605
|
+
write(vfs, "/proc/stat", `${cpuStats}${perCpuStats}\nintr ${Math.floor(totalJiffies * 2)} 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\nctxt ${Math.floor(totalJiffies * 50)}\nbtime ${Math.floor(shellStartTime / 1000)}\nprocesses ${numProcs + 10}\nprocs_running 1\nprocs_blocked 0\n`);
|
|
606
|
+
// /proc/vmstat — virtual memory stats
|
|
607
|
+
const pgpgin = Math.floor(totalJiffies * 0.5);
|
|
608
|
+
const pgpgout = Math.floor(totalJiffies * 0.3);
|
|
609
|
+
const pswpin = 0;
|
|
610
|
+
const pswpout = 0;
|
|
611
|
+
const pgalloc = Math.floor(totalJiffies * 2);
|
|
612
|
+
const pgfault = pgalloc + Math.floor(totalJiffies * 0.5);
|
|
613
|
+
const pgmajfault = Math.floor(totalJiffies * 0.01);
|
|
614
|
+
write(vfs, "/proc/vmstat", `nr_free_pages ${Math.floor(freeMemKb / 4)}\nnr_zone_inactive_anon 0\nnr_zone_active_anon 0\nnr_zone_inactive_file ${Math.floor(cachedKb / 4)}\nnr_zone_active_file ${Math.floor(buffersKb / 4)}\nnr_zone_unevictable 0\nnr_zone_write_pending 0\nnr_mlock 0\nnr_page_table_pages ${pageTablesKb}\nnr_kernel_stack ${Math.floor(totalMemKb * 0.0005)}\nnr_bounce 0\nnr_zspages 0\nnr_free_cma 0\nnuma_hit ${Math.floor(totalJiffies * 3)}\nnuma_miss 0\nnuma_foreign 0\nnuma_interleave 0\nnuma_local ${Math.floor(totalJiffies * 3)}\nnuma_other 0\nnr_inactive_anon 0\nnr_active_anon 0\nnr_inactive_file ${Math.floor(cachedKb / 4)}\nnr_active_file ${Math.floor(buffersKb / 4)}\nnr_unevictable 0\nnr_slab_reclaimable ${Math.floor(slabKb * 0.6)}\nnr_slab_unreclaimable ${Math.floor(slabKb * 0.4)}\nnr_isolated_anon 0\nnr_isolated_file 0\nworkingset_nodes 0\nworkingset_refault 0\nworkingset_activate 0\nworkingset_restore 0\nworkingset_nodereclaim 0\nnr_anon_pages ${Math.floor(totalMemKb * 0.001)}\nnr_mapped ${Math.floor(cachedKb * 0.4)}\nnr_file_pages ${Math.floor(cachedKb * 0.8)}\nnr_dirty ${Math.floor(totalMemKb * 0.001)}\nnr_writeback 0\nnr_writeback_temp 0\nnr_shmem ${Math.floor(totalMemKb * 0.005)}\nnr_shmem_hugepages 0\nnr_shmem_pmdmapped 0\nnr_file_hugepages 0\nnr_file_pmdmapped 0\nnr_anon_transparent_hugepages 0\nnr_vmscan_write 0\nnr_vmscan_immediate_reclaim 0\nnr_dirtied ${Math.floor(totalJiffies * 2)}\nnr_written ${Math.floor(totalJiffies * 2)}\nnr_throttled_written 0\nnr_kernel_misc_reclaimable 0\nnr_reclaim_pages 0\nnr_zone_active_anon 0\nnr_zone_active_file ${Math.floor(buffersKb / 4)}\npgpgin ${pgpgin}\npgpgout ${pgpgout}\npswpin ${pswpin}\npswpout ${pswpout}\npgalloc_dma 0\npgalloc_dma32 ${Math.floor(pgalloc * 0.3)}\npgalloc_normal ${Math.floor(pgalloc * 0.7)}\npgalloc_movable 0\npgfree ${pgalloc}\npgactivate ${Math.floor(totalJiffies * 0.5)}\npgdeactivate 0\npgfault ${pgfault}\npgmajfault ${pgmajfault}\npglazyfree 0\npgrefill 0\npgsteal_kswapd 0\npgsteal_direct 0\npgscan_kswapd 0\npgscan_direct 0\npgskip_dma 0\npgskip_dma32 0\npgskip_normal 0\npgskip_movable 0\npgmigrate_success 0\npgmigrate_fail 0\ncompact_migrate_scanned 0\ncompact_free_scanned 0\ncompact_isolated 0\ncompact_stall 0\ncompact_fail 0\ncompact_success 0\nhtlb_buddy_alloc_success 0\nhtlb_buddy_alloc_fail 0\nunevictable_pgs_culled 0\nunevictable_pgs_scanned 0\nunevictable_pgs_rescued 0\nunevictable_pgs_mlocked 0\nunevictable_pgs_munlocked 0\nunevictable_pgs_cleared 0\nunevictable_pgs_stranded 0\nswap_ra 0\nswap_ra_hit 0\nnr_hugepages 0\nnr_hugepages_bootmem 0\n\n`);
|
|
615
|
+
// /proc/pressure — PSI (Pressure Stall Information)
|
|
616
|
+
ensureDir(vfs, "/proc/pressure");
|
|
617
|
+
const someAvg10 = (Math.random() * 0.3).toFixed(2);
|
|
618
|
+
const someAvg60 = (Math.random() * 0.2 + 0.1).toFixed(2);
|
|
619
|
+
const someAvg300 = (Math.random() * 0.1 + 0.05).toFixed(2);
|
|
620
|
+
const someTotal = Math.floor(totalJiffies * 10);
|
|
621
|
+
write(vfs, "/proc/pressure/cpu", `some avg10=${someAvg10} avg60=${someAvg60} avg300=${someAvg300} total=${someTotal}\n`);
|
|
622
|
+
write(vfs, "/proc/pressure/memory", `some avg10=${(Number(someAvg10) * 0.5).toFixed(2)} avg60=${(Number(someAvg60) * 0.3).toFixed(2)} avg300=${(Number(someAvg300) * 0.2).toFixed(2)} total=${Math.floor(someTotal * 0.3)}\n`);
|
|
623
|
+
write(vfs, "/proc/pressure/io", `some avg10=${(Number(someAvg10) * 0.7).toFixed(2)} avg60=${(Number(someAvg60) * 0.5).toFixed(2)} avg300=${(Number(someAvg300) * 0.3).toFixed(2)} total=${Math.floor(someTotal * 0.5)}\n`);
|
|
624
|
+
// /proc/modules
|
|
625
|
+
write(vfs, "/proc/modules", `${[
|
|
626
|
+
"virtio 163840 10 - Live 0x0000000000000000",
|
|
627
|
+
"virtio_ring 28672 10 virtio, Live 0x0000000000000000",
|
|
628
|
+
"virtio_blk 20480 10 - Live 0x0000000000000000",
|
|
629
|
+
"virtio_net 57344 10 - Live 0x0000000000000000",
|
|
630
|
+
"virtio_console 28672 10 - Live 0x0000000000000000",
|
|
631
|
+
"virtio_pci 24576 10 - Live 0x0000000000000000",
|
|
632
|
+
"virtio_pci_legacy_dev 12288 1 virtio_pci, Live 0x0000000000000000",
|
|
633
|
+
"virtio_pci_modern_dev 16384 1 virtio_pci, Live 0x0000000000000000",
|
|
634
|
+
"ext4 847872 10 - Live 0x0000000000000000",
|
|
635
|
+
"jbd2 131072 1 ext4, Live 0x0000000000000000",
|
|
636
|
+
"mbcache 16384 1 ext4, Live 0x0000000000000000",
|
|
637
|
+
"fuse 172032 10 - Live 0x0000000000000000",
|
|
638
|
+
"overlay 131072 10 - Live 0x0000000000000000",
|
|
639
|
+
"nf_tables 188416 10 - Live 0x0000000000000000",
|
|
640
|
+
"tun 49152 10 - Live 0x0000000000000000",
|
|
641
|
+
"bridge 286720 10 - Live 0x0000000000000000",
|
|
642
|
+
"dm_mod 155648 10 - Live 0x0000000000000000",
|
|
643
|
+
"crc32c_intel 24576 10 - Live 0x0000000000000000",
|
|
644
|
+
].join("\n")}\n`);
|
|
592
645
|
// /proc/cmdline — Firecracker boot args
|
|
593
646
|
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`);
|
|
594
647
|
// /proc/filesystems — matching real container
|
|
@@ -651,26 +704,69 @@ export function refreshProc(vfs, props, hostname, shellStartTime, sessions = [])
|
|
|
651
704
|
write(vfs, "/proc/mounts", mountsContent);
|
|
652
705
|
ensureDir(vfs, "/proc/self");
|
|
653
706
|
write(vfs, "/proc/self/mounts", mountsContent);
|
|
654
|
-
// /proc/net
|
|
707
|
+
// /proc/net — dynamic when network manager is available
|
|
655
708
|
ensureDir(vfs, "/proc/net");
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
709
|
+
if (network) {
|
|
710
|
+
const ifaces = network.getInterfaces();
|
|
711
|
+
const routes = network.getRoutes();
|
|
712
|
+
const arpCache = network.getArpCache();
|
|
713
|
+
const ipToHex = (ip) => ip.split(".")
|
|
714
|
+
.reverse()
|
|
715
|
+
.map((n) => parseInt(n, 10).toString(16).padStart(2, "0"))
|
|
716
|
+
.join("")
|
|
717
|
+
.toUpperCase();
|
|
718
|
+
// /proc/net/dev
|
|
719
|
+
const devHeader = "Inter-| Receive | Transmit\n face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed";
|
|
720
|
+
const devLines = ifaces.map((iface) => {
|
|
721
|
+
const name = iface.name.padStart(4);
|
|
722
|
+
if (iface.name === "lo") {
|
|
723
|
+
return `${name}: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0`;
|
|
724
|
+
}
|
|
725
|
+
const rxBytes = Math.floor(Math.random() * 200000);
|
|
726
|
+
const rxPkts = Math.floor(Math.random() * 2000);
|
|
727
|
+
const txBytes = Math.floor(Math.random() * 50000000);
|
|
728
|
+
const txPkts = Math.floor(Math.random() * 3000);
|
|
729
|
+
return `${name}: ${String(rxBytes).padStart(8)} ${String(rxPkts).padStart(7)} 0 0 0 0 0 0 ${String(txBytes).padStart(9)} ${String(txPkts).padStart(7)} 0 0 0 0 0 0`;
|
|
730
|
+
});
|
|
731
|
+
write(vfs, "/proc/net/dev", `${devHeader}\n${devLines.join("\n")}\n`);
|
|
732
|
+
// /proc/net/route
|
|
733
|
+
const routeLines = routes.map((r) => [
|
|
734
|
+
r.device,
|
|
735
|
+
ipToHex(r.destination === "default" ? "0.0.0.0" : r.destination),
|
|
736
|
+
ipToHex(r.gateway),
|
|
737
|
+
r.flags === "UG"
|
|
738
|
+
? "0003"
|
|
739
|
+
: r.flags === "U"
|
|
740
|
+
? "0001"
|
|
741
|
+
: "0000",
|
|
742
|
+
"0", "0", "100",
|
|
743
|
+
ipToHex(r.netmask),
|
|
744
|
+
"0", "0", "0",
|
|
745
|
+
].join("\t"));
|
|
746
|
+
write(vfs, "/proc/net/route", `Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n${routeLines.join("\n")}\n`);
|
|
747
|
+
// /proc/net/arp
|
|
748
|
+
const arpLines = arpCache.map((e) => `${e.ip.padEnd(15)} 0x1 0x2 ${e.mac.padEnd(17)} * ${e.device}`);
|
|
749
|
+
write(vfs, "/proc/net/arp", `IP address HW type Flags HW address Mask Device\n${arpLines.join("\n")}\n`);
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
write(vfs, "/proc/net/dev", "Inter-| Receive | Transmit\n face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n eth0: 128628 1230 0 19 0 0 0 0 52027469 2045 0 0 0 0 0 0\n");
|
|
753
|
+
write(vfs, "/proc/net/route", "Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\neth0\t00000000\t0101A8C0\t0003\t0\t0\t100\t00000000\t0\t0\t0\neth0\t0000A8C0\t00000000\t0001\t0\t0\t100\t00FFFFFF\t0\t0\t0\n");
|
|
754
|
+
write(vfs, "/proc/net/arp", "IP address HW type Flags HW address Mask Device\n");
|
|
755
|
+
}
|
|
662
756
|
write(vfs, "/proc/net/if_inet6", "");
|
|
663
|
-
|
|
757
|
+
// /proc/net/tcp — fake listening sockets
|
|
758
|
+
const tcpListen = [
|
|
759
|
+
" 0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10000 1 0000000000000000 100 0 0 10 0",
|
|
760
|
+
" 1: 00000000:022D 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10001 1 0000000000000000 100 0 0 10 0",
|
|
761
|
+
" 2: 00000000:0A8C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10002 1 0000000000000000 100 0 0 10 0",
|
|
762
|
+
].join("\n");
|
|
763
|
+
write(vfs, "/proc/net/tcp", ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n${tcpListen}\n`);
|
|
664
764
|
write(vfs, "/proc/net/tcp6", " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
665
765
|
write(vfs, "/proc/net/udp", " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
|
|
666
|
-
write(vfs, "/proc/net/route", "Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n" +
|
|
667
|
-
"eth0\t00000000\t0101A8C0\t0003\t0\t0\t100\t00000000\t0\t0\t0\n" +
|
|
668
|
-
"eth0\t0000A8C0\t00000000\t0001\t0\t0\t100\t00FFFFFF\t0\t0\t0\n");
|
|
669
|
-
write(vfs, "/proc/net/arp", "IP address HW type Flags HW address Mask Device\n");
|
|
670
766
|
write(vfs, "/proc/net/fib_trie", "Local:\n +-- 0.0.0.0/0 3 0 5\n");
|
|
671
767
|
write(vfs, "/proc/net/unix", "Num RefCount Protocol Flags Type St Inode Path\n" +
|
|
672
768
|
"0000000000000000: 00000002 00000000 00010000 0001 01 10000 /run/dbus/system_bus_socket\n");
|
|
673
|
-
write(vfs, "/proc/net/sockstat", "sockets: used
|
|
769
|
+
write(vfs, "/proc/net/sockstat", "sockets: used 11\nTCP: inuse 3 orphan 0 tw 0 alloc 3 mem 1024\nUDP: inuse 0 mem 0\nUDPLITE: inuse 0\nRAW: inuse 0\nFRAG: inuse 0 memory 0\n");
|
|
674
770
|
// /proc/partitions — virtio block devices
|
|
675
771
|
write(vfs, "/proc/partitions", `${[
|
|
676
772
|
"major minor #blocks name",
|
|
@@ -994,61 +1090,6 @@ function bootstrapUsr(vfs) {
|
|
|
994
1090
|
"hostname", "uname", "ps", "kill", "df", "du", "curl", "wget",
|
|
995
1091
|
"nano", "diff", "uniq", "xargs", "base64",
|
|
996
1092
|
];
|
|
997
|
-
// From a real container
|
|
998
|
-
// const builtins = [
|
|
999
|
-
// // core
|
|
1000
|
-
// "sh", "bash", "dash",
|
|
1001
|
-
// "ls", "cat", "echo", "grep", "find", "sort",
|
|
1002
|
-
// "head", "tail", "cut", "tr", "sed", "awk", "mawk", "gawk",
|
|
1003
|
-
// "wc", "tee", "tar", "gzip", "gunzip", "bzip2", "xz",
|
|
1004
|
-
// "touch", "mkdir", "rm", "mv", "cp", "ln", "pwd",
|
|
1005
|
-
// "chmod", "chown", "chgrp", "env", "date", "sleep",
|
|
1006
|
-
// "id", "whoami", "hostname", "uname", "ps", "kill",
|
|
1007
|
-
// "df", "du", "dd", "stat", "file",
|
|
1008
|
-
// // net
|
|
1009
|
-
// "curl", "wget", "nc", "netcat", "ss", "ip",
|
|
1010
|
-
// // editors
|
|
1011
|
-
// "nano", "vi",
|
|
1012
|
-
// // text
|
|
1013
|
-
// "diff", "uniq", "xargs", "base64", "md5sum", "sha256sum",
|
|
1014
|
-
// "strings", "hexdump", "od", "column", "fmt", "paste",
|
|
1015
|
-
// "join", "comm", "split", "csplit", "fold", "expand",
|
|
1016
|
-
// // archive
|
|
1017
|
-
// "zip", "unzip",
|
|
1018
|
-
// // process
|
|
1019
|
-
// "top", "htop", "free", "uptime", "dmesg", "lsof",
|
|
1020
|
-
// "strace", "ltrace", "pgrep", "pkill", "nohup", "nice",
|
|
1021
|
-
// // fs
|
|
1022
|
-
// "mount", "umount", "lsblk", "fdisk", "blkid", "e2fsck",
|
|
1023
|
-
// // misc
|
|
1024
|
-
// "bc", "expr", "seq", "yes", "true", "false", "test",
|
|
1025
|
-
// "readlink", "realpath", "dirname", "basename", "mktemp",
|
|
1026
|
-
// "install", "make",
|
|
1027
|
-
// // dev tools
|
|
1028
|
-
// "gcc", "gcc-13", "g++", "g++-13", "cpp", "as", "ld",
|
|
1029
|
-
// "ar", "nm", "objdump", "objcopy", "strip", "size",
|
|
1030
|
-
// "cc", "c++", "pkg-config",
|
|
1031
|
-
// // package
|
|
1032
|
-
// "apt", "apt-get", "apt-cache", "dpkg", "dpkg-query",
|
|
1033
|
-
// "lsb_release", "add-apt-repository",
|
|
1034
|
-
// // scripting
|
|
1035
|
-
// "perl", "python3", "python3.12", "pipx",
|
|
1036
|
-
// // node/npm
|
|
1037
|
-
// "node", "npm", "npx",
|
|
1038
|
-
// // java
|
|
1039
|
-
// "java", "javac", "jar", "javadoc",
|
|
1040
|
-
// // security
|
|
1041
|
-
// "openssl", "gpg", "gpg2", "gpgv", "ssh", "ssh-keygen",
|
|
1042
|
-
// "sudo", "su", "passwd", "adduser", "useradd",
|
|
1043
|
-
// // misc system
|
|
1044
|
-
// "systemctl", "journalctl", "loginctl",
|
|
1045
|
-
// "timedatectl", "localectl",
|
|
1046
|
-
// "lshw", "lscpu", "lsusb", "lspci",
|
|
1047
|
-
// // text proc
|
|
1048
|
-
// "jq", "xmllint", "pandoc",
|
|
1049
|
-
// // multimedia
|
|
1050
|
-
// "ffmpeg",
|
|
1051
|
-
// ];
|
|
1052
1093
|
for (const bin of builtins) {
|
|
1053
1094
|
ensureFile(vfs, `/usr/bin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
|
|
1054
1095
|
}
|
|
@@ -1547,7 +1588,7 @@ export function getStaticRootfsSnapshot(hostname, props) {
|
|
|
1547
1588
|
* @param shellStartTime Unix ms of shell creation (for uptime).
|
|
1548
1589
|
* @param sessions Active sessions (for /proc/<pid> population).
|
|
1549
1590
|
*/
|
|
1550
|
-
export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime, sessions = []) {
|
|
1591
|
+
export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime, sessions = [], network) {
|
|
1551
1592
|
const snapshot = getStaticRootfsSnapshot(hostname, props);
|
|
1552
1593
|
const hasRestoredData = vfs.getMode() === "fs" && vfs.exists("/home");
|
|
1553
1594
|
if (hasRestoredData) {
|
|
@@ -1557,7 +1598,7 @@ export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime
|
|
|
1557
1598
|
vfs.importRootTree(decodeVfs(snapshot));
|
|
1558
1599
|
}
|
|
1559
1600
|
bootstrapRoot(vfs);
|
|
1560
|
-
refreshProc(vfs, props, hostname, shellStartTime, sessions);
|
|
1601
|
+
refreshProc(vfs, props, hostname, shellStartTime, sessions, network);
|
|
1561
1602
|
syncEtcPasswd(vfs, users);
|
|
1562
1603
|
}
|
|
1563
1604
|
// ─── optional live engine ─────────────────────────────────────────────────────
|
|
@@ -1572,13 +1613,13 @@ export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime
|
|
|
1572
1613
|
* setInterval(() => engine.tick(shell.listActiveSessions()), 5000);
|
|
1573
1614
|
* ```
|
|
1574
1615
|
*/
|
|
1575
|
-
export function createLinuxRootfsEngine(vfs, props, hostname, startTime) {
|
|
1616
|
+
export function createLinuxRootfsEngine(vfs, props, hostname, startTime, network) {
|
|
1576
1617
|
return {
|
|
1577
1618
|
boot(users, sessions = []) {
|
|
1578
|
-
bootstrapLinuxRootfs(vfs, users, hostname, props, startTime, sessions);
|
|
1619
|
+
bootstrapLinuxRootfs(vfs, users, hostname, props, startTime, sessions, network);
|
|
1579
1620
|
},
|
|
1580
1621
|
tick(sessions = []) {
|
|
1581
|
-
refreshProc(vfs, props, hostname, startTime, sessions);
|
|
1622
|
+
refreshProc(vfs, props, hostname, startTime, sessions, network);
|
|
1582
1623
|
},
|
|
1583
1624
|
};
|
|
1584
1625
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ShellProperties } from "../VirtualShell";
|
|
2
|
+
/** System info fields used to render neofetch output. */
|
|
2
3
|
export interface NeofetchInfo {
|
|
3
4
|
user: string;
|
|
4
5
|
host: string;
|
|
@@ -15,4 +16,5 @@ export interface NeofetchInfo {
|
|
|
15
16
|
memoryUsedMiB?: number;
|
|
16
17
|
memoryTotalMiB?: number;
|
|
17
18
|
}
|
|
19
|
+
/** Builds the full neofetch ASCII art system-info string. */
|
|
18
20
|
export declare function buildNeofetchOutput(info: NeofetchInfo): string;
|
package/dist/modules/neofetch.js
CHANGED
|
@@ -133,7 +133,7 @@ function countDpkgPackages() {
|
|
|
133
133
|
const matches = data.match(/^Package:\s+/gm);
|
|
134
134
|
return matches?.length ?? 0;
|
|
135
135
|
}
|
|
136
|
-
catch { }
|
|
136
|
+
catch { /* dpkg status file may not exist */ }
|
|
137
137
|
}
|
|
138
138
|
return undefined;
|
|
139
139
|
}
|
|
@@ -148,7 +148,7 @@ function countSnapPackages() {
|
|
|
148
148
|
const count = entries.filter((entry) => entry.isDirectory()).length;
|
|
149
149
|
return count;
|
|
150
150
|
}
|
|
151
|
-
catch { }
|
|
151
|
+
catch { /* snap directory may not be readable */ }
|
|
152
152
|
}
|
|
153
153
|
return undefined;
|
|
154
154
|
}
|
|
@@ -216,6 +216,7 @@ function resolveDefaults(info) {
|
|
|
216
216
|
memoryTotalMiB: info.memoryTotalMiB ?? toMiB(totalMem),
|
|
217
217
|
};
|
|
218
218
|
}
|
|
219
|
+
/** Builds the full neofetch ASCII art system-info string. */
|
|
219
220
|
export function buildNeofetchOutput(info) {
|
|
220
221
|
const fields = resolveDefaults(info);
|
|
221
222
|
const uptime = formatUptime(fields.uptimeSeconds);
|