收起左侧

分享一个飞牛NAS自动休眠设置脚本

2
回复
55
查看
[ 复制链接 ]

4

主题

4

回帖

0

牛值

江湖小虾

我自己组了一个飞牛NAS,但发现飞牛系统只有硬盘可以自动休眠,成个系统不可以。以前用群晖,它是可以整个系统休眠的,类似关机这样,然后通过网络唤醒又可以自动开机。这个功能对于我这种一穷二白,交不起电费的来说简直太实用了(希望官方后续更新可以做进系统)。既然飞牛系统没有系统休眠,那只能自己写一个脚本了。一开始设计逻辑是通过监控硬盘读写数据进行判断是否进入休眠倒计时,但后来发现,硬盘时刻都有会一丁点的数据读写,主要来源后台任务(日志、索引、备份等)会持续产生硬盘活动。既然这样就改变思路,通过多维度综合考虑(例如:网络连接数),实现系统休眠。具体方法如下:

一、查看网卡是否支持WoL运行 ethtool <网卡名>后,在输出中寻找 Supports Wake-on​ 和 Wake-on​ 字段。例如
  1. [size=2]Supports Wake-on: pumbg
  2. Wake-on: g[/size]
复制代码
Wake-on 的值
  • d:禁用
  • p:物理活动唤醒
  • u:单播数据包唤醒
  • m:多播数据包唤醒
  • b:广播数据包唤醒
  • a:ARP包唤醒
  • g:魔术包唤醒(WoL)
  • s:带密码的魔术包唤醒

二、创建配置文件

  1. sudo nano /etc/nas-suspend.conf
复制代码

  1. # ============================================
  2. # NAS自动休眠配置文件
  3. # ============================================

  4. # ---------- 基础设置 ----------
  5. # 空闲多长时间后休眠(秒)
  6. INACTIVITY_TIMEOUT=900      # 15分钟

  7. # 检查间隔(秒)
  8. CHECK_INTERVAL=60           # 60秒

  9. # 日志文件路径
  10. LOG_FILE="/var/log/nas-suspend.log"

  11. # ---------- 连接检测设置 ----------
  12. # 允许的最大活跃连接数
  13. # 0 = 不允许任何外部连接
  14. MAX_ALLOWED_CONNECTIONS=3

  15. # ---------- 网络检测设置 ----------
  16. # 网络流量变化阈值(字节)
  17. # 超过此值认为有活动
  18. MIN_NETWORK_CHANGE=1048576  # 1MB

  19. # ---------- 调试设置 ----------
  20. # 启用详细日志
  21. VERBOSE_LOG=true
复制代码
注:Ctrl+X 退出并选择保存

三、创建脚本
  1. sudo nano /usr/local/bin/nas-auto-suspend.sh
