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