/**
  * Raise more useful errors when there are basic filesystem problems.
  */
 private function assertLocalExists()
 {
     if (!$this->usesLocalWorkingCopy()) {
         return;
     }
     $local = $this->getLocalPath();
     Filesystem::assertExists($local);
     Filesystem::assertIsDirectory($local);
     Filesystem::assertReadable($local);
 }
 protected final function launchDaemons(array $daemons, $debug, $run_as_current_user = false)
 {
     // Convert any shorthand classnames like "taskmaster" into proper class
     // names.
     foreach ($daemons as $key => $daemon) {
         $class = $this->findDaemonClass($daemon['class']);
         $daemons[$key]['class'] = $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 `%s` was not able to `%s` to the correct user. \n" . 'Phabricator is configured to run daemons as "%s", ' . 'but the current user is "%s". ' . "\n" . 'Use `%s` to run as a different user, pass `%s` to ignore this ' . 'warning, or edit `%s` to change the configuration.', 'phd', 'sudo', $phd_user, $current_user, 'sudo', '--as-current-user', 'phd.user'));
             } else {
                 $this->runDaemonsAsUser = $phd_user;
                 $console->writeOut(pht('Starting daemons as %s', $phd_user) . "\n");
             }
         }
     }
     $this->printLaunchingDaemons($daemons, $debug);
     $flags = array();
     if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) {
         $flags[] = '--trace';
     }
     if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
         $flags[] = '--verbose';
     }
     $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
     if ($instance) {
         $flags[] = '-l';
         $flags[] = $instance;
     }
     $config = array();
     if (!$debug) {
         $config['daemonize'] = true;
     }
     if (!$debug) {
         $config['log'] = $this->getLogDirectory() . '/daemons.log';
     }
     $pid_dir = $this->getPIDDirectory();
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     $config['piddir'] = $pid_dir;
     $config['daemons'] = $daemons;
     $command = csprintf('./phd-daemon %Ls', $flags);
     $phabricator_root = dirname(phutil_get_library_root('phabricator'));
     $daemon_script_dir = $phabricator_root . '/scripts/daemon/';
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array(__CLASS__, 'ignoreSignal'));
         echo "\n    phabricator/scripts/daemon/ \$ {$command}\n\n";
         $tempfile = new TempFile('daemon.config');
         Filesystem::writeFile($tempfile, json_encode($config));
         phutil_passthru('(cd %s && exec %C < %s)', $daemon_script_dir, $command, $tempfile);
     } else {
         try {
             $this->executeDaemonLaunchCommand($command, $daemon_script_dir, $config, $this->runDaemonsAsUser);
         } catch (Exception $e) {
             // Retry without sudo
             $console->writeOut("%s\n", pht('sudo command failed. Starting daemon as current user.'));
             $this->executeDaemonLaunchCommand($command, $daemon_script_dir, $config);
         }
     }
 }
 protected final function launchDaemon($class, array $argv, $debug)
 {
     $daemon = $this->findDaemonClass($class);
     $console = PhutilConsole::getConsole();
     if ($debug) {
         if ($argv) {
             $console->writeOut(pht("Launching daemon \"%s\" in debug mode (not daemonized) " . "with arguments %s.\n", $daemon, csprintf('%LR', $argv)));
         } else {
             $console->writeOut(pht("Launching daemon \"%s\" in debug mode (not daemonized).\n", $daemon));
         }
     } else {
         if ($argv) {
             $console->writeOut(pht("Launching daemon \"%s\" with arguments %s.\n", $daemon, csprintf('%LR', $argv)));
         } else {
             $console->writeOut(pht("Launching daemon \"%s\".\n", $daemon));
         }
     }
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $flags = array();
     if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) {
         $flags[] = '--trace';
     }
     if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
         $flags[] = '--verbose';
     }
     if (!$debug) {
         $flags[] = '--daemonize';
     }
     if (!$debug) {
         $log_file = $this->getLogDirectory() . '/daemons.log';
         $flags[] = csprintf('--log=%s', $log_file);
     }
     $pid_dir = $this->getPIDDirectory();
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     $flags[] = csprintf('--phd=%s', $pid_dir);
     $command = csprintf('./phd-daemon %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv));
     $phabricator_root = dirname(phutil_get_library_root('phabricator'));
     $daemon_script_dir = $phabricator_root . '/scripts/daemon/';
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array(__CLASS__, 'ignoreSignal'));
         echo "\n    phabricator/scripts/daemon/ \$ {$command}\n\n";
         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();
     }
 }
 public function launchDaemon($daemon, array $argv, $debug = false)
 {
     $symbols = $this->loadAvailableDaemonClasses();
     $symbols = ipull($symbols, 'name', 'name');
     if (empty($symbols[$daemon])) {
         throw new Exception("Daemon '{$daemon}' is not loaded, misspelled or abstract.");
     }
     $libphutil_root = dirname(phutil_get_library_root('phutil'));
     $launch_daemon = $libphutil_root . '/scripts/daemon/';
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $flags = array();
     if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) {
         $flags[] = '--trace';
     }
     if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
         $flags[] = '--verbose';
     }
     if (!$debug) {
         $flags[] = '--daemonize';
     }
     $bootloader = PhutilBootloader::getInstance();
     foreach ($bootloader->getAllLibraries() as $library) {
         if ($library == 'phutil') {
             // No need to load libphutil, it's necessarily loaded implicitly by the
             // daemon itself.
             continue;
         }
         $flags[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library));
     }
     $flags[] = csprintf('--conduit-uri=%s', PhabricatorEnv::getURI('/api/'));
     if (!$debug) {
         $log_file = $this->getLogDirectory() . '/daemons.log';
         $flags[] = csprintf('--log=%s', $log_file);
     }
     $pid_dir = $this->getPIDDirectory();
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     $flags[] = csprintf('--phd=%s', $pid_dir);
     $command = csprintf('./launch_daemon.php %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv));
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal'));
         echo "\n    libphutil/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($launch_daemon);
         $future->resolvex();
     }
 }
 public function launchDaemon($daemon, array $argv, $debug = false)
 {
     $symbols = $this->loadAvailableDaemonClasses();
     $symbols = ipull($symbols, 'name', 'name');
     if (empty($symbols[$daemon])) {
         throw new Exception("Daemon '{$daemon}' is not known.");
     }
     $pid_dir = $this->getControlDirectory('pid');
     $log_dir = $this->getControlDirectory('log') . '/daemons.log';
     $libphutil_root = dirname(phutil_get_library_root('phutil'));
     $launch_daemon = $libphutil_root . '/scripts/daemon/';
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $bootloader = PhutilBootloader::getInstance();
     $all_libraries = $bootloader->getAllLibraries();
     $non_default_libraries = array_diff($all_libraries, array('phutil', 'phabricator'));
     $extra_libraries = array();
     foreach ($non_default_libraries as $library) {
         $extra_libraries[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library));
     }
     $command = csprintf("./launch_daemon.php " . "%s " . "--load-phutil-library=%s " . "%C " . "--conduit-uri=%s " . "--phd=%s " . ($debug ? '--trace ' : '--daemonize '), $daemon, phutil_get_library_root('phabricator'), implode(' ', $extra_libraries), PhabricatorEnv::getURI('/api/'), $pid_dir);
     if (!$debug) {
         // If we're running "phd debug", send output straight to the console
         // instead of to a logfile.
         $command = csprintf("%C --log=%s", $command, $log_dir);
     }
     // Append the daemon's argv.
     $command = csprintf("%C %C", $command, implode(' ', $argv));
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal'));
         echo "\n    libphutil/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($launch_daemon);
         $future->resolvex();
     }
 }