diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php index 8aa8eac0d8..0196f47722 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php @@ -19,12 +19,19 @@ final class PhabricatorDaemonManagementRestartWorkflow 'seconds. Defaults to __15__ seconds.'), 'default' => 15, ), + array( + 'name' => 'force', + 'help' => pht( + 'Also stop running processes that look like daemons but do '. + 'not have corresponding PID files.'), + ), )); } public function execute(PhutilArgumentParser $args) { $graceful = $args->getArg('graceful'); - $err = $this->executeStopCommand(array(), $graceful); + $force = $args->getArg('force'); + $err = $this->executeStopCommand(array(), $graceful, $force); if ($err) { return $err; } diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php index 3b690af58c..209a6cf964 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php @@ -20,6 +20,12 @@ final class PhabricatorDaemonManagementStopWorkflow 'seconds. Defaults to __15__ seconds.'), 'default' => 15, ), + array( + 'name' => 'force', + 'help' => pht( + 'Also stop running processes that look like daemons but do '. + 'not have corresponding PID files.'), + ), array( 'name' => 'pids', 'wildcard' => true, @@ -30,7 +36,8 @@ final class PhabricatorDaemonManagementStopWorkflow public function execute(PhutilArgumentParser $args) { $pids = $args->getArg('pids'); $graceful = $args->getArg('graceful'); - return $this->executeStopCommand($pids, $graceful); + $force = $args->getArg('force'); + return $this->executeStopCommand($pids, $graceful, $force); } } diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php index 588c4e9c7a..5d78d8a2c8 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php @@ -286,15 +286,39 @@ abstract class PhabricatorDaemonManagementWorkflow return 0; } - protected final function executeStopCommand(array $pids, $grace_period) { + protected final function executeStopCommand( + array $pids, + $grace_period, + $force) { + $console = PhutilConsole::getConsole(); $daemons = $this->loadRunningDaemons(); + $rogue_daemons = PhutilDaemonOverseer::findRunningDaemons(); if (!$daemons) { - $console->writeErr(pht('There are no running Phabricator daemons.')."\n"); + if ($force && $rogue_daemons) { + $stop_rogue_daemons = $this->buildRogueDaemons($rogue_daemons); + $this->sendStopSignals($stop_rogue_daemons, $grace_period); + } else { + $console->writeErr(pht( + 'There are no running Phabricator daemons.')."\n"); + if ($rogue_daemons) { + $console->writeErr($this->getForceStopHint($rogue_daemons)."\n"); + } + } return 0; } + if ($rogue_daemons) { + if ($force) { + $daemons = array_merge( + $daemons, + $this->buildRogueDaemons($rogue_daemons)); + } else { + $console->writeErr($this->getForceStopHint($rogue_daemons)."\n"); + } + } + $daemons = mpull($daemons, null, 'getPID'); $running = array(); @@ -325,21 +349,7 @@ abstract class PhabricatorDaemonManagementWorkflow } $all_daemons = $running; - - // If we're doing a graceful shutdown, try SIGINT first. - if ($grace_period) { - $running = $this->sendSignal($running, SIGINT, $grace_period); - } - - // If we still have daemons, SIGTERM them. - if ($running) { - $running = $this->sendSignal($running, SIGTERM, 15); - } - - // If the overseer is still alive, SIGKILL it. - if ($running) { - $this->sendSignal($running, SIGKILL, 0); - } + $this->sendStopSignals($running, $grace_period); foreach ($all_daemons as $daemon) { if ($daemon->getPIDFile()) { @@ -350,6 +360,41 @@ abstract class PhabricatorDaemonManagementWorkflow return 0; } + private function getForceStopHint($rogue_daemons) { + return pht( + 'There are processes running that look like Phabricator daemons but '. + 'have no corresponding PID files:'."\n\n".'%s'."\n\n". + 'Stop these processes by re-running this command with the --force '. + 'parameter.', + implode("\n", ipull($rogue_daemons, 'command'))); + } + + private function buildRogueDaemons(array $daemons) { + $rogue_daemons = array(); + foreach ($daemons as $pid => $data) { + $rogue_daemons[] = + PhabricatorDaemonReference::newFromRogueDictionary($data); + } + return $rogue_daemons; + } + + private function sendStopSignals($daemons, $grace_period) { + // If we're doing a graceful shutdown, try SIGINT first. + if ($grace_period) { + $daemons = $this->sendSignal($daemons, SIGINT, $grace_period); + } + + // If we still have daemons, SIGTERM them. + if ($daemons) { + $daemons = $this->sendSignal($daemons, SIGTERM, 15); + } + + // If the overseer is still alive, SIGKILL it. + if ($daemons) { + $this->sendSignal($daemons, SIGKILL, 0); + } + } + private function sendSignal(array $daemons, $signo, $wait) { $console = PhutilConsole::getConsole(); diff --git a/src/infrastructure/daemon/control/PhabricatorDaemonReference.php b/src/infrastructure/daemon/control/PhabricatorDaemonReference.php index 8a0283b85f..e2fa340054 100644 --- a/src/infrastructure/daemon/control/PhabricatorDaemonReference.php +++ b/src/infrastructure/daemon/control/PhabricatorDaemonReference.php @@ -41,6 +41,21 @@ final class PhabricatorDaemonReference { return $ref; } + /** + * Appropriate for getting @{class:PhabricatorDaemonReference} objects from + * the data from @{class:PhabricatorDaemonManagementWorkflow}'s method + * @{method:findRunningDaemons}. + * + * NOTE: the objects are not fully featured and should be used with caution. + */ + public static function newFromRogueDictionary(array $dict) { + $ref = new PhabricatorDaemonReference(); + $ref->name = pht('Rogue %s', idx($dict, 'type')); + $ref->pid = idx($dict, 'pid'); + + return $ref; + } + public function updateStatus($new_status) { try { if (!$this->daemonLog) {