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.

361 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/ServiceRepository.php';
require_once __DIR__ . '/../src/storage/SystemSettingsRepository.php';
//错误提示
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
// 登录验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$serviceRepo = new ServiceRepository();
$settingsRepo = new SystemSettingsRepository();
$message = '';
$error = '';
// 获取端口设置(放在函数定义之前)
$smtpPort = $settingsRepo->get('smtp_port', 25);
$pop3Port = $settingsRepo->get('pop3_port', 110);
function startService($serviceName, $port) {
$scriptPath = __DIR__ . "/../scripts/start_{$serviceName}.php";
$logFile = __DIR__ . "/../logs/{$serviceName}.log";
error_log("尝试启动服务: $serviceName , 端口: $port");
if (!file_exists($scriptPath)) {
return ['success' => false, 'message' => '启动脚本不存在'];
}
if (isServiceRunning($serviceName, $port)) {
return ['success' => false, 'message' => '服务已在运行'];
}
@file_put_contents($logFile, '');
//===== 修复:更稳健的命令执行 =====
$raw = shell_exec(sprintf(
'cd %s && (%s > %s 2>/dev/null </dev/null & echo $!)',
escapeshellarg(dirname($scriptPath, 2)),
'nohup php ' . escapeshellarg($scriptPath),
escapeshellarg($logFile)
));
/*$raw = shell_exec(sprintf(
'cd %s && (%s > /dev/null 2>&1 </dev/null & echo $!)',
escapeshellarg(dirname($scriptPath, 2)),
'nohup php ' . escapeshellarg($scriptPath),
escapeshellarg($logFile) // 这一行现在仅用于占位,实际不再写
));*/
// 修复trim(null)问题
$raw = (string)$raw; // 强制转换为字符串
$pid = trim($raw); // 现在可以安全trim
error_log("Shell命令执行结果: " . var_export($raw, true));
error_log("获取的PID: " . $pid); // 修复:这里$pid已定义
if (!is_numeric($pid) || $pid < 1) {
error_log("PID无效: $pid");
return ['success' => false, 'message' => '未能获取有效进程号'];
}
/* ===== 修复:添加进程状态检查 ===== */
sleep(1); // 先等待1秒
// 检查进程是否还在运行
$processExists = file_exists("/proc/$pid");
error_log("进程是否存在: " . ($processExists ? "" : ""));
if (!$processExists) {
// 查看脚本为什么退出了
if (file_exists($logFile)) {
$logContent = file_get_contents($logFile);
error_log("脚本输出日志:\n" . $logContent);
}
return ['success' => false, 'message' => '进程已退出'];
}
/* ===== 修复:更长的等待时间 ===== */
$maxChecks = 6;
for ($i = 0; $i < $maxChecks; $i++) {
sleep(1); // 每次检查等待1秒
if (isServiceRunning($serviceName, $port)) {
// 再次确认PID仍然有效
if (file_exists("/proc/$pid")) {
return ['success' => true, 'pid' => $pid];
} else {
// 进程已退出
return ['success' => false, 'message' => '进程已退出'];
}
}
error_log("" . ($i+1) . " 次检查:端口未监听");
}
// 启动失败 → 清理孤儿
if (file_exists("/proc/$pid")) {
@exec("kill -9 {$pid} 2>/dev/null");
}
// 输出失败原因
if (file_exists($logFile)) {
// 只读取最后10行避免读取大文件
$logContent = shell_exec("tail -10 " . escapeshellarg($logFile));
error_log("最终日志内容(最后100行):\n" . $logContent);
}
return ['success' => false, 'message' => '服务端口未监听,启动失败'];
}
function stopService($serviceName, $pid, $port) {
// ===== 改:优先用 DB 里的 PID没有再现场嗅探 =====
if (empty($pid) || !is_numeric($pid)) {
$pid = getServicePid($serviceName, $port);
}
if ($pid) {
@exec("kill {$pid} 2>/dev/null");
sleep(1);
if (isServiceRunning($serviceName, $port)) {
@exec("kill -9 {$pid} 2>/dev/null");
sleep(1);
}
}
return !isServiceRunning($serviceName, $port);
}
function isServiceRunning($serviceName, $port) {
// 方法1: 使用单引号确保变量正确展开
$cmd1 = "netstat -tlnp 2>/dev/null | grep ':" . $port . "' | grep LISTEN";
$result1 = shell_exec($cmd1);
// 方法2: 使用ss命令更可靠
$cmd2 = "ss -tlnp 2>/dev/null | grep ':" . $port . "'";
$result2 = shell_exec($cmd2);
// 方法3: 使用lsof你已验证有效
$cmd3 = "lsof -i :" . $port . " 2>/dev/null";
$result3 = shell_exec($cmd3);
// 方法4: 直接socket连接测试最可靠
$fp = @fsockopen('127.0.0.1', $port, $errno, $errstr, 1);
if ($fp) {
fclose($fp);
return true;
}
// 任意一个方法有结果都算服务在运行
return !empty(trim($result1 ?? '')) ||
!empty(trim($result2 ?? '')) ||
!empty(trim($result3 ?? ''));
}
function getServicePid($serviceName, $port) {
return trim(shell_exec("lsof -ti: {$port} 2>/dev/null | head -1"));
}
// ===================== 修改部分结束 =====================
// 处理服务起停
if (isset($_GET['action'])) {
$serviceName = $_GET['service'] ?? '';
$action = $_GET['action'] ?? '';
if ($serviceName === 'smtp' || $serviceName === 'pop3') {
$port = $serviceName === 'smtp' ? $smtpPort : $pop3Port;
if ($action === 'start') {
$result = startService($serviceName, $port);
if ($result['success']) {
$pid = $result['pid'];
$serviceRepo->updateStatus($serviceName, true, $pid);
$message = strtoupper($serviceName) . "服务已启动 (PID: {$pid})";
} else {
$error = strtoupper($serviceName) . "服务启动失败: " . $result['message'];
}
} elseif ($action === 'stop') {
$status = $serviceRepo->getStatus($serviceName);
$pid = $status['pid'] ?? getServicePid($serviceName, $port);
if (stopService($serviceName, $pid, $port)) {
$serviceRepo->updateStatus($serviceName, false, null);
$message = strtoupper($serviceName) . "服务已停止";
} else {
$error = strtoupper($serviceName) . "服务停止失败";
}
// ===================== 修改部分结束 =====================
}
}
}
// 获取服务状态(修改判断逻辑)
// ===================== 修改部分开始 =====================
$smtpStatus = $serviceRepo->getStatus('smtp');
$pop3Status = $serviceRepo->getStatus('pop3');
// 实际检查服务是否运行(使用配置的端口)
$smtpRunning = isServiceRunning('smtp', $smtpPort);
$pop3Running = isServiceRunning('pop3', $pop3Port);
// 如果数据库状态与实际状态不一致,更新数据库
if ($smtpStatus['is_running'] != $smtpRunning) {
$pid = $smtpRunning ? getServicePid('smtp', $smtpPort) : null;
$serviceRepo->updateStatus('smtp', $smtpRunning, $pid);
}
if ($pop3Status['is_running'] != $pop3Running) {
$pid = $pop3Running ? getServicePid('pop3', $pop3Port) : null;
$serviceRepo->updateStatus('pop3', $pop3Running, $pid);
}
// 重新获取更新后的状态
$smtpStatus = $serviceRepo->getStatus('smtp');
$pop3Status = $serviceRepo->getStatus('pop3');
$smtpRunning = $smtpStatus['is_running'];
$pop3Running = $pop3Status['is_running'];
// ===================== 修改部分结束 =====================
?>
<!DOCTYPE html>
<html>
<head>
<title>服务管理 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.service-box { border: 1px solid #ddd; padding: 20px; margin-bottom: 20px; border-radius: 5px; }
.service-box h3 { margin-top: 0; }
.status { display: inline-block; padding: 6px 12px; border-radius: 4px; font-weight: 500; margin-right: 10px; }
.status-running { background: #28a745; color: white; }
.status-stopped { background: #dc3545; color: white; }
.btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-success { background: #28a745; color: white; }
.btn-danger { background: #dc3545; color: white; }
.info { color: #666; font-size: 14px; margin-top: 10px; }
.note { background: #fff3cd; border: 1px solid #ffc107; padding: 15px; border-radius: 5px; margin-top: 20px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<a href="users.php">用户管理</a>
<a href="broadcast.php">群发邮件</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<a href="services.php">服务管理</a>
<a href="settings.php">系统设置</a>
<a href="help.php">帮助</a>
</div>
<div class="container">
<h2>服务管理</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<!-- SMTP服务 -->
<div class="service-box">
<h3>SMTP服务邮件发送</h3>
<p>
<span class="status status-<?php echo $smtpRunning ? 'running' : 'stopped'; ?>">
<?php echo $smtpRunning ? '运行中' : '已停止'; ?>
</span>
<?php if ($smtpRunning): ?>
<a href="?service=smtp&action=stop" class="btn btn-danger" onclick="return confirm('确定要停止SMTP服务吗');">停止服务</a>
<?php else: ?>
<a href="?service=smtp&action=start" class="btn btn-success" onclick="return confirm('确定要启动SMTP服务吗');">启动服务</a>
<?php endif; ?>
</p>
<div class="info">
<strong>端口:</strong><?php echo $smtpPort; ?><br>
<strong>进程ID</strong><?php echo $smtpStatus['pid'] ?? '-'; ?><br>
<strong>最后启动:</strong><?php echo $smtpStatus['last_started_at'] ?? '-'; ?><br>
<strong>最后停止:</strong><?php echo $smtpStatus['last_stopped_at'] ?? '-'; ?>
</div>
</div>
<!-- POP3服务 -->
<div class="service-box">
<h3>POP3服务邮件接收</h3>
<p>
<span class="status status-<?php echo $pop3Running ? 'running' : 'stopped'; ?>">
<?php echo $pop3Running ? '运行中' : '已停止'; ?>
</span>
<?php if ($pop3Running): ?>
<a href="?service=pop3&action=stop" class="btn btn-danger" onclick="return confirm('确定要停止POP3服务吗');">停止服务</a>
<?php else: ?>
<a href="?service=pop3&action=start" class="btn btn-success" onclick="return confirm('确定要启动POP3服务吗');">启动服务</a>
<?php endif; ?>
</p>
<div class="info">
<strong>端口:</strong><?php echo $pop3Port; ?><br>
<strong>进程ID</strong><?php echo $pop3Status['pid'] ?? '-'; ?><br>
<strong>最后启动:</strong><?php echo $pop3Status['last_started_at'] ?? '-'; ?><br>
<strong>最后停止:</strong><?php echo $pop3Status['last_stopped_at'] ?? '-'; ?>
</div>
</div>
<div class="note">
<strong>注意:</strong>
<ul>
<li>如果服务启动失败,请检查是否没有该端口访问权限或端口被占用</li>
<li>当前配置SMTP端口 <?php echo $smtpPort; ?>, POP3端口 <?php echo $pop3Port; ?></li>
</ul>
</div>
</div>
<!-- ===================== 新增部分:自动刷新状态 =====================
<script>
// 每5秒自动检查服务状态
setInterval(function() {
fetch(window.location.href)
.then(response => response.text())
.then(html => {
// 从返回的HTML中提取运行状态
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
// 检查SMTP状态是否变化
const smtpStatusText = tempDiv.querySelector('.service-box:nth-child(1) .status')?.textContent;
const currentSmtpStatus = document.querySelector('.service-box:nth-child(1) .status')?.textContent;
// 检查POP3状态是否变化
const pop3StatusText = tempDiv.querySelector('.service-box:nth-child(2) .status')?.textContent;
const currentPop3Status = document.querySelector('.service-box:nth-child(2) .status')?.textContent;
// 如果有变化,刷新页面
if (smtpStatusText !== currentSmtpStatus || pop3StatusText !== currentPop3Status) {
console.log('服务状态变化,刷新页面...');
location.reload();
}
})
.catch(error => console.error('状态检查失败:', error));
}, 30000);
</script>**-->
</body>
</html>