diff --git a/resources/sql/autopatches/20141223.daemonloguser.sql b/resources/sql/autopatches/20141223.daemonloguser.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141223.daemonloguser.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_daemon.daemon_log + ADD runningAsUser VARCHAR(255); diff --git a/src/applications/daemon/event/PhabricatorDaemonEventListener.php b/src/applications/daemon/event/PhabricatorDaemonEventListener.php --- a/src/applications/daemon/event/PhabricatorDaemonEventListener.php +++ b/src/applications/daemon/event/PhabricatorDaemonEventListener.php @@ -34,11 +34,13 @@ private function handleLaunchEvent(PhutilEvent $event) { $id = $event->getValue('id'); + $current_user = posix_getpwuid(posix_geteuid()); $daemon = id(new PhabricatorDaemonLog()) ->setDaemon($event->getValue('daemonClass')) ->setHost(php_uname('n')) ->setPID(getmypid()) + ->setRunningAsUser($current_user['name']) ->setEnvHash(PhabricatorEnv::calculateEnvironmentHash()) ->setStatus(PhabricatorDaemonLog::STATUS_RUNNING) ->setArgv($event->getValue('argv')) diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php --- a/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php @@ -21,11 +21,17 @@ 'name' => 'argv', 'wildcard' => true, ), + array( + 'name' => 'as-current-user', + 'help' => 'Run the daemon as the current user '. + 'instead of the configured phd.user', + ), )); } public function execute(PhutilArgumentParser $args) { $argv = $args->getArg('argv'); + $run_as_current_user = $args->getArg('as-current-user'); if (!$argv) { throw new PhutilArgumentUsageException( @@ -33,7 +39,11 @@ } $daemon_class = array_shift($argv); - return $this->launchDaemon($daemon_class, $argv, $is_debug = true); + return $this->launchDaemon( + $daemon_class, + $argv, + $is_debug = true, + $run_as_current_user); } } 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 @@ -3,6 +3,8 @@ abstract class PhabricatorDaemonManagementWorkflow extends PhabricatorManagementWorkflow { + private $runDaemonsAsUser = null; + protected final function loadAvailableDaemonClasses() { $loader = new PhutilSymbolLoader(); return $loader @@ -103,10 +105,37 @@ return head($match); } - protected final function launchDaemon($class, array $argv, $debug) { + protected final function launchDaemon( + $class, + array $argv, + $debug, + $run_as_current_user = false) { + $daemon = $this->findDaemonClass($class); $console = PhutilConsole::getConsole(); + if (!$run_as_current_user) { + // Check if the script is started as the correct user + $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); + $current_user = posix_getpwuid(posix_geteuid()); + $current_user = $current_user['name']; + if ($phd_user && $phd_user != $current_user) { + if ($debug) { + throw new PhutilArgumentUsageException(pht( + 'You are trying to run a daemon as a nonstandard user, '. + 'and `phd` was not able to `sudo` to the correct user. '."\n". + 'Phabricator is configured to run daemons as "%s", '. + 'but the current user is "%s". '."\n". + 'Use `sudo` to run as a different user, pass `--as-current-user` '. + 'to ignore this warning, or edit `phd.user` '. + 'to change the configuration.', $phd_user, $current_user)); + } else { + $this->runDaemonsAsUser = $phd_user; + $console->writeOut(pht('Starting daemons as %s', $phd_user)."\n"); + } + } + } + if ($debug) { if ($argv) { $console->writeOut( @@ -187,11 +216,39 @@ phutil_passthru('(cd %s && exec %C)', $daemon_script_dir, $command); } else { - $future = new ExecFuture('exec %C', $command); - // Play games to keep 'ps' looking reasonable. - $future->setCWD($daemon_script_dir); - $future->resolvex(); + try { + $this->executeDaemonLaunchCommand( + $command, + $daemon_script_dir, + $this->runDaemonsAsUser); + } catch (CommandException $e) { + // Retry without sudo + $console->writeOut(pht( + "sudo command failed. Starting daemon as current user\n")); + $this->executeDaemonLaunchCommand( + $command, + $daemon_script_dir); + } + } + } + + private function executeDaemonLaunchCommand( + $command, + $daemon_script_dir, + $run_as_user = null) { + + if ($run_as_user) { + // If anything else besides sudo should be + // supported then insert it here (runuser, su, ...) + $command = csprintf( + 'sudo -En -u %s -- %C', + $run_as_user, + $command); } + $future = new ExecFuture('cd %s && exec %C', $daemon_script_dir, $command); + // Play games to keep 'ps' looking reasonable. + $future->setCWD($daemon_script_dir); + $future->resolvex(); } public static function ignoreSignal($signo) { diff --git a/src/applications/daemon/storage/PhabricatorDaemonLog.php b/src/applications/daemon/storage/PhabricatorDaemonLog.php --- a/src/applications/daemon/storage/PhabricatorDaemonLog.php +++ b/src/applications/daemon/storage/PhabricatorDaemonLog.php @@ -13,6 +13,7 @@ protected $daemon; protected $host; protected $pid; + protected $runningAsUser; protected $argv; protected $explicitArgv = array(); protected $envHash; @@ -28,6 +29,7 @@ 'daemon' => 'text255', 'host' => 'text255', 'pid' => 'uint32', + 'runningAsUser' => 'text255', 'envHash' => 'bytes40', 'status' => 'text8', ),