mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-27 07:44:04 +02:00
RemoteCommandFile: Replace exec with proc_open
This commit is contained in:
parent
de61e0c703
commit
3a00923116
@ -73,6 +73,20 @@ class RemoteCommandFile implements CommandTransportInterface
|
|||||||
*/
|
*/
|
||||||
protected $renderer;
|
protected $renderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH subprocess pipes
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $sshPipes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH subprocess
|
||||||
|
*
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
protected $sshProcess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new remote command file command transport
|
* Create a new remote command file command transport
|
||||||
*/
|
*/
|
||||||
@ -274,26 +288,198 @@ class RemoteCommandFile implements CommandTransportInterface
|
|||||||
$this->port,
|
$this->port,
|
||||||
$this->path
|
$this->path
|
||||||
);
|
);
|
||||||
$ssh = sprintf('ssh -o BatchMode=yes -p %u', $this->port);
|
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
|
// -o BatchMode=yes for disabling interactive authentication methods
|
||||||
|
|
||||||
if (isset($this->user)) {
|
if (isset($this->user)) {
|
||||||
$ssh .= sprintf(' -l %s', escapeshellarg($this->user));
|
$cmd .= ' -l ' . escapeshellarg($this->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->privateKey)) {
|
if (isset($this->privateKey)) {
|
||||||
$ssh .= sprintf(' -o StrictHostKeyChecking=no -i %s', escapeshellarg($this->privateKey));
|
// TODO: StrictHostKeyChecking=no for compat only, must be removed
|
||||||
|
$cmd .= ' -o StrictHostKeyChecking=no'
|
||||||
|
. ' -i ' . escapeshellarg($this->privateKey);
|
||||||
}
|
}
|
||||||
$ssh .= sprintf(
|
|
||||||
' %s "echo %s > %s" 2>&1', // Redirect stderr to stdout
|
$cmd .= sprintf(
|
||||||
|
' %s "cat > %s"',
|
||||||
escapeshellarg($this->host),
|
escapeshellarg($this->host),
|
||||||
escapeshellarg($commandString),
|
|
||||||
escapeshellarg($this->path)
|
escapeshellarg($this->path)
|
||||||
);
|
);
|
||||||
exec($ssh, $output, $status);
|
|
||||||
if ($status !== 0) {
|
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'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fclose($this->sshPipes[0]);
|
||||||
|
$readBuffer = $read = array($this->sshPipes[2]);
|
||||||
|
$write = null;
|
||||||
|
$except = null;
|
||||||
|
$stderr = '';
|
||||||
|
while (
|
||||||
|
false !== (stream_select($readBuffer, $write, $exceptBuffer, 0, 20000))
|
||||||
|
&& $this->getSshProcessStatus('running')
|
||||||
|
) {
|
||||||
|
if (! empty($readBuffer)) {
|
||||||
|
$stderr .= stream_get_contents($readBuffer[0]);
|
||||||
|
}
|
||||||
|
// Reset buffer
|
||||||
|
$readBuffer = $read;
|
||||||
|
}
|
||||||
|
if (! empty($stderr)) {
|
||||||
|
throw new CommandTransportException('Can\'t send external Icinga command: %s', trim($stderr));
|
||||||
|
}
|
||||||
|
} 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(
|
throw new CommandTransportException(
|
||||||
'Can\'t send external Icinga command: %s',
|
'Can\'t send external Icinga command, failed to fork SSH'
|
||||||
implode(' ', $output)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)) {
|
||||||
|
if (is_resource($this->sshPipes[0])) {
|
||||||
|
fclose($this->sshPipes[0]);
|
||||||
|
}
|
||||||
|
fclose($this->sshPipes[1]);
|
||||||
|
fclose($this->sshPipes[2]);
|
||||||
|
|
||||||
|
proc_close($this->sshProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user