<?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
This comment has been minimized.
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)