飞牛NAS的ping命令脚本,自动检查网络是否通断来关机,一般配合UPS使用

飞牛NAS的ping命令脚本,自动检查网络是否通断来关机,一般配合UPS使用

Zrest
2025-08-06 / 0 评论 / 55 阅读 / 正在检测是否收录...

飞牛NAS Ping命令监控关机脚本部署指南

步骤1:创建脚本文件

  1. 登录飞牛NAS的SSH(确保已启用SSH访问)

  2. 飞牛的系统设置里,设置SSH开启。

Windows 上连接飞牛 NAS 的 SSH 详细指南

使用 Windows PowerShell(内置工具,无需安装)

  1. 打开 PowerShell(或者CMD)

    • 按 Win + R 输入 powershell(或者CMD) 回车

    • 或者在开始菜单搜索 "PowerShell" 并打开

  2. 连接 SSH

    powershell

    ssh 用户名@NAS的IP地址

    例如:

    powershell

    ssh admin@192.168.1.100
    (其中admin改成你自己的飞牛NAS账号)
  3. 首次连接确认

    • 输入 yes 接受主机密钥

  4. 输入密码

    • 输入您在飞牛NAS上设置的密码(输入时不会显示字符)

  5. 成功连接

    • 看到类似 admin@freenas:~ $ 的提示符表示连接成功

接下来的命令都在powershell(或者CMD)中链接飞牛SSH后输入:

1.创建脚本目录(默认创建在飞牛文件管理中的app文件夹里):

CMD:

sudo mkdir -p /vol1/1000/app/ping_monitor

sudo chown -R $(whoami) /vol1/1000/app/ping_monitor

 2.创建脚本文件:

CMD:

nano /vol1/1000/app/ping_monitor/ping_monitor.sh

3.将脚本完整内容粘贴到文件中(或者可以直接在飞牛的文件里搜索ping_monitor.sh,找到它写入代码,代码在本文章最下面)