复制代码
  1. #!/bin/bash

  2. # ============================================
  3. # NAS智能自动休眠脚本
  4. # 版本: 1.0
  5. # 功能: 监控NAS活动并在空闲时休眠
  6. # ============================================

  7. # -------------------------------------------------
  8. # 配置加载
  9. # -------------------------------------------------
  10. CONFIG_FILE="/etc/nas-suspend.conf"

  11. # 默认配置
  12. INACTIVITY_TIMEOUT=1800      # 30分钟
  13. CHECK_INTERVAL=60           # 60秒
  14. LOG_FILE="/var/log/nas-suspend.log"
  15. MAX_ALLOWED_CONNECTIONS=0   # 不允许外部连接
  16. MIN_NETWORK_CHANGE=1048576  # 1MB网络变化阈值
  17. VERBOSE_LOG=true           # 详细日志

  18. # 加载配置文件
  19. if [ -f "$CONFIG_FILE" ] && [ -r "$CONFIG_FILE" ]; then
  20.     source "$CONFIG_FILE"
  21.     echo "$(date '+%Y-%m-%d %H:%M:%S') - 已加载配置文件" >&2
  22. fi

  23. # -------------------------------------------------
  24. # 日志函数
  25. # -------------------------------------------------
  26. log() {
  27.     local level="$1"
  28.     local message="$2"
  29.     local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  30.    
  31.     if [ "$VERBOSE_LOG" = "true" ] || [ "$level" = "INFO" ] || [ "$level" = "ERROR" ]; then
  32.         echo "[${timestamp}] [${level}] ${message}" | tee -a "$LOG_FILE" >&2
  33.     else
  34.         echo "[${timestamp}] [${level}] ${message}" >> "$LOG_FILE"
  35.     fi
  36. }

  37. # -------------------------------------------------
  38. # 检测函数
  39. # -------------------------------------------------

  40. # 1. 检查活跃连接数
  41. check_connections() {
  42.     # 排除本地连接,只计算外部TCP连接
  43.     local connections=$(ss -tn state established 2>/dev/null | awk '!/127\.0\.0\.1|::1/ && NR>1' | wc -l)
  44.     echo "$connections"
  45. }

  46. # 2. 检查网络流量
  47. check_network_traffic() {
  48.     # 获取总接收和发送字节数
  49.     local rx_bytes=0
  50.     local tx_bytes=0
  51.    
  52.     for iface in /sys/class/net/eth* /sys/class/net/en*; do
  53.         if [ -d "$iface" ]; then
  54.             iface_name=$(basename "$iface")
  55.             rx_bytes=$((rx_bytes + $(cat "/sys/class/net/$iface_name/statistics/rx_bytes" 2>/dev/null || echo 0)))
  56.             tx_bytes=$((tx_bytes + $(cat "/sys/class/net/$iface_name/statistics/tx_bytes" 2>/dev/null || echo 0)))
  57.         fi
  58.     done
  59.    
  60.     echo "${rx_bytes}:${tx_bytes}"
  61. }

  62. # 3. 检查磁盘活动(可选,针对NAS特别有用)
  63. check_disk_activity() {
  64.     # 检查所有物理硬盘的读写活动
  65.     local total_io=0
  66.     for disk in /sys/block/sd* /sys/block/nvme*; do
  67.         if [ -d "$disk" ]; then
  68.             disk_name=$(basename "$disk")
  69.             read reads writes < <(awk '{print $4+$8}' "/sys/block/$disk_name/stat" 2>/dev/null | echo "0 0")
  70.             total_io=$((total_io + reads + writes))
  71.         fi
  72.     done
  73.     echo "$total_io"
  74. }

  75. # -------------------------------------------------
  76. # 启用网络唤醒
  77. # -------------------------------------------------
  78. enable_wake_on_lan() {
  79.     log "INFO" "启用网络唤醒(WOL)..."
  80.    
  81.     local enabled_count=0
  82.     for iface in /sys/class/net/eth* /sys/class/net/en*; do
  83.         if [ -d "$iface" ]; then
  84.             iface_name=$(basename "$iface")
  85.             if ethtool -s "$iface_name" wol g 2>/dev/null; then
  86.                 log "INFO" "  ✓ 已启用 $iface_name 的WOL"
  87.                 enabled_count=$((enabled_count + 1))
  88.             else
  89.                 log "WARNING" "  ✗ 无法启用 $iface_name 的WOL"
  90.             fi
  91.         fi
  92.     done
  93.    
  94.     if [ $enabled_count -eq 0 ]; then
  95.         log "WARNING" "未能启用任何网卡的WOL功能"
  96.     fi
  97. }

  98. # -------------------------------------------------
  99. # 综合空闲检测
  100. # -------------------------------------------------
  101. is_system_idle() {
  102.     local idle=true
  103.     local reasons=""
  104.    
  105.     # 1. 检查连接
  106.     local connections=$(check_connections)
  107.     if [ "$connections" -gt "$MAX_ALLOWED_CONNECTIONS" ]; then
  108.         idle=false
  109.         reasons="${reasons}连接数(${connections}) "
  110.     fi
  111.    
  112.     # 2. 检查网络流量(使用静态变量存储上一次的值)
  113.     local current_network=$(check_network_traffic)
  114.     local current_rx=$(echo "$current_network" | cut -d: -f1)
  115.     local current_tx=$(echo "$current_network" | cut -d: -f2)
  116.    
  117.     # 第一次运行时初始化
  118.     if [ -z "${last_network_rx:-}" ]; then
  119.         last_network_rx=$current_rx
  120.         last_network_tx=$current_tx
  121.     fi
  122.    
  123.     # 计算网络流量变化
  124.     local rx_diff=$((current_rx - last_network_rx))
  125.     local tx_diff=$((current_tx - last_network_tx))
  126.    
  127.     # 检查是否有显著的网络活动
  128.     if [ "${rx_diff#-}" -gt "$MIN_NETWORK_CHANGE" ] || [ "${tx_diff#-}" -gt "$MIN_NETWORK_CHANGE" ]; then
  129.         idle=false
  130.         reasons="${reasons}网络活动(RX:${rx_diff}, TX:${tx_diff}) "
  131.     fi
  132.    
  133.     # 更新最后一次的网络统计(每小时重置一次,避免溢出)
  134.     if [ $(date +%M) -eq 0 ]; then  # 每小时整点重置
  135.         last_network_rx=$current_rx
  136.         last_network_tx=$current_tx
  137.         log "DEBUG" "每小时重置网络基准统计"
  138.     fi
  139.    
  140.     # 3. 检查磁盘活动
  141.     local disk_io=$(check_disk_activity)
  142.     if [ "$disk_io" -gt 1000 ]; then  # 如果有大量IO操作
  143.         idle=false
  144.         reasons="${reasons}磁盘活动 "
  145.     fi
  146.    
  147.     # 返回结果
  148.     if $idle; then
  149.         echo "idle"
  150.     else
  151.         echo "active:${reasons}"
  152.     fi
  153. }

  154. # -------------------------------------------------
  155. # 主函数
  156. # -------------------------------------------------
  157. main() {
  158.     # 初始化日志文件
  159.     touch "$LOG_FILE" 2>/dev/null || {
  160.         echo "错误: 无法创建日志文件 $LOG_FILE" >&2
  161.         exit 1
  162.     }
  163.     chmod 644 "$LOG_FILE" 2>/dev/null
  164.    
  165.     log "INFO" "========================================"
  166.     log "INFO" "NAS智能自动休眠脚本启动"
  167.     log "INFO" "========================================"
  168.     log "INFO" "配置文件: $CONFIG_FILE"
  169.     log "INFO" "日志文件: $LOG_FILE"
  170.     log "INFO" "超时时间: ${INACTIVITY_TIMEOUT}秒"
  171.     log "INFO" "检查间隔: ${CHECK_INTERVAL}秒"
  172.     log "INFO" "最大允许连接: ${MAX_ALLOWED_CONNECTIONS}"
  173.     log "INFO" "网络变化阈值: ${MIN_NETWORK_CHANGE}字节"
  174.     log "INFO" "========================================"
  175.    
  176.     # 初始状态
  177.     local idle_start_time=$(date +%s)
  178.     local last_status="unknown"
  179.     local cycle_count=0
  180.    
  181.     log "INFO" "开始监控系统活动..."
  182.     log "INFO" "初始连接数: $(check_connections)"
  183.    
  184.     # 主循环
  185.     while true; do
  186.         sleep "$CHECK_INTERVAL"
  187.         cycle_count=$((cycle_count + 1))
  188.         
  189.         local current_time=$(date +%s)
  190.         local status_result=$(is_system_idle)
  191.         local status=$(echo "$status_result" | cut -d: -f1)
  192.         
  193.         if [ "$status" = "active" ]; then
  194.             # 系统活跃
  195.             local reasons=$(echo "$status_result" | cut -d: -f2-)
  196.             
  197.             if [ "$last_status" != "active" ]; then
  198.                 log "INFO" "系统状态: 活跃 → ${reasons}"
  199.             fi
  200.             
  201.             idle_start_time=$current_time
  202.             last_status="active"
  203.         else
  204.             # 系统空闲
  205.             local idle_duration=$((current_time - idle_start_time))
  206.             
  207.             # 状态变化或每5分钟记录一次
  208.             if [ "$last_status" != "idle" ] || [ $((cycle_count % 5)) -eq 0 ]; then
  209.                 log "INFO" "系统状态: 空闲 (${idle_duration}秒)"
  210.             fi
  211.             
  212.             # 检查是否达到休眠条件
  213.             if [ $idle_duration -ge $INACTIVITY_TIMEOUT ]; then
  214.                 log "INFO" "========================================"
  215.                 log "INFO" "🎯 达到休眠条件"
  216.                 log "INFO" "系统已连续空闲 ${idle_duration}秒"
  217.                 log "INFO" "开始最终确认检查..."
  218.                 log "INFO" "========================================"
  219.                
  220.                 # 最终确认(等待30秒再次检查)
  221.                 sleep 30
  222.                 local final_check=$(is_system_idle)
  223.                
  224.                 if [ "$(echo "$final_check" | cut -d: -f1)" = "idle" ]; then
  225.                     log "INFO" "✅ 最终确认: 系统仍然空闲"
  226.                     
  227.                     # 启用网络唤醒
  228.                     enable_wake_on_lan
  229.                     
  230.                     # 准备休眠
  231.                     log "INFO" "同步磁盘数据..."
  232.                     sync
  233.                     sleep 2
  234.                     
  235.                     log "INFO" "💤 正在进入休眠状态..."
  236.                     
  237.                     # 执行休眠
  238.                     if systemctl suspend; then
  239.                         log "INFO" "✅ 休眠命令执行成功"
  240.                     else
  241.                         local exit_code=$?
  242.                         log "ERROR" "❌ 休眠命令失败,退出码: $exit_code"
  243.                     fi
  244.                     
  245.                     # 唤醒后
  246.                     sleep 10
  247.                     log "INFO" "⏰ 系统唤醒,重新开始监控"
  248.                     idle_start_time=$(date +%s)
  249.                     last_status="unknown"
  250.                     cycle_count=0
  251.                 else
  252.                     log "INFO" "⚠️  最终检查失败,系统恢复活跃"
  253.                     idle_start_time=$current_time
  254.                     last_status="active"
  255.                 fi
  256.             fi
  257.             
  258.             last_status="idle"
  259.         fi
  260.         
  261.         # 每10分钟记录一次系统状态
  262.         if [ $((cycle_count % 10)) -eq 0 ]; then
  263.             log "DEBUG" "系统状态汇总 - 周期: ${cycle_count}, 连接数: $(check_connections)"
  264.         fi
  265.     done
  266. }

  267. # -------------------------------------------------
  268. # 错误处理和信号捕获
  269. # -------------------------------------------------
  270. handle_exit() {
  271.     log "INFO" "脚本被终止"
  272.     exit 0
  273. }

  274. trap handle_exit SIGTERM SIGINT

  275. # -------------------------------------------------
  276. # 脚本入口
  277. # -------------------------------------------------
  278. # 确保以root权限运行
  279. if [ "$EUID" -ne 0 ]; then
  280.     echo "错误: 此脚本需要root权限运行" >&2
  281.     exit 1
  282. fi

  283. # 运行主函数
  284. main
