💋 Sync 2026-09-07 03:05:06

This commit is contained in:
github-actions[bot] committed 2026-09-07 03:05:06 +08:00
1 parent d8bd09d3cc
commit a1443a0a8b
22 files changed
+428 -1918

No files matched your search

@@ -1,600 +0,0 @@
#!/bin/bash
# Copyright (C) 2006 OpenWrt.org
# Copyright 2022-2026 sirpdboy <herboy2008@gmail.com>
crrun=$1
crid=$2
NAME=timecontrol
DEBUG=1 # 开启调试
config_t_get() {
local index=${3:-0}
local ret=$(uci -q get "${NAME}.@${1}[${index}].${2}")
echo "${ret:-$4}"
}
LOG_FILE="/var/log/timecontrol.log"
IDLIST="/var/$NAME.idlist"
bin_nft=$(which nft 2>/dev/null)
bin_iptables=$(which iptables 2>/dev/null)
bin_ip6tables=$(which ip6tables 2>/dev/null)
bin_conntrack=$(which conntrack 2>/dev/null)
nftables_ver=0
iptables_ver=0
# 获取配置
chain=$(config_t_get timecontrol chain 0 "forward")
list_type=$(config_t_get timecontrol list_type 0 "blacklist")
if [ "$chain" = "input" ]; then
StrongCHAIN=1
else
StrongCHAIN=0
fi
dbg() {
if [ "$DEBUG" -eq 1 ]; then
local d="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$d] FW-DEBUG: $@" >> "$LOG_FILE"
echo "FW-DEBUG: $@"
fi
}
info() {
local d="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$d] FW-INFO: $@" >> "$LOG_FILE"
echo "FW-INFO: $@"
}
# 地址解析函数 - 修复格式问题
parse_target() {
local target="$1"
# 去除空格
target=$(echo "${target}" | xargs)
# dbg "解析目标地址: $target"
# IPv4单个地址
if echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
local octets=(${target//./ })
local valid=1
for octet in "${octets[@]}"; do
if [ "$octet" -gt 255 ] || [ "$octet" -lt 0 ]; then
valid=0
break
fi
done
[ "$valid" -eq 1 ] && {
echo "ipv4:single:$target"
return 0
}
# IPv4范围
elif echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}-([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
local start_ip=${target%-*}
local end_ip=${target#*-}
echo "ipv4:range:$start_ip-$end_ip"
return 0
# CIDR
elif echo "$target" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$'; then
local ip=${target%/*}
local mask=${target#*/}
[ "$mask" -le 32 ] && [ "$mask" -ge 0 ] && {
echo "ipv4:cidr:$target"
return 0
}
# MAC地址
elif echo "$target" | grep -qE '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then
echo "mac:single:$(echo "$target" | tr '[:upper:]' '[:lower:]')"
return 0
# IPv6地址
elif echo "$target" | grep -qE '^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$'; then
echo "ipv6:single:$target"
return 0
# IPv6 CIDR
elif echo "$target" | grep -qE '^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}/[0-9]{1,3}$'; then
local ipv6=${target%/*}
local mask=${target#*/}
[ "$mask" -le 128 ] && [ "$mask" -ge 0 ] && {
echo "ipv6:cidr:$target"
return 0
}
fi
dbg "无法解析地址: $target"
return 1
}
# 清理现有连接
flush_connections() {
local target="$1"
[ -x "$bin_conntrack" ] || {
dbg "conntrack不可用"
return
}
local parsed_result=$(parse_target "$target")
[ $? -eq 0 ] || {
dbg "无法解析地址用于清理连接: $target"
return
}
IFS=':' read -r type subtype value <<< "$parsed_result"
dbg "清理连接: type=$type, value=$value"
case "$type" in
"ipv4")
$bin_conntrack -D -s "$value" 2>/dev/null && dbg "清理源连接: $value"
$bin_conntrack -D -d "$value" 2>/dev/null && dbg "清理目标连接: $value"
;;
"mac")
# MAC地址需要先转换为IP
if [ -f "/proc/net/arp" ]; then
local ip_addr=$(grep -i "$value" /proc/net/arp 2>/dev/null | awk '{print $1}' | head -1)
if [ -n "$ip_addr" ]; then
$bin_conntrack -D -s "$ip_addr" 2>/dev/null && dbg "清理MAC源连接: $value -> $ip_addr"
$bin_conntrack -D -d "$ip_addr" 2>/dev/null && dbg "清理MAC目标连接: $value -> $ip_addr"
fi
fi
;;
esac
}
# 检查防火墙工具
check_firewall_tool() {
if [ -x "$bin_nft" ]; then
nftables_ver=1
dbg "检测到nftables: $bin_nft"
elif [ -x "$bin_iptables" ] && [ -x "$bin_ip6tables" ]; then
iptables_ver=1
dbg "检测到iptables: $bin_iptables, $bin_ip6tables"
else
info "错误: 未找到可用的防火墙工具"
return 1
fi
return 0
}
# 初始化防火墙
init_firewall() {
check_firewall_tool || return 1
info "初始化防火墙规则 (模式: $list_type, 强度: $chain)"
if [ -n "$nftables_ver" ]; then
# 使用nftables
dbg "初始化nftables"
# 删除可能存在的旧表
nft delete table inet timecontrol 2>/dev/null
sleep 1
# 创建新表
nft add table inet timecontrol
nft add chain inet timecontrol forward "{ type filter hook forward priority -100; policy accept; }"
# 创建黑名单集合
nft add set inet timecontrol blacklist "{ type ipv4_addr; flags interval; }"
nft add set inet timecontrol blacklist6 "{ type ipv6_addr; flags interval; }"
nft add set inet timecontrol blacklist_mac "{ type ether_addr; }"
# 添加规则(黑名单模式:匹配到就DROP)
nft add rule inet timecontrol forward ip saddr @blacklist drop
nft add rule inet timecontrol forward ip6 saddr @blacklist6 drop
nft add rule inet timecontrol forward ether saddr @blacklist_mac drop
# 强控制模式
if [ "$StrongCHAIN" -eq 1 ]; then
nft add chain inet timecontrol input "{ type filter hook input priority -100; policy accept; }"
nft add rule inet timecontrol input ip saddr @blacklist drop
nft add rule inet timecontrol input ip6 saddr @blacklist6 drop
nft add rule inet timecontrol input ether saddr @blacklist_mac drop
dbg "已启用强控制模式 (INPUT链)"
fi
info "nftables初始化完成"
elif [ -n "$iptables_ver" ]; then
# 使用iptables
dbg "初始化iptables"
# 创建ipset(如果不存在)
ipset create timecontrol_blacklist hash:net 2>/dev/null || {
ipset flush timecontrol_blacklist
dbg "已存在的ipset timecontrol_blacklist已清空"
}
ipset create timecontrol_blacklist6 hash:net family inet6 2>/dev/null || {
ipset flush timecontrol_blacklist6
dbg "已存在的ipset timecontrol_blacklist6已清空"
}
# 删除可能存在的旧规则
iptables -D FORWARD -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null
ip6tables -D FORWARD -m set --match-set timecontrol_blacklist6 src -j DROP 2>/dev/null
# 添加新规则(黑名单模式)
iptables -I FORWARD -m set --match-set timecontrol_blacklist src -j DROP
ip6tables -I FORWARD -m set --match-set timecontrol_blacklist6 src -j DROP
dbg "已添加FORWARD规则"
# 强控制模式
if [ "$StrongCHAIN" -eq 1 ]; then
iptables -D INPUT -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null
ip6tables -D INPUT -m set --match-set timecontrol_blacklist6 src -j DROP 2>/dev/null
iptables -I INPUT -m set --match-set timecontrol_blacklist src -j DROP
ip6tables -I INPUT -m set --match-set timecontrol_blacklist6 src -j DROP
dbg "已启用强控制模式 (INPUT链)"
fi
info "iptables初始化完成"
fi
return 0
}
# 停止防火墙规则
stop_firewall() {
info "停止防火墙规则"
if [ -n "$nftables_ver" ]; then
nft delete table inet timecontrol 2>/dev/null && info "nftables规则已删除"
fi
if [ -n "$iptables_ver" ]; then
# 删除iptables规则
iptables -D FORWARD -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null
iptables -D INPUT -m set --match-set timecontrol_blacklist src -j DROP 2>/dev/null
ip6tables -D FORWARD -m set --match-set timecontrol_blacklist6 src -j DROP 2>/dev/null
ip6tables -D INPUT -m set --match-set timecontrol_blacklist6 src -j DROP 2>/dev/null
# 删除ipset
ipset destroy timecontrol_blacklist 2>/dev/null
ipset destroy timecontrol_blacklist6 2>/dev/null
info "iptables规则已删除"
fi
# 清理ID列表
rm -f "$IDLIST"
}
# 添加设备到防火墙
add_device() {
local id="$1"
local target=$(config_t_get device mac "$id")
[ -z "$target" ] && {
dbg "添加设备失败: ID $id 的目标地址为空"
return
}
local comment=$(config_t_get device comment "$id" "设备$id")
info "添加设备到防火墙: $comment ($target)"
local parsed_result=$(parse_target "$target")
if [ $? -ne 0 ]; then
info "添加失败: 无法解析地址 $target"
return
fi
IFS=':' read -r type subtype value <<< "$parsed_result"
dbg "解析结果: type=$type, subtype=$subtype, value=$value"
if [ -n "$nftables_ver" ]; then
# nftables处理
case "$type" in
"ipv4")
nft add element inet timecontrol blacklist "{ $value }" 2>&1 | while read line; do dbg "nft: $line"; done
dbg "已添加到nftables黑名单(IPv4): $value"
;;
"ipv6")
nft add element inet timecontrol blacklist6 "{ $value }" 2>&1 | while read line; do dbg "nft: $line"; done
dbg "已添加到nftables黑名单(IPv6): $value"
;;
"mac")
nft add element inet timecontrol blacklist_mac "{ $value }" 2>&1 | while read line; do dbg "nft: $line"; done
dbg "已添加到nftables黑名单(MAC): $value"
;;
esac
elif [ -n "$iptables_ver" ]; then
# iptables处理
case "$type" in
"ipv4")
ipset add timecontrol_blacklist "$value" 2>&1 | while read line; do dbg "ipset: $line"; done
dbg "已添加到ipset黑名单(IPv4): $value"
;;
"ipv6")
ipset add timecontrol_blacklist6 "$value" 2>&1 | while read line; do dbg "ipset: $line"; done
dbg "已添加到ipset黑名单(IPv6): $value"
;;
"mac")
# iptables不支持MAC地址直接过滤,记录日志
info "警告: iptables不支持MAC地址过滤,设备 $target 可能无法被阻止"
;;
esac
fi
# 强控制模式清理连接
if [ "$StrongCHAIN" -eq 1 ]; then
dbg "强控制模式,清理现有连接"
flush_connections "$target"
fi
# 验证规则
verify_firewall_rule "$target"
}
# 验证防火墙规则
verify_firewall_rule() {
local target="$1"
dbg "验证防火墙规则: $target"
if [ -n "$nftables_ver" ]; then
nft list table inet timecontrol 2>/dev/null | grep -q "$target" && {
dbg "验证成功: $target 在nftables规则中"
return 0
}
elif [ -n "$iptables_ver" ]; then
ipset test timecontrol_blacklist "$target" 2>/dev/null && {
dbg "验证成功: $target 在ipset中"
return 0
}
fi
dbg "验证失败: $target 不在防火墙规则中"
return 1
}
# 从防火墙移除设备
del_device() {
local id="$1"
local target=$(config_t_get device mac "$id")
[ -z "$target" ] && {
dbg "移除设备失败: ID $id 的目标地址为空"
return
}
local comment=$(config_t_get device comment "$id" "设备$id")
info "从防火墙移除设备: $comment ($target)"
local parsed_result=$(parse_target "$target")
[ $? -eq 0 ] || {
info "移除失败: 无法解析地址 $target"
return
}
IFS=':' read -r type subtype value <<< "$parsed_result"
if [ -n "$nftables_ver" ]; then
case "$type" in
"ipv4")
nft delete element inet timecontrol blacklist "{ $value }" 2>/dev/null
dbg "已从nftables移除(IPv4): $value"
;;
"ipv6")
nft delete element inet timecontrol blacklist6 "{ $value }" 2>/dev/null
dbg "已从nftables移除(IPv6): $value"
;;
"mac")
nft delete element inet timecontrol blacklist_mac "{ $value }" 2>/dev/null
dbg "已从nftables移除(MAC): $value"
;;
esac
elif [ -n "$iptables_ver" ]; then
case "$type" in
"ipv4")
ipset del timecontrol_blacklist "$value" 2>/dev/null
dbg "已从ipset移除(IPv4): $value"
;;
"ipv6")
ipset del timecontrol_blacklist6 "$value" 2>/dev/null
dbg "已从ipset移除(IPv6): $value"
;;
esac
fi
}
# 显示防火墙状态
show_firewall_status() {
echo ""
echo "防火墙状态:"
echo "控制模式: $list_type"
echo "控制强度: $chain $( [ "$StrongCHAIN" -eq 1 ] && echo "(强控制)" )"
echo ""
if [ -n "$nftables_ver" ]; then
echo "nftables规则:"
nft list table inet timecontrol 2>/dev/null || echo " 未找到timecontrol表"
elif [ -n "$iptables_ver" ]; then
echo "iptables规则:"
echo "FORWARD链:"
iptables -L FORWARD -n | grep -i timecontrol || echo " 未找到timecontrol规则"
ip6tables -L FORWARD -n | grep -i timecontrol || echo " 未找到IPv6 timecontrol规则"
if [ "$StrongCHAIN" -eq 1 ]; then
echo ""
echo "INPUT链:"
iptables -L INPUT -n | grep -i timecontrol || echo " 未找到timecontrol规则"
ip6tables -L INPUT -n | grep -i timecontrol || echo " 未找到IPv6 timecontrol规则"
fi
echo ""
echo "ipset内容:"
ipset list timecontrol_blacklist 2>/dev/null | head -20 || echo " timecontrol_blacklist未找到"
echo ""
ipset list timecontrol_blacklist6 2>/dev/null | head -20 || echo " timecontrol_blacklist6未找到"
fi
}
# 诊断函数
diagnose() {
echo ""
echo "=== 时间控制系统诊断 ==="
echo ""
# 检查服务
echo "1. 服务状态:"
if ps | grep -q "timecontrolctrl"; then
echo " ✓ timecontrolctrl 正在运行"
else
echo " ✗ timecontrolctrl 未运行"
fi
# 检查配置文件
echo ""
echo "2. 配置文件:"
if [ -f "/etc/config/timecontrol" ]; then
echo " ✓ 配置文件存在"
uci show timecontrol 2>/dev/null | grep -c "device" | while read count; do
echo " 配置了 $count 个设备"
done
else
echo " ✗ 配置文件不存在"
fi
# 检查防火墙工具
echo ""
echo "3. 防火墙工具:"
if [ -x "$bin_nft" ]; then
echo " ✓ nftables: $bin_nft"
echo " 版本: $($bin_nft --version 2>/dev/null | head -1)"
elif [ -x "$bin_iptables" ]; then
echo " ✓ iptables: $bin_iptables"
echo " 版本: $($bin_iptables --version 2>/dev/null | head -1)"
else
echo " ✗ 未找到防火墙工具"
fi
# 显示当前规则
show_firewall_status
# 检查ID列表
echo ""
echo "4. 当前控制列表:"
if [ -f "$IDLIST" ] && [ -s "$IDLIST" ]; then
echo " 当前禁止的设备:"
cat "$IDLIST" | sed 's/!//g' | while read id; do
local target=$(config_t_get device mac "$id")
local comment=$(config_t_get device comment "$id" "设备$id")
echo " ID$id: $comment ($target)"
done
else
echo " 当前没有设备被禁止"
fi
echo ""
echo "=== 诊断完成 ==="
}
# 主命令处理
case "$crrun" in
"start")
info "启动时间控制"
stop_firewall
init_firewall
if [ $? -eq 0 ]; then
info "时间控制启动成功"
show_firewall_status
else
info "时间控制启动失败"
fi
;;
"stop")
info "停止时间控制"
stop_firewall
info "时间控制已停止"
;;
"add")
[ -z "$crid" ] && {
echo "错误: 需要指定设备ID"
exit 1
}
info "添加设备控制: ID=$crid"
add_device "$crid"
show_firewall_status
;;
"del")
[ -z "$crid" ] && {
echo "错误: 需要指定设备ID"
exit 1
}
info "移除设备控制: ID=$crid"
del_device "$crid"
show_firewall_status
;;
"status")
show_firewall_status
;;
"diagnose")
diagnose
;;
"test")
# 测试地址解析
echo "测试地址解析:"
for test in "192.168.1.100" "192.168.1.0/24" "00:11:22:33:44:55" "invalid"; do
echo -n "$test: "
if parse_target "$test" >/dev/null; then
echo "✓ 有效"
parse_target "$test"
else
echo "✗ 无效"
fi
done
;;
"flush")
# 清理所有连接
info "清理所有连接"
if [ -x "$bin_conntrack" ]; then
$bin_conntrack -F
info "连接已清理"
else
info "conntrack不可用"
fi
;;
"help"|"")
echo "时间控制系统命令工具"
echo ""
echo "用法: $0 {start|stop|add <id>|del <id>|status|diagnose|test|flush|help}"
echo ""
echo "命令说明:"
echo " start - 初始化防火墙规则"
echo " stop - 停止并清理所有防火墙规则"
echo " add <id> - 添加设备到控制列表"
echo " del <id> - 从控制列表移除设备"
echo " status - 显示防火墙状态"
echo " diagnose - 系统诊断"
echo " test - 测试地址解析"
echo " flush - 清理所有网络连接"
echo " help - 显示此帮助信息"
;;
*)
echo "错误: 未知命令 '$crrun'"
echo "使用: $0 help 查看帮助"
exit 1
;;
esac
@@ -1,117 +0,0 @@
#!/bin/bash
# 时间控制日志查看工具
NAME=timecontrol
LOG_FILE="/var/log/$NAME.log"
STATUS_LOG="/var/lib/$NAME/status.log"
CONNECTION_LOG="/var/lib/$NAME/connections.log"
show_realtime_log() {
echo "正在显示实时日志,按 Ctrl+C 退出..."
echo ""
tail -f "$LOG_FILE" | while read line; do
# 高亮显示重要信息
if echo "$line" | grep -q "STATUS-CHANGE\|TIME_EXCEEDED\|RESET"; then
echo -e "\033[1;31m$line\033[0m" # 红色显示重要变更
elif echo "$line" | grep -q "ALLOW_ACCESS\|解除限制"; then
echo -e "\033[1;32m$line\033[0m" # 绿色显示允许访问
elif echo "$line" | grep -q "BLOCK_ACCESS\|添加限制"; then
echo -e "\033[1;33m$line\033[0m" # 黄色显示禁止访问
else
echo "$line"
fi
done
}
show_status_log() {
echo "最近状态变更记录:"
echo "────────────────────────────────────────────────────────────────────"
if [ -f "$STATUS_LOG" ]; then
tail -n 20 "$STATUS_LOG" | while read line; do
local time=$(echo "$line" | cut -d']' -f1 | sed 's/\[//')
local message=$(echo "$line" | cut -d']' -f2-)
printf "%-20s %s\n" "$time" "$message"
done
else
echo "暂无状态记录"
fi
}
show_connection_log() {
echo "设备连接记录:"
echo "────────────────────────────────────────────────────────────────────"
echo "时间 设备 状态"
echo "────────────────────────────────────────────────────────────────────"
if [ -f "$CONNECTION_LOG" ]; then
tail -n 20 "$CONNECTION_LOG" | while read line; do
local timestamp=$(echo "$line" | cut -d',' -f1)
local target=$(echo "$line" | cut -d',' -f2)
local action=$(echo "$line" | cut -d',' -f3)
local time_str=$(date -d "@$timestamp" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$timestamp")
local action_text=""
case "$action" in
"connect") action_text="上线" ;;
"disconnect") action_text="下线" ;;
*) action_text="$action" ;;
esac
printf "%-20s %-18s %s\n" "$time_str" "$target" "$action_text"
done
else
echo "暂无连接记录"
fi
}
show_summary() {
local summary_file="/tmp/timecontrol_status.txt"
if [ -f "$summary_file" ]; then
cat "$summary_file"
else
echo "状态摘要文件不存在,正在生成..."
timecontrol status
fi
}
show_help() {
echo "时间控制日志查看工具"
echo ""
echo "用法: timecontrol-log {realtime|status|connections|summary|help}"
echo ""
echo "命令:"
echo " realtime - 实时显示日志(彩色高亮)"
echo " status - 显示状态变更记录"
echo " connections - 显示设备连接记录"
echo " summary - 显示状态摘要"
echo " help - 显示此帮助"
echo ""
echo "示例:"
echo " timecontrol-log realtime # 实时监控"
echo " timecontrol-log status # 查看状态变更"
}
case "$1" in
"realtime")
show_realtime_log
;;
"status")
show_status_log
;;
"connections")
show_connection_log
;;
"summary")
show_summary
;;
"help"|"")
show_help
;;
*)
echo "未知命令: $1"
show_help
exit 1
;;
esac
@@ -1,335 +0,0 @@
#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright 2022-2026 sirpdboy <herboy2008@gmail.com>
NAME=timecontrol
LOG_FILE="/var/log/timecontrol.log"
DEBUG=1
# 时长数据库目录
DURATION_DIR="/var/lib/timecontrol"
DURATION_DB="$DURATION_DIR/duration.db"
CONNECTION_LOG="$DURATION_DIR/connections.log"
# 状态文件
IDLIST="/var/$NAME.idlist"
STATUS_DB="$DURATION_DIR/status.db"
# 初始化目录
init_dirs() {
mkdir -p "$DURATION_DIR"
touch "$DURATION_DB" "$CONNECTION_LOG" "$STATUS_DB" 2>/dev/null
}
# 日志函数
dbg() {
[ "$DEBUG" -eq 1 ] && {
local d="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$d] CTRL-DEBUG: $@" >> "$LOG_FILE"
}
}
info() {
local d="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$d] CTRL-INFO: $@" >> "$LOG_FILE"
}
# 配置文件读取
config_t_get() {
local index=${3:-0}
local ret=$(uci -q get "${NAME}.@${1}[${index}].${2}")
echo "${ret:=${4}}"
}
# 获取启用设备
get_enabled_devices() {
uci show $NAME 2>/dev/null | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sort -n
}
# 时间检查函数
is_time_in_range() {
local start_time=$1
local end_time=$2
local current_time=$(date +%H:%M)
if [ "$start_time" = "$end_time" ]; then
return 0
elif [ "$start_time" \< "$end_time" ]; then
[ "$current_time" \> "$start_time" ] && [ "$current_time" \< "$end_time" ] && return 0
else
[ "$current_time" \> "$start_time" ] || [ "$current_time" \< "$end_time" ] && return 0
fi
return 1
}
is_weekday_in_range() {
local configured_weekdays=$1
local current_weekday=$(date +%u)
[ "$configured_weekdays" = "0" ] && return 0
for ww in $(echo $configured_weekdays | sed 's/,/ /g'); do
[ "$current_weekday" = "$ww" ] && return 0
done
return 1
}
# 时长管理
record_connection_time() {
local target="$1"
local action="$2"
local timestamp=$(date +%s)
echo "$timestamp,$target,$action" >> "$CONNECTION_LOG"
if [ "$action" = "connect" ]; then
if ! grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
echo "$target,$timestamp,0,0" >> "$DURATION_DB"
dbg "初始化时长: $target"
fi
elif [ "$action" = "disconnect" ]; then
if grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
local last_connect=$(grep "^$target," "$DURATION_DB" | cut -d',' -f2)
local total_used=$(grep "^$target," "$DURATION_DB" | cut -d',' -f3)
local last_reset=$(grep "^$target," "$DURATION_DB" | cut -d',' -f4)
local session_duration=$((timestamp - last_connect))
local new_total=$((total_used + session_duration))
sed -i "/^$target,/d" "$DURATION_DB"
echo "$target,$timestamp,$new_total,$last_reset" >> "$DURATION_DB"
fi
fi
}
get_connection_time() {
local target="$1"
local current_time=$(date +%s)
if grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
local last_connect=$(grep "^$target," "$DURATION_DB" | cut -d',' -f2)
local total_used=$(grep "^$target," "$DURATION_DB" | cut -d',' -f3)
if tail -n 5 "$CONNECTION_LOG" 2>/dev/null | grep -q "^[0-9]*,$target,connect$"; then
local session_duration=$((current_time - last_connect))
total_used=$((total_used + session_duration))
fi
echo "$total_used"
else
echo "0"
fi
}
# 重置检查
should_reset_duration() {
local target="$1"
local reset_cycle="$2"
local last_reset_file="$DURATION_DIR/last_reset_$target"
[ ! -f "$last_reset_file" ] && return 0
local last_reset=$(cat "$last_reset_file" 2>/dev/null)
local current_time=$(date +%s)
case "$reset_cycle" in
"daily")
local last_date=$(date -d "@$last_reset" +%Y%m%d 2>/dev/null || echo "0")
local current_date=$(date +%Y%m%d)
[ "$last_date" != "$current_date" ]
;;
"weekly")
local last_week=$(date -d "@$last_reset" +%Y%W 2>/dev/null || echo "0")
local current_week=$(date +%Y%W)
[ "$last_week" != "$current_week" ]
;;
"monthly")
local last_month=$(date -d "@$last_reset" +%Y%m 2>/dev/null || echo "0")
local current_month=$(date +%Y%m)
[ "$last_month" != "$current_month" ]
;;
*)
false
;;
esac
}
reset_duration_counter() {
local target="$1"
local reset_cycle="$2"
local current_time=$(date +%s)
if grep -q "^$target," "$DURATION_DB" 2>/dev/null; then
sed -i "/^$target,/d" "$DURATION_DB"
fi
echo "$target,$current_time,0,$current_time" >> "$DURATION_DB"
echo "$current_time" > "$DURATION_DIR/last_reset_$target"
info "重置时长: $target (周期: $reset_cycle)"
}
# 主检查函数
check_device_control() {
local id="$1"
local target=$(config_t_get device mac "$id")
local time_mode=$(config_t_get device time_mode "$id" "period")
local weekdays=$(config_t_get device week "$id" "0")
# 检查星期
is_weekday_in_range "$weekdays" || {
dbg "星期不允许: $target"
return 1
}
case "$time_mode" in
"period")
local start_time=$(config_t_get device timestart "$id" "00:00")
local end_time=$(config_t_get device timeend "$id" "00:00")
is_time_in_range "$start_time" "$end_time" || {
dbg "时间段外: $target ($start_time-$end_time)"
return 1
}
dbg "时间段内: $target"
return 0
;;
"duration")
local duration=$(config_t_get device duration "$id" "60")
local reset_cycle=$(config_t_get device reset_cycle "$id" "daily")
# 重置检查
if should_reset_duration "$target" "$reset_cycle"; then
reset_duration_counter "$target" "$reset_cycle"
fi
# 记录连接
local last_action=$(grep ",$target," "$CONNECTION_LOG" 2>/dev/null | tail -1 | cut -d',' -f3)
if [ "$last_action" != "connect" ]; then
record_connection_time "$target" "connect"
fi
# 检查时长
local used_seconds=$(get_connection_time "$target")
local used_minutes=$((used_seconds / 60))
local max_minutes=$duration
dbg "时长检查: $target 已用=${used_minutes}分钟, 限制=${max_minutes}分钟"
if [ "$used_minutes" -ge "$max_minutes" ]; then
dbg "已超时: $target"
return 1
fi
dbg "未超时: $target"
return 0
;;
"combined")
local start_time=$(config_t_get device timestart "$id" "00:00")
local end_time=$(config_t_get device timeend "$id" "00:00")
local use_duration=$(config_t_get device use_duration "$id" "0")
# 时间段检查
is_time_in_range "$start_time" "$end_time" || {
dbg "时间段外: $target"
return 1
}
# 时长检查
if [ "$use_duration" = "1" ]; then
local duration=$(config_t_get device duration "$id" "60")
local reset_cycle=$(config_t_get device reset_cycle "$id" "daily")
if should_reset_duration "$target" "$reset_cycle"; then
reset_duration_counter "$target" "$reset_cycle"
fi
local last_action=$(grep ",$target," "$CONNECTION_LOG" 2>/dev/null | tail -1 | cut -d',' -f3)
if [ "$last_action" != "connect" ]; then
record_connection_time "$target" "connect"
fi
local used_seconds=$(get_connection_time "$target")
local used_minutes=$((used_seconds / 60))
local max_minutes=$duration
if [ "$used_minutes" -ge "$max_minutes" ]; then
dbg "时间段内但已超时: $target"
return 1
fi
fi
dbg "时间段内允许: $target"
return 0
;;
*)
# 默认时间段控制
local start_time=$(config_t_get device timestart "$id" "00:00")
local end_time=$(config_t_get device timeend "$id" "00:00")
is_time_in_range "$start_time" "$end_time"
return $?
;;
esac
}
# 更新设备状态
update_device_status() {
local id="$1"
local should_allow="$2" # 0=允许, 1=禁止
local target=$(config_t_get device mac "$id")
local comment=$(config_t_get device comment "$id" "设备$id")
# 检查当前状态
local current_blocked=0
if [ -f "$IDLIST" ] && grep -q "!${id}!" "$IDLIST" 2>/dev/null; then
current_blocked=1
fi
dbg "设备状态: $target, 应该允许=$should_allow, 当前阻止=$current_blocked"
if [ "$should_allow" -eq 0 ]; then
# 应该允许
if [ "$current_blocked" -eq 1 ]; then
dbg "解除阻止: $target"
timecontrol del "$id"
sed -i "/!$id!/d" "$IDLIST" 2>/dev/null
info "允许上网: $comment ($target)"
record_connection_time "$target" "disconnect"
fi
else
# 应该阻止
if [ "$current_blocked" -eq 0 ]; then
dbg "添加阻止: $target"
timecontrol add "$id"
if ! grep -q "!$id!" "$IDLIST" 2>/dev/null; then
echo "!$id!" >> "$IDLIST"
fi
info "阻止上网: $comment ($target)"
record_connection_time "$target" "disconnect"
fi
fi
}
# 主处理循环
main_loop() {
info "时间控制守护进程启动"
init_dirs
while :; do
dbg "开始检查设备"
[ `uci show $NAME 2>/dev/null | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sort -n | wc -l` eq 0 ] && timecontrol stop && break
for id in $(get_enabled_devices); do
if check_device_control "$id"; then
update_device_status "$id" 0 # 允许
else
update_device_status "$id" 1 # 阻止
fi
done
sleep 60
done
}
# 启动
main_loop
@@ -1,32 +0,0 @@
#!/bin/sh
#
# Copyright (C) 2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-timecontrol
#
logfile="/var/log/timecontrol.log"
lang=$(uci get luci.main.lang 2>/dev/null)
if [ -z "$lang" ] || [[ "$lang" == "auto" ]]; then
lang=$(echo "${LANG:-${LANGUAGE:-${LC_ALL:-${LC_MESSAGES:-zh_cn}}}}" | awk -F'[ .@]' '{print tolower($1)}' | sed 's/-/_/' 2>/dev/null)
fi
translate() {
# 处理特殊字符
local lua_script=$(cat <<LUA
require "luci.i18n".setlanguage("$lang")
print(require "luci.i18n".translate([==[$1]==]))
LUA
)
lua -e "$lua_script"
}
if [ "$1" == "clear_log" ]; then
# 清空日志
>"${logfile}"
elif [ "$1" == "child" ]; then
shift
command_name=$1
shift
"$command_name" "$@"
fi
@@ -1,32 +0,0 @@
{
"admin/control/timecontrol": {
"title": "Time Control",
"order": 10,
"action": {
"type": "firstchild"
},
"acl": [ "read" ],
"depends": {
"acl": [ "luci-app-timecontrol" ]
},
"recurse": true
},
"admin/control/timecontrol/basic": {
"title": "Time Control",
"order": 10,
"action": {
"type": "view",
"path": "timecontrol/basic"
},
"acl": [ "read" ]
},
"admin/control/timecontrol/log": {
"title": "Log",
"order": 40,
"action": {
"type": "view",
"path": "timecontrol/log"
},
"acl": [ "read" ]
}
}
@@ -1,21 +1,11 @@
{
"luci-app-timecontrol": {
"description": "Grant UCI Internet time control for luci-app-timecontrol",
"read": {
"ubus": {
"file": ["exec", "list", "stat", "read"],
"uci": [ "*" ],
"timecontrol": ["*"]
}
},
"write": {
"ubus": {
"timecontrol": ["*"],
"file": ["write"],
"uci": ["*"]
}
}
}
}
"luci-app-timecontrol": {
"description": "Grant UCI access for luci-app-timecontrol",
"read": {
"uci": [ "timecontrol" ]
},
"write": {
"uci": [ "timecontrol" ]
}
}
}
@@ -0,0 +1,4 @@
{
"config": "timecontrol",
"init": "timecontrol"
}