Changeset View
Changeset View
Standalone View
Standalone View
src/daemon/PhutilDaemonOverseer.php
Show All 9 Lines | final class PhutilDaemonOverseer extends Phobject { | ||||
private $argv; | private $argv; | ||||
private static $instance; | private static $instance; | ||||
private $config; | private $config; | ||||
private $pools = array(); | private $pools = array(); | ||||
private $traceMode; | private $traceMode; | ||||
private $traceMemory; | private $traceMemory; | ||||
private $daemonize; | private $daemonize; | ||||
private $piddir; | |||||
private $log; | private $log; | ||||
private $libraries = array(); | private $libraries = array(); | ||||
private $modules = array(); | private $modules = array(); | ||||
private $verbose; | private $verbose; | ||||
private $lastPidfile; | |||||
private $startEpoch; | private $startEpoch; | ||||
private $autoscale = array(); | private $autoscale = array(); | ||||
private $autoscaleConfig = array(); | private $autoscaleConfig = array(); | ||||
const SIGNAL_NOTIFY = 'signal/notify'; | const SIGNAL_NOTIFY = 'signal/notify'; | ||||
const SIGNAL_RELOAD = 'signal/reload'; | const SIGNAL_RELOAD = 'signal/reload'; | ||||
const SIGNAL_GRACEFUL = 'signal/graceful'; | const SIGNAL_GRACEFUL = 'signal/graceful'; | ||||
const SIGNAL_TERMINATE = 'signal/terminate'; | const SIGNAL_TERMINATE = 'signal/terminate'; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (function_exists('posix_isatty') && posix_isatty(STDIN)) { | ||||
fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n"); | fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n"); | ||||
} | } | ||||
$config = @file_get_contents('php://stdin'); | $config = @file_get_contents('php://stdin'); | ||||
$config = id(new PhutilJSONParser())->parse($config); | $config = id(new PhutilJSONParser())->parse($config); | ||||
$this->libraries = idx($config, 'load'); | $this->libraries = idx($config, 'load'); | ||||
$this->log = idx($config, 'log'); | $this->log = idx($config, 'log'); | ||||
$this->daemonize = idx($config, 'daemonize'); | $this->daemonize = idx($config, 'daemonize'); | ||||
$this->piddir = idx($config, 'piddir'); | |||||
$this->config = $config; | $this->config = $config; | ||||
if (self::$instance) { | if (self::$instance) { | ||||
throw new Exception( | throw new Exception( | ||||
pht('You may not instantiate more than one Overseer per process.')); | pht('You may not instantiate more than one Overseer per process.')); | ||||
} | } | ||||
self::$instance = $this; | self::$instance = $this; | ||||
$this->startEpoch = time(); | $this->startEpoch = time(); | ||||
// Check this before we daemonize, since if it's an issue the child will | |||||
// exit immediately. | |||||
if ($this->piddir) { | |||||
$dir = $this->piddir; | |||||
try { | |||||
Filesystem::assertWritable($dir); | |||||
} catch (Exception $ex) { | |||||
throw new Exception( | |||||
pht( | |||||
"Specified daemon PID directory ('%s') does not exist or is ". | |||||
"not writable by the daemon user!", | |||||
amckinley: One thing we're losing here is that daemons will no longer crash instantly on startup if they… | |||||
Done Inline ActionsHaha, yes. All told, I think I deleted like four blocks of // TODO: It would be nice if this wasn't a horrible, fragile mess. code. epriestley: Haha, yes. All told, I think I deleted like four blocks of `// TODO: It would be nice if this… | |||||
$dir)); | |||||
} | |||||
} | |||||
if (!idx($config, 'daemons')) { | if (!idx($config, 'daemons')) { | ||||
throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
pht('You must specify at least one daemon to start!')); | pht('You must specify at least one daemon to start!')); | ||||
} | } | ||||
if ($this->log) { | if ($this->log) { | ||||
// NOTE: Now that we're committed to daemonizing, redirect the error | // NOTE: Now that we're committed to daemonizing, redirect the error | ||||
// log if we have a `--log` parameter. Do this at the last moment | // log if we have a `--log` parameter. Do this at the last moment | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | while (true) { | ||||
$futures[] = $future; | $futures[] = $future; | ||||
} | } | ||||
if ($pool->getDaemons()) { | if ($pool->getDaemons()) { | ||||
$running_pools = true; | $running_pools = true; | ||||
} | } | ||||
} | } | ||||
$this->updatePidfile(); | |||||
$this->updateMemory(); | $this->updateMemory(); | ||||
$this->waitForDaemonFutures($futures); | $this->waitForDaemonFutures($futures); | ||||
if (!$futures && !$running_pools) { | if (!$futures && !$running_pools) { | ||||
if ($this->shouldShutdown()) { | if ($this->shouldShutdown()) { | ||||
break; | break; | ||||
} | } | ||||
Show All 40 Lines | foreach ($configs as $config) { | ||||
$this->pools[] = $pool; | $this->pools[] = $pool; | ||||
} | } | ||||
} | } | ||||
private function getDaemonPools() { | private function getDaemonPools() { | ||||
return $this->pools; | return $this->pools; | ||||
} | } | ||||
private function updatePidfile() { | |||||
if (!$this->piddir) { | |||||
return; | |||||
} | |||||
$pidfile = $this->toDictionary(); | |||||
if ($pidfile !== $this->lastPidfile) { | |||||
$this->lastPidfile = $pidfile; | |||||
$pidfile_path = $this->piddir.'/daemon.'.getmypid(); | |||||
try { | |||||
Filesystem::writeFile( | |||||
$pidfile_path, | |||||
phutil_json_encode($pidfile)); | |||||
} catch (Exception $ex) { | |||||
// This write can fail if the disk is full. We already tested the | |||||
// directory for writability on startup, so just ignore this and | |||||
// move on rather than crashing. If the disk is full this error may | |||||
// not make it to a log file, but at least we tried. | |||||
$this->logMessage( | |||||
'PIDF', | |||||
pht( | |||||
'Unable to update PID file: %s.', | |||||
$ex->getMessage())); | |||||
} | |||||
} | |||||
} | |||||
public function toDictionary() { | |||||
$daemons = array(); | |||||
foreach ($this->getDaemonPools() as $pool) { | |||||
foreach ($pool->getDaemons() as $daemon) { | |||||
if (!$daemon->isRunning()) { | |||||
continue; | |||||
} | |||||
$daemons[] = $daemon->toDictionary(); | |||||
} | |||||
} | |||||
return array( | |||||
'pid' => getmypid(), | |||||
'start' => $this->startEpoch, | |||||
'config' => $this->config, | |||||
'daemons' => $daemons, | |||||
); | |||||
} | |||||
private function updateMemory() { | private function updateMemory() { | ||||
if (!$this->traceMemory) { | if (!$this->traceMemory) { | ||||
return; | return; | ||||
} | } | ||||
$this->logMessage( | $this->logMessage( | ||||
'RAMS', | 'RAMS', | ||||
pht( | pht( | ||||
▲ Show 20 Lines • Show All 151 Lines • Show Last 20 Lines |
One thing we're losing here is that daemons will no longer crash instantly on startup if they don't have permissions or the disk is full.