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; }