From 8ca6cc32b2c789a3149861159ad258f2cb9491e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:39:39 +0200 Subject: Adding upstream version 2.11.4. Signed-off-by: Daniel Baumann --- .../Command/Transport/RemoteCommandFile.php | 465 +++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php (limited to 'modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php') diff --git a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php new file mode 100644 index 0000000..5426bb9 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php @@ -0,0 +1,465 @@ +renderer = new IcingaCommandFileCommandRenderer(); + } + + /** + * Set the name of the Icinga instance this transport will transfer commands to + * + * @param string $name + * + * @return $this + */ + public function setInstance($name) + { + $this->instanceName = $name; + return $this; + } + + /** + * Return the name of the Icinga instance this transport will transfer commands to + * + * @return string + */ + public function getInstance() + { + return $this->instanceName; + } + + /** + * Set the remote host + * + * @param string $host + * + * @return $this + */ + public function setHost($host) + { + $this->host = (string) $host; + return $this; + } + + /** + * Get the remote host + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the port to connect to on the remote host + * + * @param int $port + * + * @return $this + */ + public function setPort($port) + { + $this->port = (int) $port; + return $this; + } + + /** + * Get the port to connect on the remote host + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Set the user to log in as on the remote host + * + * @param string $user + * + * @return $this + */ + public function setUser($user) + { + $this->user = (string) $user; + return $this; + } + + /** + * Get the user to log in as on the remote host + * + * Defaults to current PHP process' user + * + * @return string|null + */ + public function getUser() + { + return $this->user; + } + + /** + * Set the path to the private key file + * + * @param string $privateKey + * + * @return $this + */ + public function setPrivateKey($privateKey) + { + $this->privateKey = (string) $privateKey; + return $this; + } + + /** + * Get the path to the private key + * + * @return string + */ + public function getPrivateKey() + { + return $this->privateKey; + } + + /** + * Use a given resource to set the user and the key + * + * @param string + * + * @throws ConfigurationError + */ + public function setResource($resource = null) + { + $config = ResourceFactory::getResourceConfig($resource); + + if (! isset($config->user)) { + throw new ConfigurationError( + t("Can't send external Icinga Command. Remote user is missing") + ); + } + if (! isset($config->private_key)) { + throw new ConfigurationError( + t("Can't send external Icinga Command. The private key for the remote user is missing") + ); + } + + $this->setUser($config->user); + $this->setPrivateKey($config->private_key); + } + + /** + * Set the path to the Icinga command file on the remote host + * + * @param string $path + * + * @return $this + */ + public function setPath($path) + { + $this->path = (string) $path; + return $this; + } + + /** + * Get the path to the Icinga command file on the remote host + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Write the command to the Icinga command file on the remote host + * + * @param IcingaCommand $command + * @param int|null $now + * + * @throws ConfigurationError + * @throws CommandTransportException + */ + public function send(IcingaCommand $command, $now = null) + { + if (! isset($this->path)) { + throw new ConfigurationError( + 'Can\'t send external Icinga Command. Path to the remote command file is missing' + ); + } + if (! isset($this->host)) { + throw new ConfigurationError('Can\'t send external Icinga Command. Remote host is missing'); + } + $commandString = $this->renderer->render($command, $now); + Logger::debug( + 'Sending external Icinga command "%s" to the remote command file "%s:%u%s"', + $commandString, + $this->host, + $this->port, + $this->path + ); + return $this->sendCommandString($commandString); + } + + /** + * Get the SSH command + * + * @return string + */ + protected function sshCommand() + { + $cmd = sprintf( + 'exec ssh -o BatchMode=yes -p %u', + $this->port + ); + // -o BatchMode=yes for disabling interactive authentication methods + + if (isset($this->user)) { + $cmd .= ' -l ' . escapeshellarg($this->user); + } + + if (isset($this->privateKey)) { + // TODO: StrictHostKeyChecking=no for compat only, must be removed + $cmd .= ' -o StrictHostKeyChecking=no' + . ' -i ' . escapeshellarg($this->privateKey); + } + + $cmd .= sprintf( + ' %s "cat > %s"', + escapeshellarg($this->host), + escapeshellarg($this->path) + ); + + return $cmd; + } + + /** + * Send the command over SSH + * + * @param string $commandString + * + * @throws CommandTransportException + */ + protected function sendCommandString($commandString) + { + if ($this->isSshAlive()) { + $ret = fwrite($this->sshPipes[0], $commandString . "\n"); + if ($ret === false) { + $this->throwSshFailure('Cannot write to the remote command pipe'); + } elseif ($ret !== strlen($commandString) + 1) { + $this->throwSshFailure( + 'Failed to write the whole command to the remote command pipe' + ); + } + } else { + $this->throwSshFailure(); + } + } + + /** + * Get the pipes of the SSH subprocess + * + * @return array + */ + protected function getSshPipes() + { + if ($this->sshPipes === null) { + $this->forkSsh(); + } + + return $this->sshPipes; + } + + /** + * Get the SSH subprocess + * + * @return resource + */ + protected function getSshProcess() + { + if ($this->sshProcess === null) { + $this->forkSsh(); + } + + return $this->sshProcess; + } + + /** + * Get the status of the SSH subprocess + * + * @param string $what + * + * @return mixed + */ + protected function getSshProcessStatus($what = null) + { + $status = proc_get_status($this->getSshProcess()); + if ($what === null) { + return $status; + } else { + return $status[$what]; + } + } + + /** + * Get whether the SSH subprocess is alive + * + * @return bool + */ + protected function isSshAlive() + { + return $this->getSshProcessStatus('running'); + } + + /** + * Fork SSH subprocess + * + * @throws CommandTransportException If fork fails + */ + protected function forkSsh() + { + $descriptors = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ); + + $this->sshProcess = proc_open($this->sshCommand(), $descriptors, $this->sshPipes); + + if (! is_resource($this->sshProcess)) { + throw new CommandTransportException( + 'Can\'t send external Icinga command: Failed to fork SSH' + ); + } + } + + /** + * Read from STDERR + * + * @return string + */ + protected function readStderr() + { + return stream_get_contents($this->sshPipes[2]); + } + + /** + * Throw SSH failure + * + * @param string $msg + * + * @throws CommandTransportException + */ + protected function throwSshFailure($msg = 'Can\'t send external Icinga command') + { + throw new CommandTransportException( + '%s: %s', + $msg, + $this->readStderr() . var_export($this->getSshProcessStatus(), true) + ); + } + + /** + * Close SSH pipes and SSH subprocess + */ + public function __destruct() + { + if (is_resource($this->sshProcess)) { + fclose($this->sshPipes[0]); + fclose($this->sshPipes[1]); + fclose($this->sshPipes[2]); + + proc_close($this->sshProcess); + } + } +} -- cgit v1.2.3