Page MenuHomePhorge

No OneTemporary

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

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)

Event Timeline