4.保存文件(Ctrl+O,回车,Ctrl+X

步骤2:设置脚本权限

CMD:

chmod +x /vol1/1000/app/ping_monitor/ping_monitor.sh

步骤3:安装必要依赖(可以跳过不装)

CMD:

sudo apt update
sudo apt install jq -y  # 安装JSON处理工具

步骤4:配置脚本参数(可选)

编辑脚本文件修改以下参数:

CMD:

nano /vol1/1000/app/ping_monitor/ping_monitor.sh

需要关注的配置项(最下面代码的内容):

ping_monitor.sh:

TARGET_IP="192.168.0.1"       # 修改为您的路由器IP
MAX_FAILURES=10                # 连续失败次数阈值(默认10次)
MIN_UPTIME=300                # 最小运行时间(默认300秒=5分钟)
SHUTDOWN_DELAY=15             # 关机前等待时间(默认15秒)

步骤5:设置定时任务

  1. 编辑crontab:

    CMD:

    crontab -e
  2. 添加以下行(意思是每分钟运行一次ping_monitor.sh):

    CMD:

    * * * * * /vol1/1000/app/ping_monitor/ping_monitor.sh
  3. 保存退出(Ctrl+O,回车,Ctrl+X

步骤6:测试脚本

  1. 手动运行脚本:

    CMD:

    /vol1/1000/app/ping_monitor/ping_monitor.sh
  2. 检查日志:

    CMD:

    tail -f /vol1/1000/app/ping_monitor/logs/ping_monitor.log

    正常输出应类似:

    text

    2025-07-02 10:30:45 - INFO - ===== Ping监控启动 =====
    2025-07-02 10:30:45 - INFO - 系统运行时间: 12345s
    2025-07-02 10:30:45 - INFO - 目标IP: 192.168.0.1, 最大失败次数: 10
    2025-07-02 10:30:45 - INFO - 当前失败计数: 0
    2025-07-02 10:30:45 - INFO - 状态更新完成。下次检查在1分钟后。

这个代码里的通知功能没有用,无法通知,运行的具体的信息都能在LOG日志里面找到。


ping_monitor.sh:

# Ping监控关机脚本 for 飞牛NAS(防止开机死循环) - 优化版
 
# 配置参数
TARGET_IP="192.168.0.1"       # 监控的目标IP(修改为您的路由器IP)
MAX_FAILURES=10                # 连续失败次数阈值
MIN_UPTIME=300                # 最小运行时间(秒)300秒=5分钟
SHUTDOWN_DELAY=15             # 关机前等待时间(秒)
LOG_DIR="/vol1/1000/app/ping_monitor/logs"
DATA_FILE="/vol1/1000/app/ping_monitor/ping_failure_count.json"
LOCK_FILE="/vol1/1000/app/ping_monitor/.shutdown_lock"
LOG_FILE="$LOG_DIR/ping_monitor.log"
MAX_LOG_SIZE=2048576          # 最大日志大小(1MB)
MAX_LOG_FILES=5               # 保留的历史日志文件数量
 
# 确保目录存在
mkdir -p "$LOG_DIR" || {
    echo "无法创建日志目录: $LOG_DIR" >&2
    exit 1
}
 
# 日志轮转函数
rotate_logs() {
    [ -f "$LOG_FILE" ] || return 0
    
    # 检查日志大小
    local log_size=$(wc -c < "$LOG_FILE" 2>/dev/null)
    [ -z "$log_size" ] && log_size=0
    
    if [ "$log_size" -ge "$MAX_LOG_SIZE" ]; then
        # 删除最旧的日志文件
        local oldest_log="${LOG_FILE}.${MAX_LOG_FILES}"
        [ -f "$oldest_log" ] && rm -f "$oldest_log"
        
        # 轮转日志文件
        for i in $(seq $((MAX_LOG_FILES-1)) -1 1); do
            local src="${LOG_FILE}.${i}"
            local dest="${LOG_FILE}.$((i+1))"
            [ -f "$src" ] && mv -f "$src" "$dest"
        done
        
        # 移动当前日志
        mv -f "$LOG_FILE" "${LOG_FILE}.1"
        
        # 创建新日志文件
        echo "日志轮转完成于 $(date)" > "$LOG_FILE"
    fi
}
 
# 日志记录函数
log() {
    local level=$1
    local message=$2
    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
    
    # 安全地轮转日志
    rotate_logs 2>/dev/null
    
    # 写入日志
    echo -e "${timestamp} - ${level} - ${message}" >> "$LOG_FILE" 2>/dev/null
}
 
# 发送通知函数
send_notification() {
    local title=$1
    local message=$2
    
    # 方法1: 使用飞牛官方通知API
    if command -v fn_notify &>/dev/null; then
        fn_notify -t "$title" -m "$message" -p high >/dev/null 2>&1 && return 0
    fi
    
    # 方法2: 使用DBus发送通知
    if command -v dbus-send &>/dev/null; then
        dbus-send --session --dest=org.freedesktop.Notifications \
        --type=method_call /org/freedesktop/Notifications \
        org.freedesktop.Notifications.Notify \
        string:"Ping Monitor" uint32:0 string:"" \
        string:"$title" string:"$message" \
        array:string:"" dict:string:string:"" int32:5000 >/dev/null 2>&1 && return 0
    fi
    
    # 方法3: 尝试使用curl发送通知(如果API可用)
    if command -v curl &>/dev/null; then
        curl -s -X POST -H "Content-Type: application/json" \
            -d "{\"title\":\"$title\", \"message\":\"$message\", \"priority\":\"high\"}" \
            http://localhost:3040/api/notification >/dev/null 2>&1 && return 0
    fi
    
    log "WARNING" "无法发送通知: $title - $message"
    return 1
}
 
# Ping检测函数
ping_ip() {
    # 使用不同的ping工具以适应不同环境
    if command -v ping &>/dev/null; then
        ping -c 2 -W 5 "$TARGET_IP" > /dev/null 2>&1
    elif command -v busybox &>/dev/null; then
        busybox ping -c 2 -W 5 "$TARGET_IP" > /dev/null 2>&1
    else
        log "ERROR" "未找到ping命令!"
        return 1
    fi
    return $?
}
 
# 获取系统运行时间(秒)
get_uptime() {
    cut -d' ' -f1 /proc/uptime | cut -d. -f1
}
 
# 增强版加载失败次数
load_failure_count() {
    [ -f "$DATA_FILE" ] || return 0
    
    # 安全读取JSON文件
    local json_data=$(<"$DATA_FILE")
    [ -z "$json_data" ] && return 0
    
    # 使用多种方法解析JSON
    local count last_update last_ip
    
    # 方法1: 使用jq
    if command -v jq &>/dev/null; then
        count=$(jq -r '.count' <<< "$json_data" 2>/dev/null)
        last_update=$(jq -r '.last_update' <<< "$json_data" 2>/dev/null)
        last_ip=$(jq -r '.last_ip' <<< "$json_data" 2>/dev/null)
    fi
    
    # 方法2: 使用grep/awk(备用方法)
    if [ -z "$count" ] || [ -z "$last_update" ] || [ -z "$last_ip" ]; then
        count=$(grep '"count"' "$DATA_FILE" | awk -F': ' '{print $2}' | tr -d ', ')
        last_update=$(grep '"last_update"' "$DATA_FILE" | awk -F': ' '{print $2}' | tr -d ', ')
        last_ip=$(grep '"last_ip"' "$DATA_FILE" | awk -F': ' '{print $2}' | tr -d '" ,')
    fi
    
    # 验证数据
    [[ "$count" =~ ^[0-9]+$ ]] || count=0
    [[ "$last_update" =~ ^[0-9]+$ ]] || last_update=0
    [[ "$last_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || last_ip=""
    
    local current_time=$(date +%s)
    
    # 检查是否超过24小时或IP改变
    if [ "$last_ip" != "$TARGET_IP" ] || \
       [ $((current_time - last_update)) -gt 86400 ]; then
        log "INFO" "重置计数器 (IP变更或超过24小时)"
        echo 0
        return
    fi
    
    echo "$count"
}
 
# 增强版保存失败次数
save_failure_count() {
    local count=$1
    local current_time=$(date +%s)
    
    # 创建临时文件
    local temp_file="${DATA_FILE}.tmp.$$"
    
    # 构建JSON数据
    cat > "$temp_file" <<EOF
{
    "count": $count,
    "last_update": $current_time,
    "last_ip": "$TARGET_IP"
}
EOF
    
    # 验证并移动文件
    if [ -s "$temp_file" ]; then
        mv -f "$temp_file" "$DATA_FILE"
        chmod 644 "$DATA_FILE"
        log "DEBUG" "保存失败计数: $count"
    else
        log "ERROR" "创建数据文件失败"
        rm -f "$temp_file"
    fi
}
 
# 关机提示函数
shutdown_warning() {
    local remaining=$1
    
    # 在控制台显示倒计时
    if [ -t 1 ]; then
        echo -ne "\r\033[K"  # 清除当前行
        echo -ne "NAS将在 ${remaining}秒 后关机! 按Ctrl+C取消"
    fi
    
    # 发送系统通知
    if [ $((remaining % 10)) -eq 0 ] || [ $remaining -le 5 ]; then
        send_notification "NAS关机警告" "检测到网络故障,NAS将在${remaining}秒后关机!"
    fi
    
    # 记录日志
    log "WARNING" "关机倒计时: ${remaining}秒"
}
 
# 飞牛NAS专用关机函数
shutdown_nas() {
    log "CRITICAL" "启动关机流程: 连续$MAX_FAILURES次ping失败 ($TARGET_IP)"
    
    # 发送最终关机通知
    send_notification "NAS正在关机" "检测到连续${MAX_FAILURES}次网络故障,NAS即将关机!"
    
    # 方法1: 使用飞牛官方关机API
    log "INFO" "尝试通过飞牛API关机"
    if curl -s -X POST "http://localhost:3040/api/system/shutdown" >/dev/null 2>&1; then
        log "INFO" "飞牛API关机命令已发送"
        return
    fi
    
    # 方法2: 使用DBus关机(飞牛推荐)
    log "INFO" "尝试通过DBus关机"
    if command -v dbus-send >/dev/null 2>&1; then
        if dbus-send --system --print-reply --dest=org.freenas.system \
            /org/freenas/system org.freenas.system.Shutdown >/dev/null 2>&1; then
            log "INFO" "DBus关机命令已发送"
            return
        fi
    fi
    
    # 方法3: 使用系统ctl关机
    log "INFO" "尝试通过systemctl关机"
    if command -v systemctl >/dev/null 2>&1; then
        systemctl poweroff
        if [ $? -eq 0 ]; then
            log "INFO" "systemctl poweroff命令已发送"
            return
        fi
    fi
    
    # 方法4: 传统关机命令
    log "INFO" "尝试传统shutdown命令"
    if /sbin/shutdown -h now; then
        log "INFO" "传统shutdown命令已发送"
        return
    fi
    
    # 方法5: 紧急关机
    log "CRITICAL" "所有关机方法均失败! 尝试紧急关机"
    sync
    echo 1 > /proc/sys/kernel/sysrq
    echo o > /proc/sysrq-trigger
}
 
# 清理函数
cleanup() {
    rm -f "${DATA_FILE}.tmp.*"
    exit 0
}
 
# 主程序
main() {
    trap cleanup EXIT INT TERM
    
    # 检查是否以root身份运行
    if [ "$(id -u)" -ne 0 ]; then
        echo -e "\n错误: 脚本必须以root权限运行\n" >&2
        exit 1
    fi
    
    # 检查关机锁文件
    if [ -f "$LOCK_FILE" ]; then
        log "WARNING" "检测到关机锁文件 - 重置状态"
        rm -f "$LOCK_FILE"
        save_failure_count 0
    fi
    
    # 获取系统运行时间
    local uptime=$(get_uptime)
    
    # 检查是否达到最小运行时间
    if [ "$uptime" -lt "$MIN_UPTIME" ]; then
        log "INFO" "系统运行时间不足 (${uptime}s < ${MIN_UPTIME}s),跳过检查"
        save_failure_count 0
        send_notification "NAS已启动" "系统正在启动中,监控将在${MIN_UPTIME}秒后开始"
        exit 0
    fi
    
    log "INFO" "===== Ping监控启动 ====="
    log "INFO" "系统运行时间: ${uptime}s"
    log "INFO" "目标IP: $TARGET_IP, 最大失败次数: $MAX_FAILURES"
    
    # 加载失败次数
    failure_count=$(load_failure_count)
    log "INFO" "当前失败计数: $failure_count"
    
    # 执行Ping测试
    if ping_ip; then
        # Ping成功时重置计数器
        if [ "$failure_count" -gt 0 ]; then
            log "INFO" "网络恢复! 重置计数器 (原计数: $failure_count)"
            send_notification "网络恢复" "到 ${TARGET_IP} 的网络连接已恢复"
        fi
        save_failure_count 0
    else
        # Ping失败时增加计数器
        failure_count=$((failure_count + 1))
        log "WARNING" "Ping失败! 失败计数: $failure_count/$MAX_FAILURES"
        save_failure_count "$failure_count"
        
        # 当接近关机阈值时发送警告
        if [ $failure_count -ge $((MAX_FAILURES - 1)) ]; then
            send_notification "网络故障警告" "到 ${TARGET_IP} 的连接失败 (${failure_count}/${MAX_FAILURES}次)"
        fi
        
        # 检查是否达到关机阈值
        if [ "$failure_count" -ge "$MAX_FAILURES" ]; then
            log "CRITICAL" "达到最大失败次数! 准备关机..."
            
            # 关机前强制重置计数器
            save_failure_count 0
            
            # 创建关机锁文件
            touch "$LOCK_FILE"
            
            # 关机倒计时
            log "WARNING" "开始 ${SHUTDOWN_DELAY} 秒关机倒计时"
            for ((i=SHUTDOWN_DELAY; i>0; i--)); do
                shutdown_warning $i
                sleep 1
            done
            
            # 添加空行清除倒计时显示
            [ -t 1 ] && echo
            
            # 执行关机
            log "CRITICAL" "正在关闭NAS..."
            shutdown_nas
            exit 0
        fi
    fi
    
    log "INFO" "状态更新完成。下次检查在1分钟后。"
}

 
# 执行主程序
main
0

评论 (0)

取消