protected function executeRepositoryOperations()
 {
     $repository = $this->getRepository();
     $viewer = $this->getUser();
     $device = AlmanacKeys::getLiveDevice();
     $skip_sync = $this->shouldSkipReadSynchronization();
     if ($this->shouldProxy()) {
         $command = $this->getProxyCommand();
         if ($device) {
             $this->writeClusterEngineLogMessage(pht("# Fetch received by \"%s\", forwarding to cluster host.\n", $device->getName()));
         }
     } else {
         $command = csprintf('git-upload-pack -- %s', $repository->getLocalPath());
         if (!$skip_sync) {
             $cluster_engine = id(new DiffusionRepositoryClusterEngine())->setViewer($viewer)->setRepository($repository)->setLog($this)->synchronizeWorkingCopyBeforeRead();
             if ($device) {
                 $this->writeClusterEngineLogMessage(pht("# Cleared to fetch on cluster host \"%s\".\n", $device->getName()));
             }
         }
     }
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment());
     $err = $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute();
     if (!$err) {
         $this->waitForGitClient();
     }
     return $err;
 }
 protected function executeRepositoryOperations()
 {
     $repository = $this->getRepository();
     $args = $this->getArgs();
     if (!$args->getArg('stdio')) {
         throw new Exception(pht('Expected `%s`!', 'hg ... --stdio'));
     }
     if ($args->getArg('command') !== array('serve')) {
         throw new Exception(pht('Expected `%s`!', 'hg ... serve'));
     }
     if ($this->shouldProxy()) {
         $command = $this->getProxyCommand();
     } else {
         $command = csprintf('hg -R %s serve --stdio', $repository->getLocalPath());
     }
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment());
     $io_channel = $this->getIOChannel();
     $protocol_channel = new DiffusionMercurialWireClientSSHProtocolChannel($io_channel);
     $err = id($this->newPassthruCommand())->setIOChannel($protocol_channel)->setCommandChannelFromExecFuture($future)->setWillWriteCallback(array($this, 'willWriteMessageCallback'))->execute();
     // TODO: It's apparently technically possible to communicate errors to
     // Mercurial over SSH by writing a special "\n<error>\n-\n" string. However,
     // my attempt to implement that resulted in Mercurial closing the socket and
     // then hanging, without showing the error. This might be an issue on our
     // side (we need to close our half of the socket?), or maybe the code
     // for this in Mercurial doesn't actually work, or maybe something else
     // is afoot. At some point, we should look into doing this more cleanly.
     // For now, when we, e.g., reject writes for policy reasons, the user will
     // see "abort: unexpected response: empty string" after the diagnostically
     // useful, e.g., "remote: This repository is read-only over SSH." message.
     if (!$err && $this->didSeeWrite) {
         $repository->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY);
     }
     return $err;
 }
 private function executeRepositoryCommand($command)
 {
     $repository = $this->getRepository();
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment());
     return $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute();
 }
 protected function executeRepositoryOperations()
 {
     $args = $this->getArgs();
     $path = head($args->getArg('dir'));
     $repository = $this->loadRepository($path);
     $command = csprintf('git-upload-pack -- %s', $repository->getLocalPath());
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment());
     $err = $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute();
     if (!$err) {
         $this->waitForGitClient();
     }
     return $err;
 }
 protected function executeRepositoryOperations()
 {
     $args = $this->getArgs();
     if (!$args->getArg('tunnel')) {
         throw new Exception('Expected `svnserve -t`!');
     }
     $command = csprintf('svnserve -t --tunnel-user=%s', $this->getUser()->getUsername());
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = new ExecFuture('%C', $command);
     $this->inProtocol = new DiffusionSubversionWireProtocol();
     $this->outProtocol = new DiffusionSubversionWireProtocol();
     $err = id($this->newPassthruCommand())->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->setWillWriteCallback(array($this, 'willWriteMessageCallback'))->setWillReadCallback(array($this, 'willReadMessageCallback'))->execute();
     if (!$err && $this->didSeeWrite) {
         $this->getRepository()->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY);
     }
     return $err;
 }
 protected function executeRepositoryOperations()
 {
     $args = $this->getArgs();
     $path = head($args->getArg('dir'));
     $repository = $this->loadRepository($path);
     // This is a write, and must have write access.
     $this->requireWriteAccess();
     $command = csprintf('git-receive-pack %s', $repository->getLocalPath());
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment());
     $err = $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute();
     if (!$err) {
         $repository->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY);
         $this->waitForGitClient();
     }
     return $err;
 }
 protected function executeRepositoryOperations()
 {
     $repository = $this->getRepository();
     $viewer = $this->getUser();
     $device = AlmanacKeys::getLiveDevice();
     $skip_sync = $this->shouldSkipReadSynchronization();
     $is_proxy = $this->shouldProxy();
     if ($is_proxy) {
         $command = $this->getProxyCommand();
         if ($device) {
             $this->writeClusterEngineLogMessage(pht("# Fetch received by \"%s\", forwarding to cluster host.\n", $device->getName()));
         }
     } else {
         $command = csprintf('git-upload-pack -- %s', $repository->getLocalPath());
         if (!$skip_sync) {
             $cluster_engine = id(new DiffusionRepositoryClusterEngine())->setViewer($viewer)->setRepository($repository)->setLog($this)->synchronizeWorkingCopyBeforeRead();
             if ($device) {
                 $this->writeClusterEngineLogMessage(pht("# Cleared to fetch on cluster host \"%s\".\n", $device->getName()));
             }
         }
     }
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $pull_event = $this->newPullEvent();
     $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment());
     $err = $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute();
     if ($err) {
         $pull_event->setResultType('error')->setResultCode($err);
     } else {
         $pull_event->setResultType('pull')->setResultCode(0);
     }
     // TODO: Currently, when proxying, we do not write a log on the proxy.
     // Perhaps we should write a "proxy log". This is not very useful for
     // statistics or auditing, but could be useful for diagnostics. Marking
     // the proxy logs as proxied (and recording devicePHID on all logs) would
     // make differentiating between these use cases easier.
     if (!$is_proxy) {
         $pull_event->save();
     }
     if (!$err) {
         $this->waitForGitClient();
     }
     return $err;
 }
 private function serveMercurialRequest(PhabricatorRepository $repository, PhabricatorUser $viewer)
 {
     $request = $this->getRequest();
     $bin = Filesystem::resolveBinary('hg');
     if (!$bin) {
         throw new Exception(pht('Unable to find `%s` in %s!', 'hg', '$PATH'));
     }
     $env = $this->getCommonEnvironment($viewer);
     $input = PhabricatorStartup::getRawInput();
     $cmd = $request->getStr('cmd');
     $args = $this->getMercurialArguments();
     $args = $this->formatMercurialArguments($cmd, $args);
     if (strlen($input)) {
         $input = strlen($input) . "\n" . $input . "0\n";
     }
     $command = csprintf('%s serve --stdio', $bin);
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command))->setEnv($env, true)->setCWD($repository->getLocalPath())->write("{$cmd}\n{$args}{$input}")->resolve();
     if ($err) {
         return new PhabricatorVCSResponse(500, pht('Error %d: %s', $err, $stderr));
     }
     if ($cmd == 'getbundle' || $cmd == 'changegroup' || $cmd == 'changegroupsubset') {
         // We're not completely sure that "changegroup" and "changegroupsubset"
         // actually work, they're for very old Mercurial.
         $body = gzcompress($stdout);
     } else {
         if ($cmd == 'unbundle') {
             // This includes diagnostic information and anything echoed by commit
             // hooks. We ignore `stdout` since it just has protocol garbage, and
             // substitute `stderr`.
             $body = strlen($stderr) . "\n" . $stderr;
         } else {
             list($length, $body) = explode("\n", $stdout, 2);
             if ($cmd == 'capabilities') {
                 $body = DiffusionMercurialWireProtocol::filterBundle2Capability($body);
             }
         }
     }
     return id(new DiffusionMercurialResponse())->setContent($body);
 }
 public function newFuture()
 {
     $argv = $this->newCommandArgv();
     $env = $this->newCommandEnvironment();
     if ($this->getSudoAsDaemon()) {
         $command = call_user_func_array('csprintf', $argv);
         $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
         $argv = array('%C', $command);
     }
     if ($this->getPassthru()) {
         $future = newv('PhutilExecPassthru', $argv);
     } else {
         $future = newv('ExecFuture', $argv);
     }
     $future->setEnv($env);
     return $future;
 }
 protected function executeRepositoryOperations()
 {
     $repository = $this->getRepository();
     $args = $this->getArgs();
     if (!$args->getArg('tunnel')) {
         throw new Exception(pht('Expected `%s`!', 'svnserve -t'));
     }
     if ($this->shouldProxy()) {
         $command = $this->getProxyCommand();
         $this->isProxying = true;
         $cwd = null;
     } else {
         $command = csprintf('svnserve -t --tunnel-user=%s', $this->getUser()->getUsername());
         $cwd = PhabricatorEnv::getEmptyCWD();
     }
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = new ExecFuture('%C', $command);
     // If we're receiving a commit, svnserve will fail to execute the commit
     // hook with an unhelpful error if the CWD isn't readable by the user we
     // are sudoing to. Switch to a readable, empty CWD before running
     // svnserve. See T10941.
     if ($cwd !== null) {
         $future->setCWD($cwd);
     }
     $this->inProtocol = new DiffusionSubversionWireProtocol();
     $this->outProtocol = new DiffusionSubversionWireProtocol();
     $this->command = id($this->newPassthruCommand())->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->setWillWriteCallback(array($this, 'willWriteMessageCallback'))->setWillReadCallback(array($this, 'willReadMessageCallback'));
     $this->command->setPauseIOReads(true);
     $err = $this->command->execute();
     if (!$err && $this->didSeeWrite) {
         $this->getRepository()->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY);
     }
     return $err;
 }