add linux support

This commit is contained in:
UnbalancedCat
2025-10-03 17:29:58 +08:00
parent 4b8e3da812
commit 82eeee50f1
6 changed files with 1932 additions and 1 deletions

316
smart_shutdown.sh Executable file
View File

@@ -0,0 +1,316 @@
#!/bin/bash
#
# 智能网络监控脚本 - Ubuntu版本
#
# 功能描述:
# 该脚本会定期测试与指定目标IP的网络连通性。
# 如果在设定的"监控窗口"时间内连接持续失败,脚本将启动一个可取消的关机倒计时。
# 如果在倒计时期间网络恢复,关机将被中止。
# 所有操作都会被记录在日志文件中。
#
# 使用方法:
# sudo ./smart_shutdown.sh
#
# 要求需要root权限来执行关机命令
#
# ==================== 配置参数 ====================
# 监控的目标IP地址
TARGET_IP="192.168.3.15"
# 正常监控时的ping间隔
NORMAL_PING_INTERVAL=15
# 监控窗口时长(秒) - 在此期间持续失败则触发关机
MONITOR_WINDOW_SECONDS=180
# 关机倒计时时长(秒)
SHUTDOWN_COUNTDOWN=60
# 关机倒计时阶段的ping间隔
COUNTDOWN_PING_INTERVAL=3
# ping超时时间
PING_TIMEOUT=3
# 日志配置
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIRECTORY="$SCRIPT_DIR/logs"
MAX_LOG_DAYS=30
# 配置文件路径
CONFIG_FILE="$SCRIPT_DIR/config.json"
# ==================== 函数定义 ====================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
# 日志写入函数
write_log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local log_file="$LOG_DIRECTORY/network_monitor_$(date '+%Y%m%d').log"
local log_entry="[$timestamp] [$level] $message"
# 确保日志目录存在
mkdir -p "$LOG_DIRECTORY"
# 写入日志文件
echo "$log_entry" >> "$log_file"
# 根据日志级别在控制台输出不同颜色的信息
case "$level" in
"SUCCESS")
echo -e "${GREEN}$log_entry${NC}"
;;
"FAIL")
echo -e "${RED}$log_entry${NC}"
;;
"WARN")
echo -e "${YELLOW}$log_entry${NC}"
;;
"CRITICAL")
echo -e "${PURPLE}$log_entry${NC}"
;;
"INFO")
echo -e "$log_entry"
;;
*)
echo -e "$log_entry"
;;
esac
}
# 加载配置文件
load_configuration() {
if [[ -f "$CONFIG_FILE" ]]; then
write_log "INFO" "加载配置文件: $CONFIG_FILE"
# 使用jq解析JSON配置文件如果可用
if command -v jq >/dev/null 2>&1; then
local target_ip=$(jq -r '.TargetIP // empty' "$CONFIG_FILE" 2>/dev/null)
local monitor_window=$(jq -r '.MonitorWindowSeconds // empty' "$CONFIG_FILE" 2>/dev/null)
local shutdown_countdown=$(jq -r '.ShutdownCountdown // empty' "$CONFIG_FILE" 2>/dev/null)
local ping_interval=$(jq -r '.NormalPingInterval // empty' "$CONFIG_FILE" 2>/dev/null)
[[ -n "$target_ip" ]] && TARGET_IP="$target_ip"
[[ -n "$monitor_window" ]] && MONITOR_WINDOW_SECONDS="$monitor_window"
[[ -n "$shutdown_countdown" ]] && SHUTDOWN_COUNTDOWN="$shutdown_countdown"
[[ -n "$ping_interval" ]] && NORMAL_PING_INTERVAL="$ping_interval"
write_log "SUCCESS" "配置文件加载成功"
else
write_log "WARN" "jq未安装无法解析JSON配置文件使用默认配置"
fi
else
# 创建默认配置文件
create_default_config
fi
}
# 创建默认配置文件
create_default_config() {
cat > "$CONFIG_FILE" << EOF
{
"TargetIP": "$TARGET_IP",
"MonitorWindowSeconds": $MONITOR_WINDOW_SECONDS,
"ShutdownCountdown": $SHUTDOWN_COUNTDOWN,
"NormalPingInterval": $NORMAL_PING_INTERVAL
}
EOF
write_log "INFO" "创建默认配置文件: $CONFIG_FILE"
}
# 获取本机IP地址
get_local_ip() {
local local_ip
# 尝试获取默认路由接口的IP地址
local_ip=$(ip route get 8.8.8.8 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' | head -1)
if [[ -z "$local_ip" ]]; then
# 备用方法获取第一个非环回接口的IP
local_ip=$(ip addr show | grep -E 'inet [0-9]' | grep -v '127.0.0.1' | head -1 | awk '{print $2}' | cut -d'/' -f1)
fi
[[ -n "$local_ip" ]] && echo "$local_ip" || echo "未知"
}
# 测试网络连接
test_network_connection() {
local target_ip="$1"
# 使用ping命令测试连接发送1个包超时时间为PING_TIMEOUT秒
ping -c 1 -W "$PING_TIMEOUT" "$target_ip" >/dev/null 2>&1
return $?
}
# 清理旧日志文件
cleanup_old_logs() {
if [[ -d "$LOG_DIRECTORY" ]]; then
# 删除超过MAX_LOG_DAYS天的日志文件
find "$LOG_DIRECTORY" -name "network_monitor_*.log" -type f -mtime +$MAX_LOG_DAYS -exec rm -f {} \; 2>/dev/null
fi
}
# 信号处理函数
cleanup_and_exit() {
write_log "INFO" "接收到退出信号,正在清理..."
write_log "INFO" "========== 脚本退出 =========="
exit 0
}
# 检查root权限
check_root_permission() {
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}错误此脚本需要root权限才能执行关机操作${NC}"
echo -e "${YELLOW}请使用以下命令运行:${NC}"
echo -e "${CYAN}sudo $0${NC}"
exit 1
fi
}
# ==================== 主程序开始 ====================
# 设置信号处理
trap cleanup_and_exit SIGINT SIGTERM
# 检查权限
check_root_permission
# 清理旧日志
cleanup_old_logs
# 加载配置
load_configuration
# 初始化
clear
local_ip=$(get_local_ip)
write_log "INFO" "========== 脚本启动 =========="
write_log "INFO" "运行用户: $(whoami)"
write_log "INFO" "本机IP: $local_ip"
write_log "INFO" "监控目标: $TARGET_IP"
write_log "INFO" "监控窗口: ${MONITOR_WINDOW_SECONDS}"
write_log "INFO" "关机倒计时: ${SHUTDOWN_COUNTDOWN}"
write_log "INFO" "日志目录: $LOG_DIRECTORY"
write_log "INFO" "配置文件: $CONFIG_FILE"
write_log "INFO" "脚本已启动,按 Ctrl+C 退出..."
echo "========================================"
# 主循环变量
failure_start_time=""
normal_status_counter=0
normal_status_log_interval=24 # 每24次正常检测记录一次日志约6分钟
# 主循环
while true; do
# 测试网络连接
if test_network_connection "$TARGET_IP"; then
# 网络连接正常
if [[ -n "$failure_start_time" ]]; then
write_log "SUCCESS" "网络连接已恢复"
echo -e "${GREEN}[OK] 网络连接已恢复,重置监控窗口${NC}"
failure_start_time="" # 重置失败计时器
normal_status_counter=0 # 重置正常状态计数器
else
echo -e "${GREEN}[OK] 网络连接正常${NC}"
# 正常连接时减少日志频率,只定期记录
((normal_status_counter++))
if [[ $normal_status_counter -ge $normal_status_log_interval ]]; then
write_log "INFO" "网络连接持续正常(已检测 $normal_status_counter 次)"
normal_status_counter=0 # 重置计数器
fi
fi
# 只在控制台显示等待信息,不写入日志文件
echo -e "${CYAN}等待 ${NORMAL_PING_INTERVAL}秒后继续监控...${NC}"
sleep "$NORMAL_PING_INTERVAL"
else
# 网络连接失败
write_log "FAIL" "Ping失败 - 目标: $TARGET_IP"
if [[ -z "$failure_start_time" ]]; then
# 记录首次失败的时间
failure_start_time=$(date +%s)
write_log "WARN" "网络首次中断,开始进入监控窗口计时"
fi
# 计算网络已中断的时间
current_time=$(date +%s)
failure_duration=$((current_time - failure_start_time))
write_log "INFO" "网络已持续中断 $failure_duration / ${MONITOR_WINDOW_SECONDS}"
# 显示详细状态
remaining_time=$((MONITOR_WINDOW_SECONDS - failure_duration))
echo -e "${YELLOW}监控状态 - 已中断: ${failure_duration}/${MONITOR_WINDOW_SECONDS}秒,剩余: ${remaining_time}${NC}"
if [[ $failure_duration -ge $MONITOR_WINDOW_SECONDS ]]; then
# 持续失败时间超过监控窗口,开始关机倒计时
write_log "CRITICAL" "网络持续中断已超过 ${MONITOR_WINDOW_SECONDS} 秒,开始关机流程"
echo "========================================"
shutdown_cancelled=false
countdown_end_time=$((current_time + SHUTDOWN_COUNTDOWN))
while [[ $(date +%s) -lt $countdown_end_time && "$shutdown_cancelled" == "false" ]]; do
current_time=$(date +%s)
remaining_seconds=$((countdown_end_time - current_time))
if [[ $remaining_seconds -le 0 ]]; then
break
fi
write_log "WARN" "距离关机还有 $remaining_seconds 秒... 正在快速检测网络"
echo "----------------------------------------"
echo -e "${YELLOW}距离关机还有 $remaining_seconds 秒...${NC}"
echo -e "${YELLOW}正在快速检测网络连接... 按 Ctrl+C 可取消关机${NC}"
# 在倒计时中再次检测网络
if test_network_connection "$TARGET_IP"; then
write_log "SUCCESS" "网络在倒计时期间恢复!取消关机"
echo "========================================"
echo -e "${GREEN}[OK] 网络连接已恢复!取消关机操作${NC}"
echo "========================================"
failure_start_time=""
shutdown_cancelled=true
break
else
echo -e "${RED}[X] 网络仍然中断${NC}"
fi
# 等待下一次检测
sleep "$COUNTDOWN_PING_INTERVAL"
done
if [[ "$shutdown_cancelled" == "false" ]]; then
write_log "CRITICAL" "关机倒计时完成,执行系统关机命令"
# 执行关机命令
# 注意:为了安全起见,默认注释掉实际的关机命令
# 如需启用,请取消下面一行的注释
# shutdown -h now
echo -e "${RED}模拟关机shutdown -h now (为防止意外,此行已注释)${NC}"
write_log "CRITICAL" "关机命令已执行(模拟模式)"
# 脚本执行完关机命令后可以退出
exit 0
fi
else
# 在监控窗口期内,继续等待
write_log "INFO" "等待 ${NORMAL_PING_INTERVAL}秒后继续..."
sleep "$NORMAL_PING_INTERVAL"
fi
fi
done