Created
September 11, 2020 17:01
-
-
Save divinity76/f7414ced27d30a875d8ee8171add7c05 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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)