commit
71c5fe2a05
|
@ -237,3 +237,9 @@ setuid:
|
|||
# concurrent updates), then run `node lib/channel-storage/migrate.js`.
|
||||
channel-storage:
|
||||
type: 'file'
|
||||
|
||||
# Allows for external services to access the system commandline
|
||||
# Useful for setups where stdin isn't available such as when using PM2
|
||||
service-socket:
|
||||
enabled: false
|
||||
socket: 'service.sock'
|
||||
|
|
42
index.js
42
index.js
|
@ -45,6 +45,7 @@ process.stdin.on("data", function (data) {
|
|||
}
|
||||
});
|
||||
|
||||
var validIP = require('net').isIP;
|
||||
function handleLine(line) {
|
||||
if (line === "/reload") {
|
||||
Logger.syslog.log("Reloading config");
|
||||
|
@ -75,5 +76,46 @@ function handleLine(line) {
|
|||
}
|
||||
} else if (line.indexOf("/reload-partitions") === 0) {
|
||||
sv.reloadPartitionMap();
|
||||
} else if (line.indexOf("/globalban") === 0) {
|
||||
var args = line.split(/\s+/); args.shift();
|
||||
if (args.length >= 2 && validIP(args[0]) !== 0) {
|
||||
var ip = args.shift();
|
||||
var comment = args.join(' ');
|
||||
require("./lib/database").globalBanIP(ip, comment, function (err, res) {
|
||||
if (!err) {
|
||||
Logger.eventlog.log("[acp] " + "SYSTEM" + " global banned " + ip);
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (line.indexOf("/unglobalban") === 0) {
|
||||
var args = line.split(/\s+/); args.shift();
|
||||
if (args.length >= 1 && validIP(args[0]) !== 0) {
|
||||
var ip = args.shift();
|
||||
require("./lib/database").globalUnbanIP(ip, function (err, res) {
|
||||
if (!err) {
|
||||
Logger.eventlog.log("[acp] " + "SYSTEM" + " un-global banned " + ip);
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (line.indexOf("/unloadchan") === 0) {
|
||||
var args = line.split(/\s+/); args.shift();
|
||||
if(args.length){
|
||||
var name = args.shift();
|
||||
var chan = sv.getChannel(name);
|
||||
var users = Array.prototype.slice.call(chan.users);
|
||||
chan.emit("empty");
|
||||
users.forEach(function (u) {
|
||||
u.kick("Channel shutting down");
|
||||
});
|
||||
Logger.eventlog.log("[acp] " + "SYSTEM" + " forced unload of " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go Go Gadget Service Socket
|
||||
if (Config.get("service-socket.enabled")) {
|
||||
Logger.syslog.log("Opening service socket");
|
||||
var ServiceSocket = require('./lib/servsock');
|
||||
var server = new ServiceSocket;
|
||||
server.init(handleLine, Config.get("service-socket.socket"));
|
||||
}
|
||||
|
|
116
servcmd.sh.js
Executable file
116
servcmd.sh.js
Executable file
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env node
|
||||
/*
|
||||
** CyTube Service Socket Commandline
|
||||
*/
|
||||
|
||||
const readline = require('readline');
|
||||
const spawn = require('child_process').spawn;
|
||||
const util = require('util');
|
||||
const net = require('net');
|
||||
const fs = require('fs');
|
||||
|
||||
const COMPLETIONS = [
|
||||
"/delete_old_tables",
|
||||
"/gc",
|
||||
"/globalban",
|
||||
"/reload",
|
||||
"/reload-partitions",
|
||||
"/switch",
|
||||
"/unglobalban",
|
||||
"/unloadchan"
|
||||
];
|
||||
|
||||
var Config = require("./lib/config");
|
||||
Config.load("config.yaml");
|
||||
|
||||
if(!Config.get("service-socket.enabled")){
|
||||
console.error('The Service Socket is not enabled.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const SOCKETFILE = Config.get("service-socket.socket");
|
||||
|
||||
// Wipe the TTY
|
||||
process.stdout.write('\x1Bc');
|
||||
|
||||
var commandline, eventlog, syslog;
|
||||
var client = net.createConnection(SOCKETFILE).on('connect', () => {
|
||||
commandline = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
completer: tabcomplete
|
||||
});
|
||||
commandline.setPrompt("> ", 2);
|
||||
commandline.on("line", function(line) {
|
||||
if(line === 'exit'){ return cleanup(); }
|
||||
if(line === 'quit'){ return cleanup(); }
|
||||
if(line.match(/^\/globalban/) && line.split(/\s+/).length === 2){
|
||||
console.log('You must provide a reason')
|
||||
return commandline.prompt();
|
||||
}
|
||||
client.write(line);
|
||||
commandline.prompt();
|
||||
});
|
||||
commandline.on('close', function() {
|
||||
return cleanup();
|
||||
});
|
||||
commandline.on("SIGINT", function() {
|
||||
commandline.clearLine();
|
||||
commandline.question("Terminate connection? ", function(answer) {
|
||||
return answer.match(/^y(es)?$/i) ? cleanup() : commandline.output.write("> ");
|
||||
});
|
||||
});
|
||||
commandline.prompt();
|
||||
|
||||
console.log = function() { cmdouthndlr("log", arguments); }
|
||||
console.warn = function() { cmdouthndlr("warn", arguments); }
|
||||
console.error = function() { cmdouthndlr("error", arguments); }
|
||||
// console.info is reserved in this script for the exit message
|
||||
// this prevents an extraneous final prompt from readline on terminate
|
||||
|
||||
eventlog = spawn('tail', ['-f', 'events.log']);
|
||||
eventlog.stdout.on('data', function (data) {
|
||||
console.log(data.toString().replace(/^(.+)$/mg, 'events: $1'));
|
||||
});
|
||||
|
||||
syslog = spawn('tail', ['-f', 'sys.log']);
|
||||
syslog.stdout.on('data', function (data) {
|
||||
console.log(data.toString().replace(/^(.+)$/mg, 'sys: $1'));
|
||||
});
|
||||
|
||||
}).on('data', (msg) => {
|
||||
msg = msg.toString();
|
||||
|
||||
if(msg === '__disconnect'){
|
||||
console.log('Server shutting down.');
|
||||
return cleanup();
|
||||
}
|
||||
|
||||
// Generic message handler
|
||||
console.log('server: ', data)
|
||||
|
||||
}).on('error', (data) => {
|
||||
console.error('Unable to connect to Service Socket.', data);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
function cmdouthndlr(type, args) {
|
||||
var t = Math.ceil((commandline.line.length + 3) / process.stdout.columns);
|
||||
var text = util.format.apply(console, args);
|
||||
commandline.output.write("\n\x1B[" + t + "A\x1B[0J");
|
||||
commandline.output.write(text + "\n");
|
||||
commandline.output.write(Array(t).join("\n\x1B[E"));
|
||||
commandline._refreshLine();
|
||||
}
|
||||
|
||||
function cleanup(){
|
||||
console.info('\n',"Terminating.",'\n');
|
||||
eventlog.kill('SIGTERM');
|
||||
syslog.kill('SIGTERM');
|
||||
client.end();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function tabcomplete(line) {
|
||||
return [COMPLETIONS.filter((cv)=>{ return cv.indexOf(line) == 0; }), line];
|
||||
}
|
|
@ -116,6 +116,10 @@ var defaults = {
|
|||
},
|
||||
"channel-storage": {
|
||||
type: "file"
|
||||
},
|
||||
"service-socket": {
|
||||
enabled: false,
|
||||
socket: "service.sock"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
53
src/servsock.js
Normal file
53
src/servsock.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
var fs = require('fs');
|
||||
var net = require('net');
|
||||
|
||||
export default class ServiceSocket {
|
||||
|
||||
constructor() {
|
||||
this.connections = {};
|
||||
}
|
||||
|
||||
init(handler, socket){
|
||||
this.handler = handler;
|
||||
this.socket = socket;
|
||||
|
||||
fs.stat(this.socket, (err, stats) => {
|
||||
if (err) {
|
||||
return this.openServiceSocket();
|
||||
}
|
||||
fs.unlink(this.socket, (err) => {
|
||||
if(err){
|
||||
console.error(err); process.exit(0);
|
||||
}
|
||||
return this.openServiceSocket();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
openServiceSocket(){
|
||||
this.server = net.createServer((stream) => {
|
||||
let id = Date.now();
|
||||
this.connections[id] = stream;
|
||||
stream.on('end', () => {
|
||||
delete this.connections[id];
|
||||
});
|
||||
stream.on('data', (msg) => {
|
||||
this.handler(msg.toString());
|
||||
});
|
||||
}).listen(this.socket);
|
||||
process.on('exit', this.closeServiceSocket.bind(this));
|
||||
}
|
||||
|
||||
closeServiceSocket() {
|
||||
if(Object.keys(this.connections).length){
|
||||
let clients = Object.keys(this.connections);
|
||||
while(clients.length){
|
||||
let client = clients.pop();
|
||||
this.connections[client].write('__disconnect');
|
||||
this.connections[client].end();
|
||||
}
|
||||
}
|
||||
this.server.close();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue