Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2892398
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
10 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/support/aphlict/server/aphlict_launcher.php b/support/aphlict/server/aphlict_launcher.php
index 80eceb8e34..c9cb6281d9 100755
--- a/support/aphlict/server/aphlict_launcher.php
+++ b/support/aphlict/server/aphlict_launcher.php
@@ -1,156 +1,157 @@
#!/usr/bin/env php
<?php
// This is a launcher for the 'aphlict' Node.js notification server that
// provides real-time notifications for Phabricator. It handles reading
// configuration from the Phabricator config, daemonizing the server,
// restarting the server if it crashes, and some basic sanity checks.
$root = dirname(dirname(dirname(dirname(__FILE__))));
require_once $root.'/scripts/__init_script__.php';
// >>> Options and Arguments ---------------------------------------------------
$args = new PhutilArgumentParser($argv);
$args->setTagline('manage Aphlict notification server');
$args->setSynopsis(<<<EOHELP
**aphlict** [__options__]
Start (or restart) the Aphlict server.
EOHELP
);
$args->parseStandardArguments();
$args->parse(array(
array(
'name' => 'foreground',
'help' => 'Run in the foreground instead of daemonizing.',
),
));
if (posix_getuid() != 0) {
throw new Exception(
"You must run this script as root; the Aphlict server needs to bind to ".
"privileged ports.");
}
list($err) = exec_manual('node -v');
if ($err) {
throw new Exception(
'`node` is not in $PATH. You must install Node.js to run the Aphlict '.
'server.');
}
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
$server_uri = new PhutilURI($server_uri);
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
$client_uri = new PhutilURI($client_uri);
$user = PhabricatorEnv::getEnvConfig('notification.user');
$log = PhabricatorEnv::getEnvConfig('notification.log');
$g_pidfile = PhabricatorEnv::getEnvConfig('notification.pidfile');
$g_future = null;
$foreground = $args->getArg('foreground');
// Build the argument list for the server itself.
$server_argv = array();
$server_argv[] = csprintf('--port=%s', $client_uri->getPort());
$server_argv[] = csprintf('--admin=%s', $server_uri->getPort());
+$server_argv[] = csprintf('--host=%s', $server_uri->getDomain());
if ($user) {
$server_argv[] = csprintf('--user=%s', $user);
}
if ($log) {
$server_argv[] = csprintf('--log=%s', $log);
}
// >>> Foreground / Background -------------------------------------------------
// If we start in the foreground, we use phutil_passthru() below to show any
// output from the server to the console, but this means *this* process won't
// receive signals until the child exits. If we write our pid to the pidfile
// and then another process starts, it will try to SIGTERM us but we won't
// receive the signal. Since the effect is the same and this is simpler, just
// ignore the pidfile if launched in `--foreground` mode; this is a debugging
// mode anyway.
if ($foreground) {
echo "Starting server in foreground, ignoring pidfile...\n";
$g_pidfile = null;
} else {
$pid = pcntl_fork();
if ($pid < 0) {
throw new Exception("Failed to fork()!");
} else if ($pid) {
exit(0);
}
}
// >>> Signals / Cleanup -------------------------------------------------------
function cleanup($sig = '?') {
global $g_pidfile;
if ($g_pidfile) {
Filesystem::remove($g_pidfile);
$g_pidfile = null;
}
global $g_future;
if ($g_future) {
$g_future->resolveKill();
$g_future = null;
}
exit(1);
}
if (!$foreground) {
declare(ticks = 1);
pcntl_signal(SIGTERM, 'cleanup');
}
register_shutdown_function('cleanup');
// >>> pidfile -----------------------------------------------------------------
if ($g_pidfile) {
if (Filesystem::pathExists($g_pidfile)) {
$old_pid = (int)Filesystem::readFile($g_pidfile);
posix_kill($old_pid, SIGTERM);
sleep(1);
Filesystem::remove($g_pidfile);
}
Filesystem::writeFile($g_pidfile, getmypid());
}
// >>> run ---------------------------------------------------------------------
$command = csprintf(
'node %s %C',
dirname(__FILE__).'/aphlict_server.js',
implode(' ', $server_argv));
if ($foreground) {
echo "Launching server:\n\n";
echo " $ ".$command."\n\n";
$err = phutil_passthru('%C', $command);
echo ">>> Server exited!\n";
exit($err);
} else {
while (true) {
$g_future = new ExecFuture('exec %C', $command);
$g_future->resolve();
// If the server exited, wait a couple of seconds and restart it.
unset($g_future);
sleep(2);
}
}
diff --git a/support/aphlict/server/aphlict_server.js b/support/aphlict/server/aphlict_server.js
index 26be15dc45..46bbacdd98 100755
--- a/support/aphlict/server/aphlict_server.js
+++ b/support/aphlict/server/aphlict_server.js
@@ -1,224 +1,225 @@
/**
* Notification server. Launch with:
*
* sudo node aphlict_server.js --user=aphlict
*
- * You can also specify `port`, `admin` and `log`.
+ * You can also specify `port`, `admin`, `host` and `log`.
*/
var config = parse_command_line_arguments(process.argv);
function parse_command_line_arguments(argv) {
var config = {
port : 22280,
admin : 22281,
+ host : '127.0.0.1',
user : null,
log: '/var/log/aphlict.log'
};
for (var ii = 2; ii < argv.length; ii++) {
var arg = argv[ii];
var matches = arg.match(/^--([^=]+)=(.*)$/);
if (!matches) {
throw new Error("Unknown argument '"+arg+"'!");
}
if (!(matches[1] in config)) {
throw new Error("Unknown argument '"+matches[1]+"'!");
}
config[matches[1]] = matches[2];
}
config.port = parseInt(config.port, 10);
config.admin = parseInt(config.admin, 10);
return config;
}
if (process.getuid() != 0) {
console.log(
"ERROR: "+
"This server must be run as root because it needs to bind to privileged "+
"port 843 to start a Flash policy server. It will downgrade to run as a "+
"less-privileged user after binding if you pass a user in the command "+
"line arguments with '--user=alincoln'.");
process.exit(1);
}
var net = require('net');
var http = require('http');
var url = require('url');
var querystring = require('querystring');
var fs = require('fs');
// set up log file
var logfile = fs.createWriteStream(
config.log,
{
flags: 'a',
encoding: null,
mode: 0666
});
function log(str) {
console.log(str);
logfile.write(str + '\n');
}
process.on('uncaughtException', function (err) {
log("\n<<< UNCAUGHT EXCEPTION! >>>\n\n" + err);
process.exit(1);
});
log('----- ' + (new Date()).toLocaleString() + ' -----\n');
function getFlashPolicy() {
return [
'<?xml version="1.0"?>',
'<!DOCTYPE cross-domain-policy SYSTEM ' +
'"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">',
'<cross-domain-policy>',
'<allow-access-from domain="*" to-ports="'+config.port+'"/>',
'</cross-domain-policy>'
].join('\n');
}
net.createServer(function(socket) {
socket.write(getFlashPolicy() + '\0');
socket.end();
log('[' + socket.remoteAddress + '] Sent Flash Policy');
socket.on('error', function (e) {
log('Error in policy server: ' + e);
});
}).listen(843);
function write_json(socket, data) {
var serial = JSON.stringify(data);
var length = Buffer.byteLength(serial, 'utf8');
length = length.toString();
while (length.length < 8) {
length = '0' + length;
}
socket.write(length + serial);
}
var clients = {};
var current_connections = 0;
// According to the internet up to 2^53 can
// be stored in javascript, this is less than that
var MAX_ID = 9007199254740991;//2^53 -1
// If we get one connections per millisecond this will
// be fine as long as someone doesn't maintain a
// connection for longer than 6854793 years. If
// you want to write something pretty be my guest
function generate_id() {
if (typeof generate_id.current_id == 'undefined'
|| generate_id.current_id > MAX_ID) {
generate_id.current_id = 0;
}
return generate_id.current_id++;
}
var send_server = net.createServer(function(socket) {
var client_id = generate_id();
var client_name = '[' + socket.remoteAddress + '] [#' + client_id + '] ';
socket.on('connect', function() {
clients[client_id] = socket;
current_connections++;
log(client_name + 'connected\t\t('
+ current_connections + ' current connections)');
});
socket.on('close', function() {
delete clients[client_id];
current_connections--;
log(client_name + 'closed\t\t('
+ current_connections + ' current connections)');
});
socket.on('timeout', function() {
log(client_name + 'timed out!');
});
socket.on('end', function() {
log(client_name + 'ended the connection');
// node automatically closes half-open connections
});
socket.on('error', function (e) {
log(cliient_name + 'Uncaught error in send server: ' + e);
});
}).listen(config.port);
var messages_out = 0;
var messages_in = 0;
var start_time = new Date().getTime();
var receive_server = http.createServer(function(request, response) {
response.writeHead(200, {'Content-Type' : 'text/plain'});
// Publishing a notification.
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
});
request.on('end', function () {
++messages_in;
var data = querystring.parse(body);
log('notification: ' + JSON.stringify(data));
broadcast(data);
response.end();
});
} else if (request.url == '/status/') {
request.on('end', function() {
var status = {
'uptime': (new Date().getTime() - start_time),
'clients.active': current_connections,
'clients.total': generate_id.current_id || 0,
'messages.in': messages_in,
'messages.out': messages_out,
'log': config.log
};
response.write(JSON.stringify(status));
response.end();
});
} else {
response.statusCode = 400;
response.write('400 Bad Request');
response.end();
}
-}).listen(config.admin, '127.0.0.1');
+}).listen(config.admin, config.host);
function broadcast(data) {
for (var client_id in clients) {
try {
write_json(clients[client_id], data);
++messages_out;
log('wrote to client ' + client_id);
} catch (error) {
delete clients[client_id];
current_connections--;
log('ERROR: could not write to client ' + client_id);
}
}
}
// If we're configured to drop permissions, get rid of them now that we've
// bound to the ports we need and opened logfiles.
if (config.user) {
process.setuid(config.user);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:45 (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126611
Default Alt Text
(10 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment