Merge branch 'master' of ssh://numinibsd/git/base/enigma-bbs

This commit is contained in:
Bryan Ashby 2016-06-20 21:39:07 -06:00
commit 21ed9dc777
5 changed files with 123 additions and 51 deletions

View File

@ -5,22 +5,22 @@
//SegfaultHandler.registerHandler('enigma-bbs-segfault.log'); //SegfaultHandler.registerHandler('enigma-bbs-segfault.log');
// ENiGMA½ // ENiGMA½
let conf = require('./config.js'); const conf = require('./config.js');
let logger = require('./logger.js'); const logger = require('./logger.js');
let miscUtil = require('./misc_util.js'); const database = require('./database.js');
let database = require('./database.js'); const clientConns = require('./client_connections.js');
let clientConns = require('./client_connections.js');
let paths = require('path'); const async = require('async');
let async = require('async'); const util = require('util');
let util = require('util'); const _ = require('lodash');
let _ = require('lodash'); const mkdirs = require('fs-extra').mkdirs;
let assert = require('assert');
let mkdirs = require('fs-extra').mkdirs;
// our main entry point // our main entry point
exports.bbsMain = bbsMain; exports.bbsMain = bbsMain;
// object with various services we want to de-init/shutdown cleanly if possible
const initServices = {};
function bbsMain() { function bbsMain() {
async.waterfall( async.waterfall(
[ [
@ -84,6 +84,39 @@ function bbsMain() {
); );
} }
function shutdownSystem() {
logger.log.info('Process interrupted, shutting down...');
async.series(
[
function closeConnections(callback) {
const activeConnections = clientConns.getActiveConnections();
let i = activeConnections.length;
while(i--) {
activeConnections[i].term.write('\n\nServer is shutting down NOW! Disconnecting...\n\n');
clientConns.removeClient(activeConnections[i]);
}
callback(null);
},
function stopEventScheduler(callback) {
if(initServices.eventScheduler) {
return initServices.eventScheduler.shutdown( () => {
callback(null); // ignore err
});
} else {
return callback(null);
}
},
function stopMsgNetwork(callback) {
require('./msg_network.js').shutdown(callback);
}
],
() => {
process.exit();
}
);
}
function initialize(cb) { function initialize(cb) {
async.series( async.series(
[ [
@ -102,18 +135,7 @@ function initialize(cb) {
function basicInit(callback) { function basicInit(callback) {
logger.init(); logger.init();
process.on('SIGINT', function onSigInt() { process.on('SIGINT', shutdownSystem);
logger.log.info('Process interrupted, shutting down...');
var activeConnections = clientConns.getActiveConnections();
var i = activeConnections.length;
while(i--) {
activeConnections[i].term.write('\n\nServer is shutting down NOW! Disconnecting...\n\n');
clientConns.removeClient(activeConnections[i]);
}
process.exit();
});
// Init some extensions // Init some extensions
require('string-format').extend(String.prototype, require('./string_util.js').stringFormatExtensions); require('string-format').extend(String.prototype, require('./string_util.js').stringFormatExtensions);
@ -172,7 +194,10 @@ function initialize(cb) {
}, },
function readyEventScheduler(callback) { function readyEventScheduler(callback) {
const EventSchedulerModule = require('./event_scheduler.js').EventSchedulerModule; const EventSchedulerModule = require('./event_scheduler.js').EventSchedulerModule;
EventSchedulerModule.loadAndStart(callback); EventSchedulerModule.loadAndStart( (err, modInst) => {
initServices.eventScheduler = modInst;
return callback(err);
});
} }
], ],
function onComplete(err) { function onComplete(err) {

View File

@ -2,7 +2,6 @@
'use strict'; 'use strict';
const events = require('events'); const events = require('events');
const _ = require('lodash'); const _ = require('lodash');
const pty = require('ptyw.js'); const pty = require('ptyw.js');
const decode = require('iconv-lite').decode; const decode = require('iconv-lite').decode;
@ -80,6 +79,10 @@ function Door(client, exeInfo) {
return cb(null); return cb(null);
} }
}; };
this.doorExited = function() {
self.emit('finished');
};
} }
require('util').inherits(Door, events.EventEmitter); require('util').inherits(Door, events.EventEmitter);
@ -90,7 +93,7 @@ Door.prototype.run = function() {
this.prepareSocketIoServer( (err, sockServer) => { this.prepareSocketIoServer( (err, sockServer) => {
if(err) { if(err) {
this.client.log.warn( { error : err.toString() }, 'Failed executing door'); this.client.log.warn( { error : err.toString() }, 'Failed executing door');
return self.emit('finished'); return self.doorExited();
} }
// Expand arg strings, e.g. {dropFile} -> DOOR32.SYS // Expand arg strings, e.g. {dropFile} -> DOOR32.SYS
@ -140,7 +143,7 @@ Door.prototype.run = function() {
door.removeAllListeners(); door.removeAllListeners();
self.emit('finished'); return self.doorExited();
}); });
}); });
}; };

View File

@ -9,6 +9,8 @@ const Log = require('./logger.js').log;
const _ = require('lodash'); const _ = require('lodash');
const later = require('later'); const later = require('later');
const path = require('path'); const path = require('path');
const pty = require('ptyw.js');
const gaze = require('gaze');
exports.getModule = EventSchedulerModule; exports.getModule = EventSchedulerModule;
exports.EventSchedulerModule = EventSchedulerModule; // allow for loadAndStart exports.EventSchedulerModule = EventSchedulerModule; // allow for loadAndStart
@ -101,8 +103,8 @@ class ScheduledEvent {
} }
} }
executeAction(cb) { executeAction(reason, cb) {
Log.info( { eventName : this.name, action : this.action }, 'Executing scheduled action...'); Log.info( { eventName : this.name, action : this.action, reason : reason }, 'Executing scheduled event action...');
if('method' === this.action.type) { if('method' === this.action.type) {
const modulePath = path.join(__dirname, '../', this.action.location); // enigma-bbs base + supplied location (path/file.js') const modulePath = path.join(__dirname, '../', this.action.location); // enigma-bbs base + supplied location (path/file.js')
@ -112,7 +114,7 @@ class ScheduledEvent {
if(err) { if(err) {
Log.debug( Log.debug(
{ error : err.toString(), eventName : this.name, action : this.action }, { error : err.toString(), eventName : this.name, action : this.action },
'Error while performing scheduled event action'); 'Error performing scheduled event action');
} }
return cb(err); return cb(err);
@ -125,7 +127,24 @@ class ScheduledEvent {
return cb(e); return cb(e);
} }
} else if('execute' === this.action.type) { } else if('execute' === this.action.type) {
// :TODO: implement execute! const opts = {
// :TODO: cwd
name : this.name,
cols : 80,
rows : 24,
env : process.env,
};
const proc = pty.spawn(this.action.what, this.action.args, opts);
proc.once('exit', exitCode => {
if(exitCode) {
Log.warn(
{ eventName : this.name, action : this.action, exitCode : exitCode },
'Bad exit code while performing scheduled event action');
}
return cb(exitCode ? new Error(`Bad exit code while performing scheduled event action: ${exitCode}`) : null);
});
} }
} }
} }
@ -140,14 +159,14 @@ function EventSchedulerModule(options) {
const self = this; const self = this;
this.runningActions = new Set(); this.runningActions = new Set();
this.performAction = function(schedEvent) { this.performAction = function(schedEvent, reason) {
if(self.runningActions.has(schedEvent.name)) { if(self.runningActions.has(schedEvent.name)) {
return; // already running return; // already running
} }
self.runningActions.add(schedEvent.name); self.runningActions.add(schedEvent.name);
schedEvent.executeAction( () => { schedEvent.executeAction(reason, () => {
self.runningActions.delete(schedEvent.name); self.runningActions.delete(schedEvent.name);
}); });
}; };
@ -169,14 +188,14 @@ EventSchedulerModule.loadAndStart = function(cb) {
const modInst = new mod.getModule(); const modInst = new mod.getModule();
modInst.startup( err => { modInst.startup( err => {
return cb(err); return cb(err, modInst);
}); });
}); });
}; };
EventSchedulerModule.prototype.startup = function(cb) { EventSchedulerModule.prototype.startup = function(cb) {
this.eventTimers = []; this.eventTimers = [];
const self = this; const self = this;
if(this.moduleConfig && _.has(this.moduleConfig, 'events')) { if(this.moduleConfig && _.has(this.moduleConfig, 'events')) {
@ -192,7 +211,7 @@ EventSchedulerModule.prototype.startup = function(cb) {
Log.debug( Log.debug(
{ {
evetnName : schedEvent.name, eventName : schedEvent.name,
schedule : this.moduleConfig.events[schedEvent.name].schedule, schedule : this.moduleConfig.events[schedEvent.name].schedule,
action : schedEvent.action, action : schedEvent.action,
}, },
@ -201,11 +220,20 @@ EventSchedulerModule.prototype.startup = function(cb) {
if(schedEvent.schedule.sched) { if(schedEvent.schedule.sched) {
this.eventTimers.push(later.setInterval( () => { this.eventTimers.push(later.setInterval( () => {
self.performAction(schedEvent); self.performAction(schedEvent, 'Schedule');
}, schedEvent.schedule.sched)); }, schedEvent.schedule.sched));
} }
// :TODO: handle watchfile -> performAction if(schedEvent.schedule.watchFile) {
gaze(schedEvent.schedule.watchFile, (err, watcher) => {
// :TODO: should track watched files & stop watching @ shutdown
watcher.on('all', (watchEvent, watchedPath) => {
if(schedEvent.schedule.watchFile === watchedPath) {
self.performAction(schedEvent, `Watch file: ${watchedPath}`);
}
});
});
}
}); });
} }
@ -216,6 +244,6 @@ EventSchedulerModule.prototype.shutdown = function(cb) {
if(this.eventTimers) { if(this.eventTimers) {
this.eventTimers.forEach( et => et.clear() ); this.eventTimers.forEach( et => et.clear() );
} }
cb(null); cb(null);
}; };

