diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php --- a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php @@ -19,12 +19,19 @@ '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 --- a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php @@ -21,6 +21,12 @@ '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 @@ 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 --- a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php @@ -286,15 +286,39 @@ 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,29 +349,50 @@ } $all_daemons = $running; + $this->sendStopSignals($running, $grace_period); + + foreach ($all_daemons as $daemon) { + if ($daemon->getPIDFile()) { + Filesystem::remove($daemon->getPIDFile()); + } + } + + 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) { - $running = $this->sendSignal($running, SIGINT, $grace_period); + $daemons = $this->sendSignal($daemons, SIGINT, $grace_period); } // If we still have daemons, SIGTERM them. - if ($running) { - $running = $this->sendSignal($running, SIGTERM, 15); + if ($daemons) { + $daemons = $this->sendSignal($daemons, SIGTERM, 15); } // If the overseer is still alive, SIGKILL it. - if ($running) { - $this->sendSignal($running, SIGKILL, 0); + if ($daemons) { + $this->sendSignal($daemons, SIGKILL, 0); } - - foreach ($all_daemons as $daemon) { - if ($daemon->getPIDFile()) { - Filesystem::remove($daemon->getPIDFile()); - } - } - - return 0; } private function sendSignal(array $daemons, $signo, $wait) { diff --git a/src/infrastructure/daemon/control/PhabricatorDaemonReference.php b/src/infrastructure/daemon/control/PhabricatorDaemonReference.php --- a/src/infrastructure/daemon/control/PhabricatorDaemonReference.php +++ b/src/infrastructure/daemon/control/PhabricatorDaemonReference.php @@ -41,6 +41,21 @@ 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) {