mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-28 09:42:41 +01:00
6581759259
Summary: Fixes T6944. Create the Aphlict PID directory if it does not exist. See also D11387. Test Plan: Started Aphlict... saw PID directory created. Reviewers: anton.vladimirov, epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Maniphest Tasks: T6944 Differential Revision: https://secure.phabricator.com/D11906
314 lines
8.2 KiB
PHP
314 lines
8.2 KiB
PHP
<?php
|
|
|
|
abstract class PhabricatorAphlictManagementWorkflow
|
|
extends PhabricatorManagementWorkflow {
|
|
|
|
private $debug = false;
|
|
private $clientHost;
|
|
private $clientPort;
|
|
|
|
protected function didConstruct() {
|
|
$this
|
|
->setArguments(
|
|
array(
|
|
array(
|
|
'name' => 'client-host',
|
|
'param' => 'hostname',
|
|
'help' => pht('Hostname to bind to for the client server.'),
|
|
),
|
|
array(
|
|
'name' => 'client-port',
|
|
'param' => 'port',
|
|
'help' => pht('Port to bind to for the client server.'),
|
|
),
|
|
));
|
|
}
|
|
|
|
public function execute(PhutilArgumentParser $args) {
|
|
$this->clientHost = $args->getArg('client-host');
|
|
$this->clientPort = $args->getArg('client-port');
|
|
return 0;
|
|
}
|
|
|
|
final public function getPIDPath() {
|
|
$path = PhabricatorEnv::getEnvConfig('notification.pidfile');
|
|
|
|
try {
|
|
$dir = dirname($path);
|
|
if (!Filesystem::pathExists($dir)) {
|
|
Filesystem::createDirectory($dir, 0755, true);
|
|
}
|
|
} catch (FilesystemException $ex) {
|
|
throw new Exception(
|
|
pht(
|
|
"Failed to create '%s'. You should manually create this directory.",
|
|
$dir));
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
final public function getLogPath() {
|
|
$path = PhabricatorEnv::getEnvConfig('notification.log');
|
|
|
|
try {
|
|
$dir = dirname($path);
|
|
if (!Filesystem::pathExists($dir)) {
|
|
Filesystem::createDirectory($dir, 0755, true);
|
|
}
|
|
} catch (FilesystemException $ex) {
|
|
throw new Exception(
|
|
pht(
|
|
"Failed to create '%s'. You should manually create this directory.",
|
|
$dir));
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
final public function getPID() {
|
|
$pid = null;
|
|
if (Filesystem::pathExists($this->getPIDPath())) {
|
|
$pid = (int)Filesystem::readFile($this->getPIDPath());
|
|
}
|
|
return $pid;
|
|
}
|
|
|
|
final public function cleanup($signo = '?') {
|
|
global $g_future;
|
|
if ($g_future) {
|
|
$g_future->resolveKill();
|
|
$g_future = null;
|
|
}
|
|
|
|
Filesystem::remove($this->getPIDPath());
|
|
|
|
exit(1);
|
|
}
|
|
|
|
protected final function setDebug($debug) {
|
|
$this->debug = $debug;
|
|
}
|
|
|
|
public static function requireExtensions() {
|
|
self::mustHaveExtension('pcntl');
|
|
self::mustHaveExtension('posix');
|
|
}
|
|
|
|
private static function mustHaveExtension($ext) {
|
|
if (!extension_loaded($ext)) {
|
|
echo "ERROR: The PHP extension '{$ext}' is not installed. You must ".
|
|
"install it to run aphlict on this machine.\n";
|
|
exit(1);
|
|
}
|
|
|
|
$extension = new ReflectionExtension($ext);
|
|
foreach ($extension->getFunctions() as $function) {
|
|
$function = $function->name;
|
|
if (!function_exists($function)) {
|
|
echo "ERROR: The PHP function {$function}() is disabled. You must ".
|
|
"enable it to run aphlict on this machine.\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
final protected function willLaunch() {
|
|
$console = PhutilConsole::getConsole();
|
|
|
|
$pid = $this->getPID();
|
|
if ($pid) {
|
|
throw new PhutilArgumentUsageException(
|
|
pht(
|
|
'Unable to start notifications server because it is already '.
|
|
'running. Use `aphlict restart` to restart it.'));
|
|
}
|
|
|
|
if (posix_getuid() == 0) {
|
|
throw new PhutilArgumentUsageException(
|
|
pht(
|
|
// TODO: Update this message after a while.
|
|
'The notification server should not be run as root. It no '.
|
|
'longer requires access to privileged ports.'));
|
|
}
|
|
|
|
// Make sure we can write to the PID file.
|
|
if (!$this->debug) {
|
|
Filesystem::writeFile($this->getPIDPath(), '');
|
|
}
|
|
|
|
// First, start the server in configuration test mode with --test. This
|
|
// will let us error explicitly if there are missing modules, before we
|
|
// fork and lose access to the console.
|
|
$test_argv = $this->getServerArgv();
|
|
$test_argv[] = '--test=true';
|
|
|
|
execx(
|
|
'%s %s %Ls',
|
|
$this->getNodeBinary(),
|
|
$this->getAphlictScriptPath(),
|
|
$test_argv);
|
|
}
|
|
|
|
private function getServerArgv() {
|
|
$ssl_key = PhabricatorEnv::getEnvConfig('notification.ssl-key');
|
|
$ssl_cert = PhabricatorEnv::getEnvConfig('notification.ssl-cert');
|
|
|
|
$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);
|
|
|
|
$log = $this->getLogPath();
|
|
|
|
$server_argv = array();
|
|
$server_argv[] = '--client-port='.coalesce(
|
|
$this->clientPort,
|
|
$client_uri->getPort());
|
|
$server_argv[] = '--admin-port='.$server_uri->getPort();
|
|
$server_argv[] = '--admin-host='.$server_uri->getDomain();
|
|
|
|
if ($ssl_key) {
|
|
$server_argv[] = '--ssl-key='.$ssl_key;
|
|
}
|
|
|
|
if ($ssl_cert) {
|
|
$server_argv[] = '--ssl-cert='.$ssl_cert;
|
|
}
|
|
|
|
$server_argv[] = '--log='.$log;
|
|
|
|
if ($this->clientHost) {
|
|
$server_argv[] = '--client-host='.$this->clientHost;
|
|
}
|
|
|
|
return $server_argv;
|
|
}
|
|
|
|
private function getAphlictScriptPath() {
|
|
$root = dirname(phutil_get_library_root('phabricator'));
|
|
return $root.'/support/aphlict/server/aphlict_server.js';
|
|
}
|
|
|
|
final protected function launch() {
|
|
$console = PhutilConsole::getConsole();
|
|
|
|
if ($this->debug) {
|
|
$console->writeOut(pht("Starting Aphlict server in foreground...\n"));
|
|
} else {
|
|
Filesystem::writeFile($this->getPIDPath(), getmypid());
|
|
}
|
|
|
|
$command = csprintf(
|
|
'%s %s %Ls',
|
|
$this->getNodeBinary(),
|
|
$this->getAphlictScriptPath(),
|
|
$this->getServerArgv());
|
|
|
|
if (!$this->debug) {
|
|
declare(ticks = 1);
|
|
pcntl_signal(SIGINT, array($this, 'cleanup'));
|
|
pcntl_signal(SIGTERM, array($this, 'cleanup'));
|
|
}
|
|
register_shutdown_function(array($this, 'cleanup'));
|
|
|
|
if ($this->debug) {
|
|
$console->writeOut("Launching server:\n\n $ ".$command."\n\n");
|
|
|
|
$err = phutil_passthru('%C', $command);
|
|
$console->writeOut(">>> Server exited!\n");
|
|
exit($err);
|
|
} else {
|
|
while (true) {
|
|
global $g_future;
|
|
$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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* -( Commands )----------------------------------------------------------- */
|
|
|
|
|
|
final protected function executeStartCommand() {
|
|
$console = PhutilConsole::getConsole();
|
|
$this->willLaunch();
|
|
|
|
$pid = pcntl_fork();
|
|
if ($pid < 0) {
|
|
throw new Exception('Failed to fork()!');
|
|
} else if ($pid) {
|
|
$console->writeErr(pht("Aphlict Server started.\n"));
|
|
exit(0);
|
|
}
|
|
|
|
// When we fork, the child process will inherit its parent's set of open
|
|
// file descriptors. If the parent process of bin/aphlict is waiting for
|
|
// bin/aphlict's file descriptors to close, it will be stuck waiting on
|
|
// the daemonized process. (This happens if e.g. bin/aphlict is started
|
|
// in another script using passthru().)
|
|
fclose(STDOUT);
|
|
fclose(STDERR);
|
|
|
|
|
|
$this->launch();
|
|
return 0;
|
|
}
|
|
|
|
|
|
final protected function executeStopCommand() {
|
|
$console = PhutilConsole::getConsole();
|
|
|
|
$pid = $this->getPID();
|
|
if (!$pid) {
|
|
$console->writeErr(pht("Aphlict is not running.\n"));
|
|
return 0;
|
|
}
|
|
|
|
$console->writeErr(pht("Stopping Aphlict Server (%s)...\n", $pid));
|
|
posix_kill($pid, SIGINT);
|
|
|
|
$start = time();
|
|
do {
|
|
if (!PhabricatorDaemonReference::isProcessRunning($pid)) {
|
|
$console->writeOut(
|
|
"%s\n",
|
|
pht('Aphlict Server (%s) exited normally.', $pid));
|
|
$pid = null;
|
|
break;
|
|
}
|
|
usleep(100000);
|
|
} while (time() < $start + 5);
|
|
|
|
if ($pid) {
|
|
$console->writeErr(pht('Sending %s a SIGKILL.', $pid)."\n");
|
|
posix_kill($pid, SIGKILL);
|
|
unset($pid);
|
|
}
|
|
|
|
Filesystem::remove($this->getPIDPath());
|
|
return 0;
|
|
}
|
|
|
|
private function getNodeBinary() {
|
|
if (Filesystem::binaryExists('nodejs')) {
|
|
return 'nodejs';
|
|
}
|
|
|
|
if (Filesystem::binaryExists('node')) {
|
|
return 'node';
|
|
}
|
|
|
|
throw new PhutilArgumentUsageException(
|
|
pht(
|
|
'No `nodejs` or `node` binary was found in $PATH. You must install '.
|
|
'Node.js to start the Aphlict server.'));
|
|
}
|
|
|
|
}
|