复制代码
四、设置脚本权限
  1. sudo chmod +x /usr/local/bin/nas-auto-suspend.sh
复制代码


五、创建systemd服务文件
  1. sudo nano /etc/systemd/system/nas-auto-suspend.service
复制代码

  1. [Unit]
  2. Description=NAS Auto Suspend Service
  3. Description=监控NAS活动并在空闲时自动休眠
  4. After=network.target multi-user.target
  5. Wants=network-online.target

  6. [Service]
  7. Type=simple
  8. User=root
  9. ExecStart=/usr/local/bin/nas-auto-suspend.sh
  10. Restart=on-failure
  11. RestartSec=30
  12. StandardOutput=journal
  13. StandardError=journal
  14. Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

  15. # 资源限制
  16. MemoryMax=100M
  17. CPUQuota=10%

  18. # 安全设置
  19. NoNewPrivileges=true
  20. PrivateTmp=true
  21. ProtectSystem=strict
  22. ReadWritePaths=/var/log/nas-suspend.log

  23. [Install]
  24. WantedBy=multi-user.target
复制代码
六、重新加载并启用服务

  1. # 重新加载systemd配置
  2. sudo systemctl daemon-reload

  3. # 创建日志文件
  4. sudo touch /var/log/nas-suspend.log
  5. sudo chmod 644 /var/log/nas-suspend.log

  6. # 启用服务(开机自启)
  7. sudo systemctl enable nas-auto-suspend.service

  8. # 启动服务
  9. sudo systemctl start nas-auto-suspend.service
