Skip to content

Instantly share code, notes, and snippets.

@divinity76
Created September 11, 2020 17:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save divinity76/f7414ced27d30a875d8ee8171add7c05 to your computer and use it in GitHub Desktop.
Save divinity76/f7414ced27d30a875d8ee8171add7c05 to your computer and use it in GitHub Desktop.
<?php
declare(strict_types = 1);
$password = "CHANGE_ME";
if (! hash_equals($password, (string) ($_REQUEST['pass'] ?? ""))) {
http_response_code(400);
die("wrong pass");
}
header("Content-Type: text/plain; charset=utf-8");
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('html_errors', '0');
$cmd = (string) ($_REQUEST["cmd"] ?? "");
echo "cmd: ";
var_dump($cmd);
$cmd = "/bin/bash -c " . escapeshellarg($cmd);
echo "cmd: ";
var_dump($cmd);
$child_stdout = tmpfile();
$child_stderr = tmpfile();
$descriptorspec = array(
// by default the child inherit our stdin if we don't create 1,
// we don't want that so we create a stdin just so we can close it
0 => array(
"pipe",
"rb"
),
// stdout
1 => array(
"file",
stream_get_meta_data($child_stdout)["uri"],
"wb"
),
// stderr
2 => array(
"file",
stream_get_meta_data($child_stderr)["uri"],
"wb"
)
);
$pipes = [];
$data = [];
$starttime = microtime(true);
$proc = proc_open($cmd, $descriptorspec, $pipes, __DIR__);
fclose($pipes[0]);
unset($pipes[0], $pipes);
$timeout_seconds = 5;
$timeout = microtime(true) + ($timeout_seconds);
$exitcode = null;
while (($status = proc_get_status($proc))['running']) {
if (microtime(true) > $timeout) {
// timeout,
// tell it to quit, give it 1 second to respond, and if it still
// hasn't quit within 1 second, we kill it
$data["error-terminated-by-timeout"] = true;
$SIGQUIT = 3;
proc_terminate($proc, $SIGQUIT);
usleep(500 * 1000);
$SIGTERM = 15;
proc_terminate($proc, $SIGTERM);
usleep(500 * 1000);
$SIGKILL = 9;
// kill it:
proc_terminate($proc, $SIGKILL);
}
// sleep 10 ms (meaning we check 100 times per second..)
usleep(10 * 1000);
}
$data["runtime_seconds"] = microtime(true) - $starttime;
$exitcode = $status['exitcode'];
proc_close($proc);
$data["exitcode"] = $exitcode;
$data["stdout"] = stream_get_contents($child_stdout);
$data["stderr"] = stream_get_contents($child_stderr);
fclose($child_stdout);
fclose($child_stderr);
print_r($data);
@divinity76
Copy link
Author

i used proc_open() instead of passthru()/system() etc, because they don’t give us access to stderr, AND they don’t give us a way to
”timeout the process after X seconds if it hasn’t finished yet”, but proc_open gives us clean access to both stderr and stdout, AND it gives us a way to

“timeout after X seconds”

i know we can get access to stderr by adding system($cmd." 2>&1”) , but then stdout and stderr will be mixed,

and we wouldn’t know what output actually came from stdout, and what came from stderr (but proc_open doesn’t have that problem to begin with, only shell_exec() / system() / passthru() / exec() & co actually has that problem)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment