-
Notifications
You must be signed in to change notification settings - Fork 0
/
RWBG.js
124 lines (121 loc) · 3.92 KB
/
RWBG.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const fs = require("fs");
const path = require("path");
const { spawn } = require("child_process");
let RWBG_ESA = spawn("./RWBG-ESA", ["--daemon"], { stdio: ["pipe", "inherit", "inherit"] });
let RWBG_RW = spawn("./RWBG-RW", ["--daemon"], { stdio: ["pipe", "inherit", "inherit"] });
process.on("exit", function() {
RWBG_ESA.kill();
RWBG_RW.kill();
});
let queue_RWBG_max_active = 3;
let queue_RWBG_running = 0;
let queue_RWBG_queue = [];
function queue_RWBG(res_RWBG) {
if(queue_RWBG_queue.length > 0 || queue_RWBG_running >= queue_RWBG_max_active)
queue_RWBG_queue.push(res_RWBG);
else res_RWBG();
}
function dequeue_RWBG() {
queue_RWBG_running--;
let res_RWBG = queue_RWBG_queue.shift();
if(res_RWBG !== undefined) res_RWBG();
}
module.exports = {
//{{{ get_RWBG(req, res, authorized)
get_RWBG: function(req, res, authorized) {
if(req.url.startsWith("/RWBG/")) {
let desc = path.basename(req.url).replace(/\.png$/, "");
if(function() {
if(!/^\dx\d \d,\d /.test(desc)) return false;
for(monitor of desc.replace(/^\dx\d \d,\d /, "").split(" ")) {
if(!/^\d,\d@\d{1,5},\d{1,5}:\d{1,4}x\d{1,4}$/.test(monitor)) return false;
}
return true;
}()) {
let res_RWBG = function() {
let dequeued = req.url.startsWith("/RWBG/ESA/");
if(!dequeued) queue_RWBG_running++;
// use this so that RWBG has no chance of trying to write to some random process's stdout if PID were reassigned
let can_exit_now = false;
let exit_now = false;
// have to cat -> cat so that inner's stdout is a pipe RWBG can print to (node uses sockets)
// cat | cat makes zombie processes (not killed from closing pipes), process substitution prevents that
let RWBG_sub = spawn("cat <(sh -c \"printf \\\"\\$\\$\\\\n\\\"; exec cat\")", { shell: true, timeout: 120000 });
RWBG_sub.stdout.once("data", function(pid) {
let empty = true;
let buffer = [];
let pause = false;
let end = false;
let data = function() {
while(true) {
if(exit_now) {
if(RWBG_sub.exitCode === null) RWBG_sub.kill();
break;
}
can_exit_now = true;
if(empty) {
res.writeHead(200, { "Content-Type": "image/png" });
empty = false;
}
if(pause) break;
let chunk = buffer.shift();
if(chunk === undefined) {
if(end) res.end();
break;
}
if(!res.write(chunk)) { pause = true; break; }
}
};
RWBG_sub.stdout.on("data", function(chunk) { if(!exit_now) { buffer.push(chunk); } data(); });
// can't pause input, it won't pipe buffer RWBG or something and causes problems
// use a buffer instead
// killing is fine, it will get SIGPIPEd
res.on("drain", function() { pause = false; data(); });
RWBG_sub.stdout.on("end", function() {
if(empty) {
res.writeHead(400);
res.end("This monitor configuration has no solutions (or there was a server issue)");
}
else {
if(buffer.length === 0) res.end();
else end = true;
}
if(!dequeued) {
dequeued = true;
dequeue_RWBG();
}
});
if(req.url.startsWith("/RWBG/ESA/")) RWBG_ESA.stdin.write(pid.toString().slice(0, -1) + " " + desc + "\n");
else RWBG_RW.stdin.write(pid.toString().slice(0, -1) + " " + desc + "\n");
});
req.removeAllListeners("close");
req.on("close", function() {
if(can_exit_now && RWBG_sub.exitCode === null) RWBG_sub.kill();
exit_now = true;
if(!dequeued) {
dequeued = true;
dequeue_RWBG();
}
});
};
if(req.url.startsWith("/RWBG/ESA/")) res_RWBG();
else {
queue_RWBG(res_RWBG);
req.on("close", function() {
let i = queue_RWBG_queue.indexOf(res_RWBG);
if(i !== -1) {
queue_RWBG_queue.splice(i, 1);
res.end();
}
});
}
}
else {
res.writeHead(400);
res.end("This monitor configuration is invalid or too large");
}
return true;
}
}
//}}}
};