复制代码


七、查看实时日志
  1. sudo journalctl -u nas-auto-suspend.service -f
复制代码


八、后话
如果你需要调整休眠时间、检查系统间隔和网络连接数监控,可以通过修改nas-suspend.conf里的参数即可
  1. # ---------- 基础设置 ----------
  2. # 空闲多长时间后休眠(秒)
  3. INACTIVITY_TIMEOUT=900 # 15分钟

  4. # 检查间隔(秒)
  5. CHECK_INTERVAL=60 # 60秒

  6. # ---------- 连接检测设置 ----------
  7. # 允许的最大活跃连接数
  8. # 0 = 不允许任何外部连接
  9. MAX_ALLOWED_CONNECTIONS=3
复制代码



收藏
送赞 1
分享

4

主题

744

回帖

0

牛值

社区共建团

社区共建团荣誉勋章飞牛百度网盘玩家fnOS1.0上线纪念勋章

唤醒的话,是飞牛app连接就能唤醒,还是得用第三方app发唤醒数据包?

飞牛APP没有这功能,只能用第三方或者路由app  详情 回复
半小时前

4

主题

4

回帖

0

牛值

江湖小虾

半小时前 楼主 显示全部楼层
yyywd 发表于 2025-12-11 20:01
唤醒的话,是飞牛app连接就能唤醒,还是得用第三方app发唤醒数据包?

飞牛APP没有这功能,只能用第三方或者路由app
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则