You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
7.6 KiB
261 lines
7.6 KiB
<?php |
|
/* |
|
* @Author: witersen |
|
* @Date: 2022-04-24 23:37:06 |
|
* @LastEditors: witersen |
|
* @LastEditTime: 2022-04-28 02:22:01 |
|
* @Description: QQ:1801168257 |
|
*/ |
|
|
|
ini_set('display_errors', '1'); |
|
|
|
error_reporting(E_ALL); |
|
|
|
define('BASE_PATH', __DIR__); |
|
|
|
require_once BASE_PATH . '/../config/subversion.config.php'; |
|
require_once BASE_PATH . '/../config/daemon.config.php'; |
|
|
|
class Daemon |
|
{ |
|
private $pidFile; |
|
private $workMode; |
|
private $scripts = [ |
|
'start', |
|
'stop', |
|
'console' |
|
]; |
|
|
|
function __construct() |
|
{ |
|
$this->pidFile = dirname(__FILE__) . '/svnadmind.pid'; |
|
} |
|
|
|
/** |
|
* 将程序变为守护进程 |
|
*/ |
|
private function initDaemon() |
|
{ |
|
$pid = pcntl_fork(); |
|
if ($pid < 0) { |
|
exit('pcntl_fork 错误'); |
|
} elseif ($pid > 0) { |
|
exit(); |
|
} |
|
$sid = posix_setsid(); |
|
if (!$sid) { |
|
exit('posix_setsid 错误'); |
|
} |
|
$pid = pcntl_fork(); |
|
if ($pid < 0) { |
|
exit('pcntl_fork 错误'); |
|
} elseif ($pid > 0) { |
|
exit(); |
|
} |
|
chdir('/'); |
|
umask(0); |
|
if (defined('STDIN')) { |
|
fclose(STDIN); |
|
} |
|
if (defined('STDOUT')) { |
|
fclose(STDOUT); |
|
} |
|
if (defined('STDERR')) { |
|
fclose(STDERR); |
|
} |
|
file_put_contents($this->pidFile, getmypid()); |
|
$this->initSocket(); |
|
} |
|
|
|
/** |
|
* 监听指定端口 |
|
*/ |
|
private function initSocket() |
|
{ |
|
//创建套接字 |
|
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or exit('启动失败:socket_create 错误' . PHP_EOL); |
|
|
|
//绑定地址和端口 |
|
socket_bind($socket, IPC_ADDRESS, IPC_PORT) or exit('启动失败:socket_bind 错误,可能是由于频繁启动,端口未释放,请稍后重试或检查端口冲突' . PHP_EOL); |
|
|
|
//设置可重复使用端口号 |
|
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); |
|
|
|
//监听 设置并发队列的最大长度 |
|
socket_listen($socket, SOCKET_LISTEN_BACKLOG); |
|
|
|
while (true) { |
|
//非阻塞式回收僵尸进程 |
|
pcntl_wait($status, WNOHANG); |
|
|
|
$client = socket_accept($socket) or exit('启动失败:socket_accept 错误' . PHP_EOL); |
|
|
|
//非阻塞式回收僵尸进程 |
|
pcntl_wait($status, WNOHANG); |
|
|
|
$pid = pcntl_fork(); |
|
if ($pid == -1) { |
|
exit('启动失败:pcntl_fork 错误' . PHP_EOL); |
|
} else if ($pid == 0) { |
|
$this->handleRequest($client); |
|
} else { |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* socket程序接收和处理请求 |
|
*/ |
|
private function handleRequest($client) |
|
{ |
|
//接收客户端发送的数据 |
|
$data = socket_read($client, SOCKET_READ_LENGTH); |
|
|
|
//console |
|
$this->workMode == 'console' ? print_r(PHP_EOL . '---------receive---------' . PHP_EOL . $data . PHP_EOL) : ''; |
|
|
|
if (trim($data) != '') { |
|
/** |
|
* shell_exec方法拿不到执行指令的错误抛出信息 |
|
*/ |
|
// $result = shell_exec($data); |
|
|
|
/** |
|
* passthru会将所有的结果(包括正确的和错误的抛出信息输出) |
|
* 可以使用 ob_start ob_get_contents ob_end_clean 拿到缓冲区的内容 |
|
* |
|
* 此方法在console调试模式下会导致 $result 变量拿不到值; |
|
* 但是在daemon模式下 $result 变量可以拿到值(猜测是输入到终端与输出到缓冲区的影响) |
|
*/ |
|
ob_start(); |
|
passthru($data); |
|
$result = ob_get_contents(); |
|
ob_end_clean(); |
|
} else { |
|
//探测程序会发送空信息 |
|
$result = ''; |
|
} |
|
|
|
//console |
|
$this->workMode == 'console' ? print_r(PHP_EOL . '---------result---------' . PHP_EOL . $result . PHP_EOL) : ''; |
|
|
|
//处理没有返回内容的情况 否则 socket_write 遇到空内容会报错 |
|
$result = trim($result) == '' ? ISNULL : trim($result); |
|
|
|
//将结果返回给客户端 |
|
socket_write($client, $result, strlen($result)) or die('启动失败:socket_write 错误' . PHP_EOL); |
|
|
|
//关闭会话 |
|
socket_close($client); |
|
|
|
//退出进程 |
|
exit(); |
|
} |
|
|
|
/** |
|
* 检查操作系统是否符合要求 |
|
*/ |
|
private function checkSysType() |
|
{ |
|
if (PHP_OS != 'Linux') { |
|
exit('启动失败:当前操作系统不为Linux' . PHP_EOL); |
|
} |
|
if (file_exists('/etc/redhat-release')) { |
|
$info = file_get_contents('/etc/redhat-release'); |
|
if (!strstr($info, 'CentOS') && (strstr($info, '8.') || strstr($info, '7.'))) { |
|
exit('启动失败:当前仅支持 CentOS 7和 CentOS8 操作系统' . PHP_EOL); |
|
} |
|
return; |
|
} |
|
exit('启动失败:当前仅支持 CentOS 7和 CentOS8 操作系统' . PHP_EOL); |
|
} |
|
|
|
/** |
|
* 检查php版本是否符合要求 |
|
*/ |
|
private function checkPhpVersion() |
|
{ |
|
if (PHP_VERSION < Required_PHP_VERSION) { |
|
exit('启动失败:当前的PHP版本为:' . PHP_VERSION . ',要求的最低PHP版本为:' . Required_PHP_VERSION . PHP_EOL); |
|
} |
|
} |
|
|
|
/** |
|
* 检查需要的函数是否被禁用 |
|
*/ |
|
private function checkDisabledFunction() |
|
{ |
|
$disabled_function = explode(',', ini_get('disable_functions')); |
|
$cli_needed_function = unserialize(CLI_NEEDED_FUNCTION); |
|
foreach ($cli_needed_function as $key => $value) { |
|
if (!in_array($value, $disabled_function)) { |
|
unset($cli_needed_function[$key]); |
|
} |
|
} |
|
if (!empty($cli_needed_function)) { |
|
exit('启动失败:需要的以下函数被禁用:' . PHP_EOL . implode(' ', $cli_needed_function) . PHP_EOL); |
|
} |
|
} |
|
|
|
/** |
|
* 以守护进程模式工作 |
|
*/ |
|
private function startDaemon() |
|
{ |
|
if (file_exists($this->pidFile)) { |
|
$pid = file_get_contents($this->pidFile); |
|
$result = trim(shell_exec("ps -ax | awk '{ print $1 }' | grep -e \"^$pid$\"")); |
|
if (strstr($result, $pid)) { |
|
exit('程序正在运行中' . PHP_EOL); |
|
} |
|
} |
|
$this->initDaemon(); |
|
} |
|
|
|
/** |
|
* 关闭守护进程 |
|
*/ |
|
private function stopDaemon() |
|
{ |
|
if (file_exists($this->pidFile)) { |
|
$pid = file_get_contents($this->pidFile); |
|
posix_kill((int)$pid, 9); |
|
unlink($this->pidFile); |
|
} |
|
} |
|
|
|
/** |
|
* 以控制台模式工作 用于调试 |
|
*/ |
|
private function startConsole() |
|
{ |
|
$this->initSocket(); |
|
} |
|
|
|
public function run($argv) |
|
{ |
|
$this->checkSysType(); |
|
$this->checkPhpVersion(); |
|
$this->checkDisabledFunction(); |
|
if (isset($argv[1])) { |
|
$this->workMode = $argv[1]; |
|
if (!in_array($this->workMode, $this->scripts)) { |
|
exit('用法:php svnadmin.php [start | stop | console]' . PHP_EOL); |
|
} |
|
if ($this->workMode == 'start') { |
|
$this->startDaemon(); |
|
} else if ($this->workMode == 'stop') { |
|
$this->stopDaemon(); |
|
} else if ($this->workMode == 'console') { |
|
$this->startConsole(); |
|
} |
|
} else { |
|
exit('用法:php svnadmin.php [start | stop | console]' . PHP_EOL); |
|
} |
|
} |
|
} |
|
|
|
if (preg_match('/cli/i', php_sapi_name())) { |
|
$deamon = new Daemon(); |
|
$deamon->run($argv); |
|
}
|
|
|