-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathtools.sh
More file actions
354 lines (312 loc) · 10.1 KB
/
tools.sh
File metadata and controls
354 lines (312 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
#!/bin/sh
# /etc/tailscale/tools.sh
CONFIG_DIR="/etc/tailscale"
mkdir -p "$CONFIG_DIR"
LOG_FILE="/var/log/tailscale_install.log"
VERSION_FILE="$CONFIG_DIR/current_version"
NTF_CONF="$CONFIG_DIR/notify.conf"
INST_CONF="$CONFIG_DIR/install.conf"
MIRROR_LIST="$CONFIG_DIR/proxies.txt"
VALID_MIRRORS="$CONFIG_DIR/valid_proxies.txt"
TMP_VALID_MIRRORS="/tmp/valid_mirrors.tmp"
REMOTE_SCRIPTS_VERSION_FILE="$CONFIG_DIR/remote_ts_scripts_version"
TIME_OUT=30
# 架构探测(统一入口)
detect_arch() {
local arch_raw="${1:-$(uname -m)}"
local arch=""
local endian=""
case "$arch_raw" in
i386|i486|i586|i686)
arch="386"
;;
x86_64)
arch="amd64"
;;
armv7l|armv7|armhf|armv6l)
arch="arm"
;;
aarch64|arm64|armv8l)
arch="arm64"
;;
mips|mipsel|mipsel_24kc|mips64|mips64el)
arch="$arch_raw"
;;
*)
log_error "❌ 不支持的架构: $arch_raw"
log_error "❌ 请在 https://github.com/CH3NGYZ/small-tailscale-openwrt/issues 反馈此架构"
return 1
;;
esac
case "$arch" in
mips)
if command -v hexdump >/dev/null 2>&1 && command -v awk >/dev/null 2>&1; then
endian=$(echo -n I | hexdump -o 2>/dev/null | awk '{ print (substr($2,6,1)=="1") ? "le" : ""; exit }')
echo "mips${endian}"
else
echo "mips"
fi
;;
mipsel|mipsel_24kc)
echo "mipsle"
;;
mips64)
echo "mips64"
;;
mips64el)
echo "mips64le"
;;
*)
echo "$arch"
;;
esac
}
# 如果未在配置中指定 ARCH,则尝试自动探测一次(懒加载)
ensure_arch() {
if [ -n "$ARCH" ]; then
return 0
fi
ARCH="$(detect_arch)" || return 1
}
# GitHub 代理模式配置
set_direct_mode() {
CUSTOM_RELEASE_PROXY="https://github.com"
CUSTOM_RAW_PROXY="https://github.com"
CUSTOM_API_PROXY="https://api.github.com"
}
set_proxy_mode() {
CUSTOM_RELEASE_PROXY="https://gh.ch3ng.top"
CUSTOM_RAW_PROXY="https://gh.ch3ng.top"
CUSTOM_API_PROXY="https://ghapi.ch3ng.top"
}
# 根据配置自动设置模式
apply_github_mode() {
[ "$GITHUB_DIRECT" = "true" ] && set_direct_mode || set_proxy_mode
}
# 初始化日志系统
log_info() {
echo -n "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1"
[ $# -eq 2 ] || echo
}
log_warn() {
echo -n "[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1"
[ $# -eq 2 ] || echo
}
log_error() {
echo -n "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1"
[ $# -eq 2 ] || echo
}
# 安全加载配置文件
safe_source() {
local file="$1"
if [ -f "$file" ] && [ -s "$file" ]; then
. "$file"
else
log_warn "⚠️ 配置文件 $file 不存在或为空"
fi
}
webget() {
# $1 输出文件
# $2 URL
# $3 是否静默: echooff
# $4 禁止重定向: rediroff
local outfile="$1"
local url="$2"
local ua="Tailscale-Helper"
# 是否静默
local quiet=""
[ "$3" = "echooff" ] && quiet="-s"
# 是否禁用重定向
local redirect="-L"
[ "$4" = "rediroff" ] && redirect=""
# ---- 优先使用 curl ----
if command -v curl >/dev/null 2>&1; then
http_code=$(timeout "$TIME_OUT" curl $quiet $redirect \
-A "$ua" \
-w "%{http_code}" \
-o "$outfile" \
"$url" 2>/dev/null)
case "$http_code" in 2*) return 0 ;; *) return 1 ;; esac
fi
# ---- 回退到 wget ----
if command -v wget >/dev/null 2>&1; then
local q="--show-progress"
[ "$3" = "echooff" ] && q="-q"
local r=""
[ "$4" = "rediroff" ] && r="--max-redirect=0"
# wget 不直接返回 HTTP 状态码,需要解析 headers
headers=$(mktemp)
timeout "$TIME_OUT" wget $q $r \
--server-response --no-check-certificate \
--header="User-Agent: $ua" \
-O "$outfile" "$url" 2>"$headers"
# 提取最后的 HTTP 状态码
http_code=$(grep -oE 'HTTP/[0-9\.]+ [0-9]+' "$headers" | tail -n1 | awk '{print $2}')
rm -f "$headers"
case "$http_code" in 2*) return 0 ;; *) return 1 ;; esac
fi
log_error "❌ curl 和 wget 都不存在"
return 1
}
# 校验函数, 自动根据长度判断类型, 支持 openssl 回退
verify_checksum() {
local file=$1
local expected=$2
local actual=""
if [ ${#expected} -eq 64 ]; then
if command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$file" | awk '{print $1}')
elif command -v openssl >/dev/null 2>&1; then
actual=$(openssl dgst -sha256 "$file" | awk '{print $2}')
else
log_warn "⚠️ 缺少 sha256sum 或 openssl,跳过校验"
return 0
fi
elif [ ${#expected} -eq 32 ]; then
if command -v md5sum >/dev/null 2>&1; then
actual=$(md5sum "$file" | awk '{print $1}')
elif command -v openssl >/dev/null 2>&1; then
actual=$(openssl dgst -md5 "$file" | awk '{print $2}')
else
log_warn "⚠️ 缺少 md5sum 或 openssl,跳过校验"
return 0
fi
else
log_warn "⚠️ 未知校验长度 (${#expected}),跳过校验"
return 0
fi
if [ "$expected" = "$actual" ]; then
log_info "✅ 校验通过"
return 0
else
log_error "❌ 校验失败!预期: $expected, 实际: $actual"
return 1
fi
}
# 读取系统启动时间(优先 /proc/uptime,提供更细粒度的耗时计算)
now_uptime() {
if [ -r /proc/uptime ]; then
awk '{print $1}' /proc/uptime
else
date +%s
fi
}
# 计算耗时(保留 2 位小数)
calc_elapsed() {
local start="$1"
local end="$2"
awk -v s="$start" -v e="$end" 'BEGIN{printf "%.2f", (e - s)}'
}
sha256_file() {
local file="$1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$file" | awk '{print $1}'
return 0
fi
if command -v openssl >/dev/null 2>&1; then
openssl dgst -sha256 "$file" | awk '{print $2}'
return 0
fi
return 1
}
md5_file() {
local file="$1"
if command -v md5sum >/dev/null 2>&1; then
md5sum "$file" | awk '{print $1}'
return 0
fi
if command -v openssl >/dev/null 2>&1; then
openssl dgst -md5 "$file" | awk '{print $2}'
return 0
fi
return 1
}
# URL 编码函数 (纯 shell 内建操作, 无外部命令依赖)
urlencode() {
local str="$1" c rest
rest="$str"
while [ -n "$rest" ]; do
c="${rest%"${rest#?}"}"
rest="${rest#?}"
case "$c" in
[A-Za-z0-9._~-]) printf '%s' "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
}
send_notify() {
local host_name="$(uci get system.@system[0].hostname 2>/dev/null || echo OpenWrt)"
local title="$host_name Tailscale通知"
local user_title="$1"
shift
local body_content="$(printf "%s\n" "$@")"
local content="$(printf "%s\n%s" "$user_title" "$body_content")"
safe_source "$NTF_CONF" # 引入配置文件
# 通用发送函数(curl 优先,wget 兼容)
# 用法: send_via_curl_or_wget URL [DATA] [METHOD] [HEADER]
# METHOD: GET / POST(默认当有DATA时)
send_via_curl_or_wget() {
local url="$1"
local data="$2"
local method="$3"
local headers="$4"
if command -v curl > /dev/null; then
if [ "$method" = "GET" ] || [ -z "$data" ]; then
curl -sS -A "Tailscale-Helper" "$url"
else
curl -sS -A "Tailscale-Helper" -X POST "$url" -d "$data" -H "$headers"
fi
elif command -v wget > /dev/null; then
if [ "$method" = "GET" ] || [ -z "$data" ]; then
wget --quiet --header="User-Agent: Tailscale-Helper" -O /dev/null "$url"
else
echo "$data" | wget --quiet --header="User-Agent: Tailscale-Helper" --method=POST --body-file=- --header="$headers" "$url"
fi
else
log_error "❌ curl 和 wget 都不可用,无法发送通知"
return 1
fi
}
# Server酱
if [ "$NOTIFY_SERVERCHAN" = "1" ] && [ -n "$SERVERCHAN_KEY" ]; then
data="text=$title&desp=$content"
send_via_curl_or_wget "https://sctapi.ftqq.com/$SERVERCHAN_KEY.send" "$data" "POST" && log_info "✅ Server酱 通知已发送"
fi
# Bark
if [ "$NOTIFY_BARK" = "1" ] && [ -n "$BARK_KEY" ]; then
title_enc=$(urlencode "$title")
content_enc=$(urlencode "$content")
bark_url="${BARK_KEY}/${title_enc}/${content_enc}"
send_via_curl_or_wget "$bark_url" "" "GET" && log_info "✅ Bark 通知已发送"
fi
# ntfy
if [ "$NOTIFY_NTFY" = "1" ] && [ -n "$NTFY_KEY" ]; then
headers="Title: $title"
send_via_curl_or_wget "https://ntfy.sh/$NTFY_KEY" "$content" "POST" "$headers" && log_info "✅ NTFY 通知已发送"
fi
# PushPlus
if [ "$NOTIFY_PUSHPLUS" = "1" ] && [ -n "$PUSHPLUS_TOKEN" ]; then
pushplus_data="{\"token\":\"$PUSHPLUS_TOKEN\",\"title\":\"$title\",\"content\":\"$content\",\"template\":\"txt\"}"
send_via_curl_or_wget "http://www.pushplus.plus/send" "$pushplus_data" "POST" "Content-Type: application/json" && log_info "✅ PushPlus 通知已发送"
fi
# 无任何通知方式启用
if [ "$NOTIFY_SERVERCHAN" != "1" ] && [ "$NOTIFY_BARK" != "1" ] && [ "$NOTIFY_NTFY" != "1" ] && [ "$NOTIFY_PUSHPLUS" != "1" ]; then
log_error "❌ 未启用任何通知方式"
fi
}
# 检查是否需要发送通知
should_notify() {
local notify_type=$1
safe_source "$NTF_CONF"
local notify_var
case "$notify_type" in
"update") notify_var="$NOTIFY_UPDATE" ;;
"mirror_fail") notify_var="$NOTIFY_MIRROR_FAIL" ;;
"emergency") notify_var="$NOTIFY_EMERGENCY" ;;
*)
log_error "❌ 未知通知类型: $notify_type"
return 1
;;
esac
[ "$notify_var" = "1" ]
}