<?php
// 允许的 CORS 配置
$allowedOrigins = ["*"];
$allowedMethods = "POST, OPTIONS";
$allowedHeaders = "Content-Type";
// 创建 TCP Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("Socket creation failed: " . socket_strerror(socket_last_error()) . "\n");
}
// 绑定 IP 和端口
if (!socket_bind($socket, '0.0.0.0', 9502)) {
die("Socket bind failed: " . socket_strerror(socket_last_error()) . "\n");
}
// 开始监听
if (!socket_listen($socket)) {
die("Socket listen failed: " . socket_strerror(socket_last_error()) . "\n");
}
echo "HTTP server running on http://0.0.0.0:9502\n";
// 注册信号处理器,防止僵尸进程
pcntl_signal(SIGCHLD, function($signo) {
while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
});
while (true) {
// 接受客户端连接
$client = socket_accept($socket);
if ($client === false) {
echo "Socket accept failed: " . socket_strerror(socket_last_error()) . "\n";
continue;
}
// 创建子进程处理请求
$pid = pcntl_fork();
if ($pid == -1) {
die("Fork failed");
} elseif ($pid) {
// 父进程:关闭客户端连接并继续监听
socket_close($client);
continue;
} else {
// 子进程:处理请求
handleRequest($client);
exit(0); // 处理完成后退出子进程
}
}
// 关闭 Socket(通常不会执行到这里)
socket_close($socket);
/**
* 处理客户端请求
*/
function handleRequest($client) {
global $allowedOrigins, $allowedMethods, $allowedHeaders;
// 读取 HTTP 请求(最多读取 8KB)
$request = socket_read($client, 8192);
if ($request === false) {
echo "Socket read failed: " . socket_strerror(socket_last_error()) . "\n";
return;
}
// 解析 HTTP 请求方法
$method = strtoupper(explode(' ', $request)[0]);
// 处理 OPTIONS 预检请求
if ($method === 'OPTIONS') {
$response = "HTTP/1.1 204 No Content\r\n";
$response .= "Access-Control-Allow-Origin: " . implode(", ", $allowedOrigins) . "\r\n";
$response .= "Access-Control-Allow-Methods: $allowedMethods\r\n";
$response .= "Access-Control-Allow-Headers: $allowedHeaders\r\n";
$response .= "Connection: close\r\n\r\n";
socket_write($client, $response);
socket_close($client);
return;
}
// 处理 POST 请求
if ($method === 'POST') {
// 解析 POST Body
$body = '';
$lines = explode("\r\n", $request);
$bodyStart = false;
foreach ($lines as $line) {
if ($line === '') {
$bodyStart = true;
continue;
}
if ($bodyStart) {
$body .= $line . "\n";
}
}
// 解析 JSON 数据
$data = json_decode(trim($body), true);
$cmd = sprintf("/root/selfTest/update_test.sh %s", $data['cmd']) ?? 'ls /root';
// 使用 proc_open 执行命令(非阻塞读取输出)
$descriptors = [
0 => ["pipe", "r"], // 标准输入
1 => ["pipe", "w"], // 标准输出
2 => ["pipe", "w"], // 标准错误
];
$process = proc_open($cmd, $descriptors, $pipes);
if (!is_resource($process)) {
$response = buildResponse(500, "Failed to execute command.");
socket_write($client, $response);
socket_close($client);
return;
}
// 设置管道为非阻塞模式
stream_set_blocking($pipes[1], false);
stream_set_blocking($pipes[2], false);
// 实时读取输出
$output = '';
$stderr = '';
do {
$read = [$pipes[1], $pipes[2]];
$write = null;
$except = null;
if (stream_select($read, $write, $except, 0) > 0) {
foreach ($read as $stream) {
if ($stream === $pipes[1]) {
$output .= fread($stream, 8192);
} elseif ($stream === $pipes[2]) {
$stderr .= fread($stream, 8192);
}
}
}
} while (proc_get_status($process)['running']);
// 关闭管道和进程
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
$exitCode = proc_close($process);
// 构建响应
$response = "HTTP/1.1 200 OK\r\n";
$response .= "Access-Control-Allow-Origin: *\r\n";
$response .= "Content-Type: application/json\r\n";
$response .= "Connection: close\r\n\r\n";
$response .= json_encode([
'command' => $cmd,
'exit_code' => $exitCode,
'output' => $output,
'error' => $stderr,
]);
socket_write($client, $response);
socket_close($client);
} else {
// 其他请求方法返回错误
$response = buildResponse(405, "Only POST requests are supported.");
socket_write($client, $response);
socket_close($client);
}
}
/**
* 构建带 CORS 头的错误响应
*/
function buildResponse($statusCode, $message) {
$statusText = [
200 => 'OK',
204 => 'No Content',
400 => 'Bad Request',
405 => 'Method Not Allowed',
500 => 'Internal Server Error',
];
$response = "HTTP/1.1 $statusCode {$statusText[$statusCode]}\r\n";
$response .= "Access-Control-Allow-Origin: *\r\n";
$response .= "Content-Type: text/plain\r\n";
$response .= "Connection: close\r\n\r\n";
$response .= $message;
return $response;
}