Mercurial > hg > rlgwebd
comparison rlgwebd @ 210:b04313038a0b
Changes for compatibility with recent versions of NodeJS.
The pty.js module is replaced with node-pty, now-mandatory callbacks
are added to various fs functions, and deprecated Buffer() calls are
replaced with Buffer.from() or Buffer.alloc().
author | John "Elwin" Edwards |
---|---|
date | Sun, 25 Aug 2019 21:27:31 -0400 |
parents | f06f2d1a5035 |
children | e6af951def94 |
comparison
equal
deleted
inserted
replaced
209:2667aaad8e08 | 210:b04313038a0b |
---|---|
8 var fs = require('fs'); | 8 var fs = require('fs'); |
9 var events = require('events'); | 9 var events = require('events'); |
10 var child_process = require('child_process'); | 10 var child_process = require('child_process'); |
11 // Dependencies | 11 // Dependencies |
12 var posix = require("posix"); | 12 var posix = require("posix"); |
13 var pty = require("pty.js"); | 13 var pty = require("node-pty"); |
14 var WebSocketServer = require("websocket").server; | 14 var WebSocketServer = require("websocket").server; |
15 | 15 |
16 /* Default options */ | 16 /* Default options */ |
17 var rlgwebd_options = { | 17 var rlgwebd_options = { |
18 control_socket: "/var/run/rlgwebd/rlgwebd.sock", | 18 control_socket: "/var/run/rlgwebd/rlgwebd.sock", |
42 if ("domain_name" in rlgwebd_options && "keyfile" in rlgwebd_options && | 42 if ("domain_name" in rlgwebd_options && "keyfile" in rlgwebd_options && |
43 "certfile" in rlgwebd_options) | 43 "certfile" in rlgwebd_options) |
44 rlgwebd_options["use_https"] = true; | 44 rlgwebd_options["use_https"] = true; |
45 | 45 |
46 var clearbufs = [ | 46 var clearbufs = [ |
47 new Buffer([27, 91, 72, 27, 91, 50, 74]), // xterm: CSI H CSI 2J | 47 Buffer.from([27, 91, 72, 27, 91, 50, 74]), // xterm: CSI H CSI 2J |
48 new Buffer([27, 91, 72, 27, 91, 74]) // screen: CSI H CSI J | 48 Buffer.from([27, 91, 72, 27, 91, 74]) // screen: CSI H CSI J |
49 ]; | 49 ]; |
50 | 50 |
51 /* Data on the games available. */ | 51 /* Data on the games available. */ |
52 var games = { | 52 var games = { |
53 "rogue3": { | 53 "rogue3": { |
107 events.EventEmitter.call(this); | 107 events.EventEmitter.call(this); |
108 /* Array of watching WebSockets. */ | 108 /* Array of watching WebSockets. */ |
109 this.watchers = []; | 109 this.watchers = []; |
110 /* replaybuf holds the output since the last screen clear, so watchers can | 110 /* replaybuf holds the output since the last screen clear, so watchers can |
111 * begin with a complete screen. replaylen is the number of bytes stored. */ | 111 * begin with a complete screen. replaylen is the number of bytes stored. */ |
112 this.replaybuf = new Buffer(1024); | 112 this.replaybuf = Buffer.alloc(1024); |
113 this.replaylen = 0; | 113 this.replaylen = 0; |
114 /* Time of last activity. */ | 114 /* Time of last activity. */ |
115 this.lasttime = new Date(); | 115 this.lasttime = new Date(); |
116 } | 116 } |
117 BaseGame.prototype = new events.EventEmitter(); | 117 BaseGame.prototype = new events.EventEmitter(); |
123 }; | 123 }; |
124 | 124 |
125 BaseGame.prototype.framepush = function(chunk) { | 125 BaseGame.prototype.framepush = function(chunk) { |
126 /* If this chunk resets the screen, discard what preceded it. */ | 126 /* If this chunk resets the screen, discard what preceded it. */ |
127 if (isclear(chunk)) { | 127 if (isclear(chunk)) { |
128 this.replaybuf = new Buffer(1024); | 128 this.replaybuf = Buffer.alloc(1024); |
129 this.replaylen = 0; | 129 this.replaylen = 0; |
130 } | 130 } |
131 /* Make sure there's space. */ | 131 /* Make sure there's space. */ |
132 while (this.replaybuf.length < chunk.length + this.replaylen) { | 132 while (this.replaybuf.length < chunk.length + this.replaylen) { |
133 var nbuf = new Buffer(this.replaybuf.length * 2); | 133 var nbuf = Buffer.alloc(this.replaybuf.length * 2); |
134 this.replaybuf.copy(nbuf, 0, 0, this.replaylen); | 134 this.replaybuf.copy(nbuf, 0, 0, this.replaylen); |
135 this.replaybuf = nbuf; | 135 this.replaybuf = nbuf; |
136 if (this.replaybuf.length > 65536) { | 136 if (this.replaybuf.length > 65536) { |
137 tslog("Warning: %s frame buffer at %d bytes", this.tag(), | 137 tslog("Warning: %s frame buffer at %d bytes", this.tag(), |
138 this.replaybuf.length); | 138 this.replaybuf.length); |
208 /* Set up the lockfile and ttyrec */ | 208 /* Set up the lockfile and ttyrec */ |
209 var ts = timestamp(this.lasttime); | 209 var ts = timestamp(this.lasttime); |
210 var progressdir = path.join("/dgldir/inprogress", this.gname); | 210 var progressdir = path.join("/dgldir/inprogress", this.gname); |
211 this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec"); | 211 this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec"); |
212 var lmsg = this.term.pid.toString() + '\n' + this.h + '\n' + this.w + '\n'; | 212 var lmsg = this.term.pid.toString() + '\n' + this.h + '\n' + this.w + '\n'; |
213 fs.writeFile(this.lock, lmsg, "utf8"); | 213 fs.writeFile(this.lock, lmsg, "utf8", function (err) { |
214 if (err) tslog("Locking failed: %s", err); | |
215 }); | |
214 var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname, | 216 var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname, |
215 ts + ".ttyrec"); | 217 ts + ".ttyrec"); |
216 this.record = fs.createWriteStream(ttyrec, { mode: 0664 }); | 218 this.record = fs.createWriteStream(ttyrec, { mode: 0664 }); |
217 /* The player's WebSocket and its handlers. */ | 219 /* The player's WebSocket and its handlers. */ |
218 this.playerconn = wsReq.accept(null, wsReq.origin); | 220 this.playerconn = wsReq.accept(null, wsReq.origin); |
236 TermSession.prototype = new BaseGame(); | 238 TermSession.prototype = new BaseGame(); |
237 | 239 |
238 /* Currently this also sends to the player and any watchers. */ | 240 /* Currently this also sends to the player and any watchers. */ |
239 TermSession.prototype.write_ttyrec = function (datastr) { | 241 TermSession.prototype.write_ttyrec = function (datastr) { |
240 this.lasttime = new Date(); | 242 this.lasttime = new Date(); |
241 var buf = new Buffer(datastr); | 243 var buf = Buffer.from(datastr); |
242 var chunk = new Buffer(buf.length + 12); | 244 var chunk = Buffer.alloc(buf.length + 12); |
243 /* TTYREC headers */ | 245 /* TTYREC headers */ |
244 chunk.writeUInt32LE(Math.floor(this.lasttime.getTime() / 1000), 0); | 246 chunk.writeUInt32LE(Math.floor(this.lasttime.getTime() / 1000), 0); |
245 chunk.writeUInt32LE(1000 * (this.lasttime.getTime() % 1000), 4); | 247 chunk.writeUInt32LE(1000 * (this.lasttime.getTime() % 1000), 4); |
246 chunk.writeUInt32LE(buf.length, 8); | 248 chunk.writeUInt32LE(buf.length, 8); |
247 buf.copy(chunk, 12); | 249 buf.copy(chunk, 12); |
271 else if (parsedMsg.t == 'd') { | 273 else if (parsedMsg.t == 'd') { |
272 var hexstr = parsedMsg.d.replace(/[^0-9a-f]/gi, ""); | 274 var hexstr = parsedMsg.d.replace(/[^0-9a-f]/gi, ""); |
273 if (hexstr.length % 2 != 0) { | 275 if (hexstr.length % 2 != 0) { |
274 hexstr = hexstr.slice(0, -1); | 276 hexstr = hexstr.slice(0, -1); |
275 } | 277 } |
276 var keybuf = new Buffer(hexstr, "hex"); | 278 var keybuf = Buffer.from(hexstr, "hex"); |
277 this.write(keybuf); | 279 this.write(keybuf); |
278 } | 280 } |
279 }; | 281 }; |
280 | 282 |
281 /* Teardown. */ | 283 /* Teardown. */ |
284 this.term.kill('SIGHUP'); | 286 this.term.kill('SIGHUP'); |
285 }; | 287 }; |
286 | 288 |
287 TermSession.prototype.destroy = function () { | 289 TermSession.prototype.destroy = function () { |
288 var tag = this.tag(); | 290 var tag = this.tag(); |
289 fs.unlink(this.lock); | 291 fs.unlink(this.lock, function (err) { |
292 if (err) tslog("Lock removal failed: %s", err); | |
293 }); | |
290 this.record.end(); | 294 this.record.end(); |
291 var watchsocks = this.watchers; | 295 var watchsocks = this.watchers; |
292 this.watchers = []; | 296 this.watchers = []; |
293 for (var i = 0; i < watchsocks.length; i++) { | 297 for (var i = 0; i < watchsocks.length; i++) { |
294 if (watchsocks[i].connected) | 298 if (watchsocks[i].connected) |
353 /* 3 functions to get data from the ttyrec file. */ | 357 /* 3 functions to get data from the ttyrec file. */ |
354 DglSession.prototype.startchunk = function () { | 358 DglSession.prototype.startchunk = function () { |
355 if (this.reading) | 359 if (this.reading) |
356 return; | 360 return; |
357 this.reading = true; | 361 this.reading = true; |
358 var header = new Buffer(12); | 362 var header = Buffer.alloc(12); |
359 fs.read(this.fd, header, 0, 12, this.rpos, this.datachunk.bind(this)); | 363 fs.read(this.fd, header, 0, 12, this.rpos, this.datachunk.bind(this)); |
360 }; | 364 }; |
361 | 365 |
362 DglSession.prototype.datachunk = function (err, n, buf) { | 366 DglSession.prototype.datachunk = function (err, n, buf) { |
363 /* Stop recursion if end of file has been reached. */ | 367 /* Stop recursion if end of file has been reached. */ |
374 var datalen = buf.readUInt32LE(8); | 378 var datalen = buf.readUInt32LE(8); |
375 if (datalen > 16384) { | 379 if (datalen > 16384) { |
376 // Something is probably wrong... | 380 // Something is probably wrong... |
377 tslog("DGL %s: looking for %d bytes", this.tag(), datalen); | 381 tslog("DGL %s: looking for %d bytes", this.tag(), datalen); |
378 } | 382 } |
379 var databuf = new Buffer(datalen); | 383 var databuf = Buffer.alloc(datalen); |
380 fs.read(this.fd, databuf, 0, datalen, this.rpos, this.handledata.bind(this)); | 384 fs.read(this.fd, databuf, 0, datalen, this.rpos, this.handledata.bind(this)); |
381 }; | 385 }; |
382 | 386 |
383 DglSession.prototype.handledata = function (err, n, buf) { | 387 DglSession.prototype.handledata = function (err, n, buf) { |
384 if (err || n < buf.length) { | 388 if (err || n < buf.length) { |
417 this.watchers = []; | 421 this.watchers = []; |
418 for (var i = 0; i < connlist.length; i++) { | 422 for (var i = 0; i < connlist.length; i++) { |
419 if (connlist[i].connected) | 423 if (connlist[i].connected) |
420 connlist[i].close(); | 424 connlist[i].close(); |
421 } | 425 } |
422 fs.close(this.fd); | 426 fs.close(this.fd, function (err) { |
427 if (err) tslog("PTY close failed: %s", err); | |
428 }); | |
423 this.emit("close"); | 429 this.emit("close"); |
424 gamemux.emit('end', this.gname, this.pname); | 430 gamemux.emit('end', this.gname, this.pname); |
425 tslog("DGL %s: closed", this.tag()); | 431 tslog("DGL %s: closed", this.tag()); |
426 }; | 432 }; |
427 | 433 |
688 | 694 |
689 /* Sets things up for a new user, like dgamelaunch's commands[register] */ | 695 /* Sets things up for a new user, like dgamelaunch's commands[register] */ |
690 function regsetup(username) { | 696 function regsetup(username) { |
691 function regsetup_l2(err) { | 697 function regsetup_l2(err) { |
692 for (var g in games) { | 698 for (var g in games) { |
693 fs.mkdir(path.join("/dgldir/ttyrec", username, games[g].uname), 0755); | 699 fs.mkdir(path.join("/dgldir/ttyrec", username, games[g].uname), 0755, |
694 } | 700 function (err) { |
695 } | 701 if (err) tslog("ttyrec mkdir failed: %s", err); |
696 fs.mkdir(path.join("/dgldir/userdata", username), 0755); | 702 }); |
703 } | |
704 } | |
705 fs.mkdir(path.join("/dgldir/userdata", username), 0755, function (err) { | |
706 if (err) tslog("Userdata mkdir failed: %s", err); | |
707 }); | |
697 fs.mkdir(path.join("/dgldir/ttyrec/", username), 0755, regsetup_l2); | 708 fs.mkdir(path.join("/dgldir/ttyrec/", username), 0755, regsetup_l2); |
698 } | 709 } |
699 | 710 |
700 function register(req, res, formdata) { | 711 function register(req, res, formdata) { |
701 var uname, passwd, email; | 712 var uname, passwd, email; |
779 catch (err) { | 790 catch (err) { |
780 /* If the PID is invalid, the lockfile is stale. */ | 791 /* If the PID is invalid, the lockfile is stale. */ |
781 if (err.code == "ESRCH") { | 792 if (err.code == "ESRCH") { |
782 var nodere = RegExp("^" + pname + ":node:"); | 793 var nodere = RegExp("^" + pname + ":node:"); |
783 if (fname.match(nodere)) { | 794 if (fname.match(nodere)) { |
784 fs.unlink(fullfile); | 795 fs.unlink(fullfile, function (err) { |
796 if (err) tslog("Stale lock removal failed: %s", err); | |
797 }); | |
785 } | 798 } |
786 } | 799 } |
787 } | 800 } |
788 /* The response doesn't mean that the game is gone. The only way | 801 /* The response doesn't mean that the game is gone. The only way |
789 * to make sure a dgamelaunch-supervised game is over would be to | 802 * to make sure a dgamelaunch-supervised game is over would be to |