View File

@ -5,9 +5,9 @@
let loadModulesForCategory = require('./module_util.js').loadModulesForCategory; let loadModulesForCategory = require('./module_util.js').loadModulesForCategory;
// standard/deps // standard/deps
let async = require('async'); let async = require('async');
exports.startup = startup exports.startup = startup;
exports.shutdown = shutdown; exports.shutdown = shutdown;
exports.recordMessage = recordMessage; exports.recordMessage = recordMessage;
@ -36,12 +36,19 @@ function startup(cb) {
); );
} }
function shutdown() { function shutdown(cb) {
msgNetworkModules.forEach(mod => { async.each(
mod.shutdown(); msgNetworkModules,
}); (msgNetModule, next) => {
msgNetModule.shutdown( () => {
msgNetworkModules = []; return next();
});
},
() => {
msgNetworkModules = [];
return cb(null);
}
);
} }
function recordMessage(message, cb) { function recordMessage(message, cb) {

View File

@ -11,7 +11,6 @@ let async = require('async');
let assert = require('assert'); let assert = require('assert');
let paths = require('path'); let paths = require('path');
let _ = require('lodash'); let _ = require('lodash');
let net = require('net');
let mkdirs = require('fs-extra').mkdirs; let mkdirs = require('fs-extra').mkdirs;
// :TODO: This should really be a system module... needs a little work to allow for such // :TODO: This should really be a system module... needs a little work to allow for such
@ -20,8 +19,6 @@ exports.getModule = AbracadabraModule;
let activeDoorNodeInstances = {}; let activeDoorNodeInstances = {};
let doorInstances = {}; // name -> { count : <instCount>, { <nodeNum> : <inst> } }
exports.moduleInfo = { exports.moduleInfo = {
name : 'Abracadabra', name : 'Abracadabra',
desc : 'External BBS Door Module', desc : 'External BBS Door Module',
@ -166,6 +163,18 @@ function AbracadabraModule(options) {
const doorInstance = new door.Door(self.client, exeInfo); const doorInstance = new door.Door(self.client, exeInfo);
doorInstance.once('finished', () => { doorInstance.once('finished', () => {
//
// Try to clean up various settings such as scroll regions that may
// have been set within the door
//
self.client.term.rawWrite(
ansi.normal() +
ansi.goto(self.client.term.termHeight, self.client.term.termWidth) +
ansi.setScrollRegion() +
ansi.goto(self.client.term.termHeight, 0) +
'\r\n\r\n'
);
self.prevMenu(); self.prevMenu();
}); });