1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
<?php
/* Icinga PDF Export | (c) 2018 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Pdfexport;
class ShellCommand
{
/** @var string Command to execute */
protected $command;
/** @var int Exit code of the command */
protected $exitCode;
/** @var resource Process resource */
protected $resource;
/**
* Create a new command
*
* @param string $command The command to execute
* @param bool $escape Whether to escape the command
*/
public function __construct($command, $escape = true)
{
$command = (string) $command;
$this->command = $escape ? escapeshellcmd($command) : $command;
}
/**
* Get the exit code of the command
*
* @return int
*/
public function getExitCode()
{
return $this->exitCode;
}
/**
* Get the status of the command
*
* @return object
*/
public function getStatus()
{
$status = (object) proc_get_status($this->resource);
if ($status->running === false && $this->exitCode === null) {
// The exit code is only valid the first time proc_get_status is
// called in terms of running false, hence we capture it
$this->exitCode = $status->exitcode;
}
return $status;
}
/**
* Execute the command
*
* @return object
*
* @throws \Exception
*/
public function execute()
{
if ($this->resource !== null) {
throw new \Exception('Command already started');
}
$descriptors = [
['pipe', 'r'], // stdin
['pipe', 'w'], // stdout
['pipe', 'w'] // stderr
];
$this->resource = proc_open(
$this->command,
$descriptors,
$pipes
);
if (! is_resource($this->resource)) {
throw new \Exception(sprintf(
"Can't fork '%s'",
$this->command
));
}
$namedpipes = (object) [
'stdin' => &$pipes[0],
'stdout' => &$pipes[1],
'stderr' => &$pipes[2]
];
fclose($namedpipes->stdin);
$read = [$namedpipes->stderr, $namedpipes->stdout];
$origRead = $read;
$write = null; // stdin not handled
$except = null;
$stdout = '';
$stderr = '';
stream_set_blocking($namedpipes->stdout, 0); // non-blocking
stream_set_blocking($namedpipes->stderr, 0);
while (stream_select($read, $write, $except, 0, 20000) !== false) {
foreach ($read as $pipe) {
if ($pipe === $namedpipes->stdout) {
$stdout .= stream_get_contents($pipe);
}
if ($pipe === $namedpipes->stderr) {
$stderr .= stream_get_contents($pipe);
}
}
foreach ($origRead as $i => $str) {
if (feof($str)) {
unset($origRead[$i]);
}
}
if (empty($origRead)) {
break;
}
// Reset pipes
$read = $origRead;
}
fclose($namedpipes->stderr);
fclose($namedpipes->stdout);
$exitCode = proc_close($this->resource);
if ($this->exitCode === null) {
$this->exitCode = $exitCode;
}
$this->resource = null;
return (object) [
'stdout' => $stdout,
'stderr' => $stderr
];
}
}
|