@earendil-works/gondolin 0.0.1 → 0.1.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/LICENSE +201 -0
- package/README.md +29 -15
- package/dist/src/sandbox-controller.d.ts.map +1 -1
- package/dist/src/sandbox-controller.js +5 -0
- package/dist/src/sandbox-controller.js.map +1 -1
- package/package.json +2 -2
- package/dist/bash.js +0 -82
- package/dist/bin/bad-bash.js +0 -111
- package/dist/bin/bash.js +0 -85
- package/dist/bin/eregion.js +0 -41
- package/dist/bin/exec.js +0 -212
- package/dist/bin/ws-server.js +0 -147
- package/dist/exec.js +0 -209
- package/dist/network-stack.js +0 -658
- package/dist/policy.js +0 -2
- package/dist/qemu-net.js +0 -756
- package/dist/sandbox-controller.js +0 -165
- package/dist/sandbox-ws-server.js +0 -729
- package/dist/virtio-protocol.js +0 -96
- package/dist/vm.js +0 -523
- package/dist/ws-protocol.js +0 -28
- package/dist/ws-server.js +0 -136
- package/dist/ws-test.js +0 -65
package/dist/bin/exec.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.runExec = runExec;
|
|
7
|
-
const net_1 = __importDefault(require("net"));
|
|
8
|
-
const virtio_protocol_1 = require("../src/virtio-protocol");
|
|
9
|
-
function parseArgs(argv) {
|
|
10
|
-
const args = { commands: [] };
|
|
11
|
-
let current = null;
|
|
12
|
-
let nextId = 1;
|
|
13
|
-
const fail = (message) => {
|
|
14
|
-
console.error(message);
|
|
15
|
-
usage();
|
|
16
|
-
process.exit(1);
|
|
17
|
-
};
|
|
18
|
-
const parseId = (value) => {
|
|
19
|
-
const id = Number(value);
|
|
20
|
-
if (!Number.isFinite(id))
|
|
21
|
-
fail("--id must be a number");
|
|
22
|
-
if (id >= nextId)
|
|
23
|
-
nextId = id + 1;
|
|
24
|
-
return id;
|
|
25
|
-
};
|
|
26
|
-
const separatorIndex = argv.indexOf("--");
|
|
27
|
-
if (separatorIndex !== -1) {
|
|
28
|
-
const optionArgs = argv.slice(0, separatorIndex);
|
|
29
|
-
const commandArgs = argv.slice(separatorIndex + 1);
|
|
30
|
-
if (commandArgs.length === 0)
|
|
31
|
-
fail("missing command after --");
|
|
32
|
-
current = {
|
|
33
|
-
cmd: commandArgs[0],
|
|
34
|
-
argv: commandArgs.slice(1),
|
|
35
|
-
env: [],
|
|
36
|
-
id: nextId++,
|
|
37
|
-
};
|
|
38
|
-
args.commands.push(current);
|
|
39
|
-
for (let i = 0; i < optionArgs.length; i += 1) {
|
|
40
|
-
const arg = optionArgs[i];
|
|
41
|
-
switch (arg) {
|
|
42
|
-
case "--sock":
|
|
43
|
-
args.sock = optionArgs[++i];
|
|
44
|
-
break;
|
|
45
|
-
case "--env":
|
|
46
|
-
current.env.push(optionArgs[++i]);
|
|
47
|
-
break;
|
|
48
|
-
case "--cwd":
|
|
49
|
-
current.cwd = optionArgs[++i];
|
|
50
|
-
break;
|
|
51
|
-
case "--id":
|
|
52
|
-
current.id = parseId(optionArgs[++i]);
|
|
53
|
-
break;
|
|
54
|
-
case "--help":
|
|
55
|
-
case "-h":
|
|
56
|
-
usage();
|
|
57
|
-
process.exit(0);
|
|
58
|
-
default:
|
|
59
|
-
fail(`Unknown argument: ${arg}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return args;
|
|
63
|
-
}
|
|
64
|
-
const requireCurrent = (flag) => {
|
|
65
|
-
if (!current)
|
|
66
|
-
fail(`${flag} requires --cmd`);
|
|
67
|
-
return current;
|
|
68
|
-
};
|
|
69
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
70
|
-
const arg = argv[i];
|
|
71
|
-
switch (arg) {
|
|
72
|
-
case "--sock":
|
|
73
|
-
args.sock = argv[++i];
|
|
74
|
-
break;
|
|
75
|
-
case "--cmd":
|
|
76
|
-
current = { cmd: argv[++i], argv: [], env: [], id: nextId++ };
|
|
77
|
-
args.commands.push(current);
|
|
78
|
-
break;
|
|
79
|
-
case "--arg": {
|
|
80
|
-
const command = requireCurrent("--arg");
|
|
81
|
-
command.argv.push(argv[++i]);
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
case "--env": {
|
|
85
|
-
const command = requireCurrent("--env");
|
|
86
|
-
command.env.push(argv[++i]);
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
case "--cwd": {
|
|
90
|
-
const command = requireCurrent("--cwd");
|
|
91
|
-
command.cwd = argv[++i];
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
case "--id": {
|
|
95
|
-
const command = requireCurrent("--id");
|
|
96
|
-
command.id = parseId(argv[++i]);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case "--help":
|
|
100
|
-
case "-h":
|
|
101
|
-
usage();
|
|
102
|
-
process.exit(0);
|
|
103
|
-
default:
|
|
104
|
-
fail(`Unknown argument: ${arg}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return args;
|
|
108
|
-
}
|
|
109
|
-
function usage() {
|
|
110
|
-
console.log("Usage:");
|
|
111
|
-
console.log(" gondolin exec --sock PATH -- CMD [ARGS...]");
|
|
112
|
-
console.log(" gondolin exec --sock PATH --cmd CMD [--arg ARG] [--env KEY=VALUE] [--cwd PATH] [--cmd CMD ...]");
|
|
113
|
-
console.log("Use -- to pass a command and its arguments directly.");
|
|
114
|
-
console.log("Arguments apply to the most recent --cmd.");
|
|
115
|
-
}
|
|
116
|
-
function buildCommandPayload(command) {
|
|
117
|
-
const payload = {
|
|
118
|
-
cmd: command.cmd,
|
|
119
|
-
};
|
|
120
|
-
if (command.argv.length > 0)
|
|
121
|
-
payload.argv = command.argv;
|
|
122
|
-
if (command.env.length > 0)
|
|
123
|
-
payload.env = command.env;
|
|
124
|
-
if (command.cwd)
|
|
125
|
-
payload.cwd = command.cwd;
|
|
126
|
-
return payload;
|
|
127
|
-
}
|
|
128
|
-
function runExec(argv = process.argv.slice(2)) {
|
|
129
|
-
const args = parseArgs(argv);
|
|
130
|
-
if (!args.sock || args.commands.length === 0) {
|
|
131
|
-
usage();
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
const socket = net_1.default.createConnection({ path: args.sock });
|
|
135
|
-
const reader = new virtio_protocol_1.FrameReader();
|
|
136
|
-
let currentIndex = 0;
|
|
137
|
-
let inflightId = null;
|
|
138
|
-
let exitCode = 0;
|
|
139
|
-
let closing = false;
|
|
140
|
-
const sendNext = () => {
|
|
141
|
-
const command = args.commands[currentIndex];
|
|
142
|
-
inflightId = command.id;
|
|
143
|
-
const payload = buildCommandPayload(command);
|
|
144
|
-
const message = (0, virtio_protocol_1.buildExecRequest)(command.id, payload);
|
|
145
|
-
socket.write((0, virtio_protocol_1.encodeFrame)(message));
|
|
146
|
-
};
|
|
147
|
-
const finish = (code) => {
|
|
148
|
-
if (code !== undefined && exitCode === 0)
|
|
149
|
-
exitCode = code;
|
|
150
|
-
if (closing)
|
|
151
|
-
return;
|
|
152
|
-
closing = true;
|
|
153
|
-
socket.end();
|
|
154
|
-
};
|
|
155
|
-
socket.on("connect", () => {
|
|
156
|
-
console.log(`connected to ${args.sock}`);
|
|
157
|
-
sendNext();
|
|
158
|
-
});
|
|
159
|
-
socket.on("data", (chunk) => {
|
|
160
|
-
reader.push(chunk, (frame) => {
|
|
161
|
-
const message = (0, virtio_protocol_1.decodeMessage)(frame);
|
|
162
|
-
if (message.t === "exec_output") {
|
|
163
|
-
const data = message.p.data;
|
|
164
|
-
if (message.p.stream === "stdout") {
|
|
165
|
-
process.stdout.write(data);
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
process.stderr.write(data);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
else if (message.t === "exec_response") {
|
|
172
|
-
if (inflightId !== null && message.id !== inflightId) {
|
|
173
|
-
console.error(`unexpected response id ${message.id} (expected ${inflightId})`);
|
|
174
|
-
finish(1);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const code = message.p.exit_code ?? 1;
|
|
178
|
-
const signal = message.p.signal;
|
|
179
|
-
if (signal !== undefined) {
|
|
180
|
-
console.error(`process exited due to signal ${signal}`);
|
|
181
|
-
}
|
|
182
|
-
if (code !== 0 && exitCode === 0)
|
|
183
|
-
exitCode = code;
|
|
184
|
-
currentIndex += 1;
|
|
185
|
-
if (currentIndex < args.commands.length) {
|
|
186
|
-
sendNext();
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
finish();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
else if (message.t === "error") {
|
|
193
|
-
console.error(`error ${message.p.code}: ${message.p.message}`);
|
|
194
|
-
finish(1);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
socket.on("error", (err) => {
|
|
199
|
-
console.error(`socket error: ${err.message}`);
|
|
200
|
-
finish(1);
|
|
201
|
-
});
|
|
202
|
-
socket.on("end", () => {
|
|
203
|
-
if (!closing && exitCode === 0)
|
|
204
|
-
exitCode = 1;
|
|
205
|
-
});
|
|
206
|
-
socket.on("close", () => {
|
|
207
|
-
process.exit(exitCode);
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
if (require.main === module) {
|
|
211
|
-
runExec();
|
|
212
|
-
}
|
package/dist/bin/ws-server.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runWsServer = runWsServer;
|
|
4
|
-
const sandbox_ws_server_1 = require("../src/sandbox-ws-server");
|
|
5
|
-
function parseArgs(argv) {
|
|
6
|
-
const args = {};
|
|
7
|
-
const fail = (message) => {
|
|
8
|
-
console.error(message);
|
|
9
|
-
usage();
|
|
10
|
-
process.exit(1);
|
|
11
|
-
};
|
|
12
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
13
|
-
const arg = argv[i];
|
|
14
|
-
if (arg === "--") {
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
switch (arg) {
|
|
18
|
-
case "--host":
|
|
19
|
-
args.host = argv[++i] ?? args.host;
|
|
20
|
-
break;
|
|
21
|
-
case "--port":
|
|
22
|
-
args.port = Number(argv[++i]);
|
|
23
|
-
if (!Number.isFinite(args.port))
|
|
24
|
-
fail("--port must be a number");
|
|
25
|
-
break;
|
|
26
|
-
case "--qemu":
|
|
27
|
-
args.qemuPath = argv[++i];
|
|
28
|
-
break;
|
|
29
|
-
case "--kernel":
|
|
30
|
-
args.kernelPath = argv[++i];
|
|
31
|
-
break;
|
|
32
|
-
case "--initrd":
|
|
33
|
-
args.initrdPath = argv[++i];
|
|
34
|
-
break;
|
|
35
|
-
case "--memory":
|
|
36
|
-
args.memory = argv[++i];
|
|
37
|
-
break;
|
|
38
|
-
case "--cpus":
|
|
39
|
-
args.cpus = Number(argv[++i]);
|
|
40
|
-
if (!Number.isFinite(args.cpus))
|
|
41
|
-
fail("--cpus must be a number");
|
|
42
|
-
break;
|
|
43
|
-
case "--virtio-sock":
|
|
44
|
-
args.virtioSocketPath = argv[++i];
|
|
45
|
-
break;
|
|
46
|
-
case "--net-sock":
|
|
47
|
-
args.netSocketPath = argv[++i];
|
|
48
|
-
break;
|
|
49
|
-
case "--virtio-fs-sock":
|
|
50
|
-
args.virtioFsSocketPath = argv[++i];
|
|
51
|
-
break;
|
|
52
|
-
case "--net-mac":
|
|
53
|
-
args.netMac = argv[++i];
|
|
54
|
-
break;
|
|
55
|
-
case "--no-net":
|
|
56
|
-
args.netEnabled = false;
|
|
57
|
-
break;
|
|
58
|
-
case "--net-debug":
|
|
59
|
-
args.netDebug = true;
|
|
60
|
-
break;
|
|
61
|
-
case "--machine":
|
|
62
|
-
args.machineType = argv[++i];
|
|
63
|
-
break;
|
|
64
|
-
case "--accel":
|
|
65
|
-
args.accel = argv[++i];
|
|
66
|
-
break;
|
|
67
|
-
case "--cpu":
|
|
68
|
-
args.cpu = argv[++i];
|
|
69
|
-
break;
|
|
70
|
-
case "--console":
|
|
71
|
-
args.console = argv[++i] === "none" ? "none" : "stdio";
|
|
72
|
-
break;
|
|
73
|
-
case "--token":
|
|
74
|
-
args.token = argv[++i];
|
|
75
|
-
break;
|
|
76
|
-
case "--no-restart":
|
|
77
|
-
args.autoRestart = false;
|
|
78
|
-
break;
|
|
79
|
-
case "--help":
|
|
80
|
-
case "-h":
|
|
81
|
-
usage();
|
|
82
|
-
process.exit(0);
|
|
83
|
-
default:
|
|
84
|
-
fail(`Unknown argument: ${arg}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return args;
|
|
88
|
-
}
|
|
89
|
-
function usage() {
|
|
90
|
-
const defaults = (0, sandbox_ws_server_1.resolveSandboxWsServerOptions)();
|
|
91
|
-
console.log("Usage: gondolin ws-server [options]");
|
|
92
|
-
console.log("Options:");
|
|
93
|
-
console.log(` --host HOST Host to bind (default ${defaults.host})`);
|
|
94
|
-
console.log(` --port PORT Port to bind (default ${defaults.port})`);
|
|
95
|
-
console.log(` --qemu PATH QEMU binary (default ${defaults.qemuPath})`);
|
|
96
|
-
console.log(" --kernel PATH Kernel path");
|
|
97
|
-
console.log(" --initrd PATH Initrd path");
|
|
98
|
-
console.log(` --memory SIZE Memory size (default ${defaults.memory})`);
|
|
99
|
-
console.log(` --cpus N vCPU count (default ${defaults.cpus})`);
|
|
100
|
-
console.log(" --virtio-sock PATH Virtio serial socket path");
|
|
101
|
-
console.log(" --virtio-fs-sock PATH Virtio filesystem socket path");
|
|
102
|
-
console.log(" --net-sock PATH QEMU net socket path");
|
|
103
|
-
console.log(" --net-mac MAC MAC address for virtio-net");
|
|
104
|
-
console.log(" --no-net Disable QEMU net backend");
|
|
105
|
-
console.log(" --net-debug Enable net backend debug logging");
|
|
106
|
-
console.log(" (or set GONDOLIN_DEBUG=net)");
|
|
107
|
-
console.log(" --machine TYPE Override QEMU machine type");
|
|
108
|
-
console.log(" --accel TYPE Override QEMU accel (kvm/hvf/tcg)");
|
|
109
|
-
console.log(" --cpu TYPE Override QEMU CPU type");
|
|
110
|
-
console.log(" --console stdio|none Console output");
|
|
111
|
-
console.log(" --token TOKEN Require token in Authorization header");
|
|
112
|
-
console.log(" --no-restart Disable auto restart on exit");
|
|
113
|
-
}
|
|
114
|
-
function formatLog(message) {
|
|
115
|
-
if (message.endsWith("\n"))
|
|
116
|
-
return message;
|
|
117
|
-
return `${message}\n`;
|
|
118
|
-
}
|
|
119
|
-
async function runWsServer(argv = process.argv.slice(2)) {
|
|
120
|
-
const args = parseArgs(argv);
|
|
121
|
-
const server = new sandbox_ws_server_1.SandboxWsServer(args);
|
|
122
|
-
server.on("log", (message) => {
|
|
123
|
-
process.stdout.write(formatLog(message));
|
|
124
|
-
});
|
|
125
|
-
server.on("error", (err) => {
|
|
126
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
127
|
-
process.stdout.write(formatLog(message));
|
|
128
|
-
});
|
|
129
|
-
const address = await server.start();
|
|
130
|
-
console.log(`WebSocket server listening on ${address.url}`);
|
|
131
|
-
const shutdown = async () => {
|
|
132
|
-
await server.stop();
|
|
133
|
-
process.exit(0);
|
|
134
|
-
};
|
|
135
|
-
process.on("SIGINT", () => {
|
|
136
|
-
void shutdown();
|
|
137
|
-
});
|
|
138
|
-
process.on("SIGTERM", () => {
|
|
139
|
-
void shutdown();
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
if (require.main === module) {
|
|
143
|
-
runWsServer().catch((err) => {
|
|
144
|
-
console.error(err.message);
|
|
145
|
-
process.exit(1);
|
|
146
|
-
});
|
|
147
|
-
}
|
package/dist/exec.js
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const net_1 = __importDefault(require("net"));
|
|
7
|
-
const virtio_protocol_1 = require("./virtio-protocol");
|
|
8
|
-
function parseArgs(argv) {
|
|
9
|
-
const args = { commands: [] };
|
|
10
|
-
let current = null;
|
|
11
|
-
let nextId = 1;
|
|
12
|
-
const fail = (message) => {
|
|
13
|
-
console.error(message);
|
|
14
|
-
usage();
|
|
15
|
-
process.exit(1);
|
|
16
|
-
};
|
|
17
|
-
const parseId = (value) => {
|
|
18
|
-
const id = Number(value);
|
|
19
|
-
if (!Number.isFinite(id))
|
|
20
|
-
fail("--id must be a number");
|
|
21
|
-
if (id >= nextId)
|
|
22
|
-
nextId = id + 1;
|
|
23
|
-
return id;
|
|
24
|
-
};
|
|
25
|
-
const separatorIndex = argv.indexOf("--");
|
|
26
|
-
if (separatorIndex !== -1) {
|
|
27
|
-
const optionArgs = argv.slice(0, separatorIndex);
|
|
28
|
-
const commandArgs = argv.slice(separatorIndex + 1);
|
|
29
|
-
if (commandArgs.length === 0)
|
|
30
|
-
fail("missing command after --");
|
|
31
|
-
current = {
|
|
32
|
-
cmd: commandArgs[0],
|
|
33
|
-
argv: commandArgs.slice(1),
|
|
34
|
-
env: [],
|
|
35
|
-
id: nextId++,
|
|
36
|
-
};
|
|
37
|
-
args.commands.push(current);
|
|
38
|
-
for (let i = 0; i < optionArgs.length; i += 1) {
|
|
39
|
-
const arg = optionArgs[i];
|
|
40
|
-
switch (arg) {
|
|
41
|
-
case "--sock":
|
|
42
|
-
args.sock = optionArgs[++i];
|
|
43
|
-
break;
|
|
44
|
-
case "--env":
|
|
45
|
-
current.env.push(optionArgs[++i]);
|
|
46
|
-
break;
|
|
47
|
-
case "--cwd":
|
|
48
|
-
current.cwd = optionArgs[++i];
|
|
49
|
-
break;
|
|
50
|
-
case "--id":
|
|
51
|
-
current.id = parseId(optionArgs[++i]);
|
|
52
|
-
break;
|
|
53
|
-
case "--help":
|
|
54
|
-
case "-h":
|
|
55
|
-
usage();
|
|
56
|
-
process.exit(0);
|
|
57
|
-
default:
|
|
58
|
-
fail(`Unknown argument: ${arg}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return args;
|
|
62
|
-
}
|
|
63
|
-
const requireCurrent = (flag) => {
|
|
64
|
-
if (!current)
|
|
65
|
-
fail(`${flag} requires --cmd`);
|
|
66
|
-
return current;
|
|
67
|
-
};
|
|
68
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
69
|
-
const arg = argv[i];
|
|
70
|
-
switch (arg) {
|
|
71
|
-
case "--sock":
|
|
72
|
-
args.sock = argv[++i];
|
|
73
|
-
break;
|
|
74
|
-
case "--cmd":
|
|
75
|
-
current = { cmd: argv[++i], argv: [], env: [], id: nextId++ };
|
|
76
|
-
args.commands.push(current);
|
|
77
|
-
break;
|
|
78
|
-
case "--arg": {
|
|
79
|
-
const command = requireCurrent("--arg");
|
|
80
|
-
command.argv.push(argv[++i]);
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
case "--env": {
|
|
84
|
-
const command = requireCurrent("--env");
|
|
85
|
-
command.env.push(argv[++i]);
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
case "--cwd": {
|
|
89
|
-
const command = requireCurrent("--cwd");
|
|
90
|
-
command.cwd = argv[++i];
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
case "--id": {
|
|
94
|
-
const command = requireCurrent("--id");
|
|
95
|
-
command.id = parseId(argv[++i]);
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
case "--help":
|
|
99
|
-
case "-h":
|
|
100
|
-
usage();
|
|
101
|
-
process.exit(0);
|
|
102
|
-
default:
|
|
103
|
-
fail(`Unknown argument: ${arg}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return args;
|
|
107
|
-
}
|
|
108
|
-
function usage() {
|
|
109
|
-
console.log("Usage:");
|
|
110
|
-
console.log(" node dist/exec.js --sock PATH -- CMD [ARGS...]");
|
|
111
|
-
console.log(" node dist/exec.js --sock PATH --cmd CMD [--arg ARG] [--env KEY=VALUE] [--cwd PATH] [--cmd CMD ...]");
|
|
112
|
-
console.log("Use -- to pass a command and its arguments directly.");
|
|
113
|
-
console.log("Arguments apply to the most recent --cmd.");
|
|
114
|
-
}
|
|
115
|
-
function buildCommandPayload(command) {
|
|
116
|
-
const payload = {
|
|
117
|
-
cmd: command.cmd,
|
|
118
|
-
};
|
|
119
|
-
if (command.argv.length > 0)
|
|
120
|
-
payload.argv = command.argv;
|
|
121
|
-
if (command.env.length > 0)
|
|
122
|
-
payload.env = command.env;
|
|
123
|
-
if (command.cwd)
|
|
124
|
-
payload.cwd = command.cwd;
|
|
125
|
-
return payload;
|
|
126
|
-
}
|
|
127
|
-
function main() {
|
|
128
|
-
const args = parseArgs(process.argv.slice(2));
|
|
129
|
-
if (!args.sock || args.commands.length === 0) {
|
|
130
|
-
usage();
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
const socket = net_1.default.createConnection({ path: args.sock });
|
|
134
|
-
const reader = new virtio_protocol_1.FrameReader();
|
|
135
|
-
let currentIndex = 0;
|
|
136
|
-
let inflightId = null;
|
|
137
|
-
let exitCode = 0;
|
|
138
|
-
let closing = false;
|
|
139
|
-
const sendNext = () => {
|
|
140
|
-
const command = args.commands[currentIndex];
|
|
141
|
-
inflightId = command.id;
|
|
142
|
-
const payload = buildCommandPayload(command);
|
|
143
|
-
const message = (0, virtio_protocol_1.buildExecRequest)(command.id, payload);
|
|
144
|
-
socket.write((0, virtio_protocol_1.encodeFrame)(message));
|
|
145
|
-
};
|
|
146
|
-
const finish = (code) => {
|
|
147
|
-
if (code !== undefined && exitCode === 0)
|
|
148
|
-
exitCode = code;
|
|
149
|
-
if (closing)
|
|
150
|
-
return;
|
|
151
|
-
closing = true;
|
|
152
|
-
socket.end();
|
|
153
|
-
};
|
|
154
|
-
socket.on("connect", () => {
|
|
155
|
-
console.log(`connected to ${args.sock}`);
|
|
156
|
-
sendNext();
|
|
157
|
-
});
|
|
158
|
-
socket.on("data", (chunk) => {
|
|
159
|
-
reader.push(chunk, (frame) => {
|
|
160
|
-
const message = (0, virtio_protocol_1.decodeMessage)(frame);
|
|
161
|
-
if (message.t === "exec_output") {
|
|
162
|
-
const data = message.p.data;
|
|
163
|
-
if (message.p.stream === "stdout") {
|
|
164
|
-
process.stdout.write(data);
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
process.stderr.write(data);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
else if (message.t === "exec_response") {
|
|
171
|
-
if (inflightId !== null && message.id !== inflightId) {
|
|
172
|
-
console.error(`unexpected response id ${message.id} (expected ${inflightId})`);
|
|
173
|
-
finish(1);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
const code = message.p.exit_code ?? 1;
|
|
177
|
-
const signal = message.p.signal;
|
|
178
|
-
if (signal !== undefined) {
|
|
179
|
-
console.error(`process exited due to signal ${signal}`);
|
|
180
|
-
}
|
|
181
|
-
if (code !== 0 && exitCode === 0)
|
|
182
|
-
exitCode = code;
|
|
183
|
-
currentIndex += 1;
|
|
184
|
-
if (currentIndex < args.commands.length) {
|
|
185
|
-
sendNext();
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
finish();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
else if (message.t === "error") {
|
|
192
|
-
console.error(`error ${message.p.code}: ${message.p.message}`);
|
|
193
|
-
finish(1);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
socket.on("error", (err) => {
|
|
198
|
-
console.error(`socket error: ${err.message}`);
|
|
199
|
-
finish(1);
|
|
200
|
-
});
|
|
201
|
-
socket.on("end", () => {
|
|
202
|
-
if (!closing && exitCode === 0)
|
|
203
|
-
exitCode = 1;
|
|
204
|
-
});
|
|
205
|
-
socket.on("close", () => {
|
|
206
|
-
process.exit(exitCode);
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
main();
|