-
Notifications
You must be signed in to change notification settings - Fork 231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a service socket #603
Add a service socket #603
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
var fs = require('fs'); | ||
var net = require('net'); | ||
|
||
export default class ServiceSocket { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this class need error event handlers? In case the client connection breaks or something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have thus far been unable to break anything. Sending SIGKILL to the client leaves orphaned children (the tail on the log files) but doesn't affect the service at all. Sending SIGTERM to the service after SIGKILL'ing a client shuts down the service with no errors. |
||
|
||
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(); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this even legal in ECMAScript?