clashctl.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. # shellcheck disable=SC2148
  2. # shellcheck disable=SC2155
  3. _set_system_proxy() {
  4. local auth=$(sudo "$BIN_YQ" '.authentication[0] // ""' "$CLASH_CONFIG_RUNTIME")
  5. [ -n "$auth" ] && auth=$auth@
  6. local bind_addr=$(sudo "$BIN_YQ" '.bind-address // ""' "$CLASH_CONFIG_RUNTIME")
  7. case $bind_addr in "" | "*" | "0.0.0.0") bind_addr=127.0.0.1 ;; esac
  8. local http_proxy_addr="http://${auth}${bind_addr}:${MIXED_PORT}"
  9. local socks_proxy_addr="socks5h://${auth}${bind_addr}:${MIXED_PORT}"
  10. local no_proxy_addr="localhost,127.0.0.1,::1"
  11. export http_proxy=$http_proxy_addr
  12. export https_proxy=$http_proxy
  13. export HTTP_PROXY=$http_proxy
  14. export HTTPS_PROXY=$http_proxy
  15. export all_proxy=$socks_proxy_addr
  16. export ALL_PROXY=$all_proxy
  17. export no_proxy=$no_proxy_addr
  18. export NO_PROXY=$no_proxy
  19. }
  20. _unset_system_proxy() {
  21. unset http_proxy
  22. unset https_proxy
  23. unset HTTP_PROXY
  24. unset HTTPS_PROXY
  25. unset all_proxy
  26. unset ALL_PROXY
  27. unset no_proxy
  28. unset NO_PROXY
  29. }
  30. function clashon() {
  31. _get_proxy_port
  32. systemctl is-active "$BIN_KERNEL_NAME" >&/dev/null || {
  33. sudo systemctl start "$BIN_KERNEL_NAME" >/dev/null || {
  34. _failcat '启动失败: 执行 clashstatus 查看日志'
  35. return 1
  36. }
  37. }
  38. clashproxy status >/dev/null && _set_system_proxy
  39. _okcat '已开启代理环境'
  40. }
  41. watch_proxy() {
  42. # 新开交互式shell,且无代理变量时
  43. [ -z "$http_proxy" ] && [[ $- == *i* ]] && {
  44. # root用户自动开启代理环境(普通用户会触发sudo验证密码导致卡住)
  45. _is_root && clashon
  46. }
  47. }
  48. function clashoff() {
  49. sudo systemctl stop "$BIN_KERNEL_NAME" && _okcat '已关闭代理环境' ||
  50. _failcat '关闭失败: 执行 "clashstatus" 查看日志' || return 1
  51. _unset_system_proxy
  52. }
  53. clashrestart() {
  54. { clashoff && clashon; } >&/dev/null
  55. }
  56. function clashproxy() {
  57. case "$1" in
  58. on)
  59. systemctl is-active "$BIN_KERNEL_NAME" >&/dev/null || {
  60. _failcat '代理程序未运行,请执行 clashon 开启代理环境'
  61. return 1
  62. }
  63. sudo "$BIN_YQ" -i '.system-proxy.enable = true' "$CLASH_CONFIG_MIXIN"
  64. _set_system_proxy
  65. _okcat '已开启系统代理'
  66. ;;
  67. off)
  68. sudo "$BIN_YQ" -i '.system-proxy.enable = false' "$CLASH_CONFIG_MIXIN"
  69. _unset_system_proxy
  70. _okcat '已关闭系统代理'
  71. ;;
  72. status)
  73. local system_proxy_status=$(sudo "$BIN_YQ" '.system-proxy.enable' "$CLASH_CONFIG_MIXIN" 2>/dev/null)
  74. [ "$system_proxy_status" = "false" ] && {
  75. _failcat "系统代理:关闭"
  76. return 1
  77. }
  78. _okcat "系统代理:开启
  79. http_proxy: $http_proxy
  80. socks_proxy:$all_proxy"
  81. ;;
  82. *)
  83. cat <<EOF
  84. 用法: clashproxy [on|off|status]
  85. on 开启系统代理
  86. off 关闭系统代理
  87. status 查看系统代理状态
  88. EOF
  89. ;;
  90. esac
  91. }
  92. function clashstatus() {
  93. sudo systemctl status "$BIN_KERNEL_NAME" "$@"
  94. }
  95. function clashui() {
  96. _get_ui_port
  97. # 公网ip
  98. # ifconfig.me
  99. local query_url='api64.ipify.org'
  100. local public_ip=$(curl -s --noproxy "*" --location --max-time 2 $query_url)
  101. local public_address="http://${public_ip:-公网}:${EXT_PORT}/ui"
  102. local local_ip=$EXT_IP
  103. local local_address="http://${local_ip}:${EXT_PORT}/ui"
  104. printf "\n"
  105. printf "╔═══════════════════════════════════════════════╗\n"
  106. printf "║ %s ║\n" "$(_okcat 'Web 控制台')"
  107. printf "║═══════════════════════════════════════════════║\n"
  108. printf "║ ║\n"
  109. printf "║ 🔓 注意放行端口:%-5s ║\n" "$EXT_PORT"
  110. printf "║ 🏠 内网:%-31s ║\n" "$local_address"
  111. printf "║ 🌏 公网:%-31s ║\n" "$public_address"
  112. printf "║ ☁️ 公共:%-31s ║\n" "$URL_CLASH_UI"
  113. printf "║ ║\n"
  114. printf "╚═══════════════════════════════════════════════╝\n"
  115. printf "\n"
  116. }
  117. _merge_config_restart() {
  118. local backup="/tmp/rt.backup"
  119. sudo cat "$CLASH_CONFIG_RUNTIME" 2>/dev/null | sudo tee $backup >&/dev/null
  120. sudo "$BIN_YQ" eval-all '. as $item ireduce ({}; . *+ $item) | (.. | select(tag == "!!seq")) |= unique' \
  121. "$CLASH_CONFIG_MIXIN" "$CLASH_CONFIG_RAW" "$CLASH_CONFIG_MIXIN" | sudo tee "$CLASH_CONFIG_RUNTIME" >&/dev/null
  122. _valid_config "$CLASH_CONFIG_RUNTIME" || {
  123. sudo cat $backup | sudo tee "$CLASH_CONFIG_RUNTIME" >&/dev/null
  124. _error_quit "验证失败:请检查 Mixin 配置"
  125. }
  126. clashrestart
  127. }
  128. function clashsecret() {
  129. case "$#" in
  130. 0)
  131. _okcat "当前密钥:$(sudo "$BIN_YQ" '.secret // ""' "$CLASH_CONFIG_RUNTIME")"
  132. ;;
  133. 1)
  134. sudo "$BIN_YQ" -i ".secret = \"$1\"" "$CLASH_CONFIG_MIXIN" || {
  135. _failcat "密钥更新失败,请重新输入"
  136. return 1
  137. }
  138. _merge_config_restart
  139. _okcat "密钥更新成功,已重启生效"
  140. ;;
  141. *)
  142. _failcat "密钥不要包含空格或使用引号包围"
  143. ;;
  144. esac
  145. }
  146. _tunstatus() {
  147. local tun_status=$(sudo "$BIN_YQ" '.tun.enable' "${CLASH_CONFIG_RUNTIME}")
  148. # shellcheck disable=SC2015
  149. [ "$tun_status" = 'true' ] && _okcat 'Tun 状态:启用' || _failcat 'Tun 状态:关闭'
  150. }
  151. _tunoff() {
  152. _tunstatus >/dev/null || return 0
  153. sudo "$BIN_YQ" -i '.tun.enable = false' "$CLASH_CONFIG_MIXIN"
  154. _merge_config_restart && _okcat "Tun 模式已关闭"
  155. }
  156. _tunon() {
  157. _tunstatus 2>/dev/null && return 0
  158. sudo "$BIN_YQ" -i '.tun.enable = true' "$CLASH_CONFIG_MIXIN"
  159. _merge_config_restart
  160. sleep 0.5s
  161. sudo journalctl -u "$BIN_KERNEL_NAME" --since "1 min ago" | grep -E -m1 'unsupported kernel version|Start TUN listening error' && {
  162. _tunoff >&/dev/null
  163. _error_quit '不支持的内核版本'
  164. }
  165. _okcat "Tun 模式已开启"
  166. }
  167. function clashtun() {
  168. case "$1" in
  169. on)
  170. _tunon
  171. ;;
  172. off)
  173. _tunoff
  174. ;;
  175. *)
  176. _tunstatus
  177. ;;
  178. esac
  179. }
  180. function clashupdate() {
  181. local url=$(cat "$CLASH_CONFIG_URL")
  182. local is_auto
  183. case "$1" in
  184. auto)
  185. is_auto=true
  186. [ -n "$2" ] && url=$2
  187. ;;
  188. log)
  189. sudo tail "${CLASH_UPDATE_LOG}" 2>/dev/null || _failcat "暂无更新日志"
  190. return 0
  191. ;;
  192. *)
  193. [ -n "$1" ] && url=$1
  194. ;;
  195. esac
  196. # 如果没有提供有效的订阅链接(url为空或者不是http开头),则使用默认配置文件
  197. [ "${url:0:4}" != "http" ] && {
  198. _failcat "没有提供有效的订阅链接:使用 ${CLASH_CONFIG_RAW} 进行更新..."
  199. url="file://$CLASH_CONFIG_RAW"
  200. }
  201. # 如果是自动更新模式,则设置定时任务
  202. [ "$is_auto" = true ] && {
  203. sudo grep -qs 'clashupdate' "$CLASH_CRON_TAB" || echo "0 0 */2 * * $_SHELL -i -c 'clashupdate $url'" | sudo tee -a "$CLASH_CRON_TAB" >&/dev/null
  204. _okcat "已设置定时更新订阅" && return 0
  205. }
  206. _okcat '👌' "正在下载:原配置已备份..."
  207. sudo cat "$CLASH_CONFIG_RAW" | sudo tee "$CLASH_CONFIG_RAW_BAK" >&/dev/null
  208. _rollback() {
  209. _failcat '🍂' "$1"
  210. sudo cat "$CLASH_CONFIG_RAW_BAK" | sudo tee "$CLASH_CONFIG_RAW" >&/dev/null
  211. _failcat '❌' "[$(date +"%Y-%m-%d %H:%M:%S")] 订阅更新失败:$url" 2>&1 | sudo tee -a "${CLASH_UPDATE_LOG}" >&/dev/null
  212. _error_quit
  213. }
  214. _download_config "$CLASH_CONFIG_RAW" "$url" || _rollback "下载失败:已回滚配置"
  215. _valid_config "$CLASH_CONFIG_RAW" || _rollback "转换失败:已回滚配置,转换日志:$BIN_SUBCONVERTER_LOG"
  216. _merge_config_restart && _okcat '🍃' '订阅更新成功'
  217. echo "$url" | sudo tee "$CLASH_CONFIG_URL" >&/dev/null
  218. _okcat '✅' "[$(date +"%Y-%m-%d %H:%M:%S")] 订阅更新成功:$url" | sudo tee -a "${CLASH_UPDATE_LOG}" >&/dev/null
  219. }
  220. function clashmixin() {
  221. case "$1" in
  222. -e)
  223. sudo vim "$CLASH_CONFIG_MIXIN" && {
  224. _merge_config_restart && _okcat "配置更新成功,已重启生效"
  225. }
  226. ;;
  227. -r)
  228. less -f "$CLASH_CONFIG_RUNTIME"
  229. ;;
  230. *)
  231. less -f "$CLASH_CONFIG_MIXIN"
  232. ;;
  233. esac
  234. }
  235. function clashupgrade() {
  236. case "$1" in
  237. -h | --help)
  238. cat <<EOF
  239. - 升级当前版本
  240. clashupgrade
  241. - 升级到稳定版
  242. clashupgrade release
  243. - 升级到测试版
  244. clashupgrade alpha
  245. EOF
  246. return 0
  247. ;;
  248. release)
  249. channel="release"
  250. ;;
  251. alpha)
  252. channel="alpha"
  253. ;;
  254. *)
  255. channel=""
  256. ;;
  257. esac
  258. _okcat "请求内核升级..."
  259. _get_ui_port
  260. local secret=$(sudo "$BIN_YQ" '.secret // ""' "$CLASH_CONFIG_RUNTIME")
  261. local res=$(
  262. curl -X POST \
  263. --silent \
  264. --noproxy "*" \
  265. --location \
  266. -H "Authorization: Bearer $secret" \
  267. "http://${EXT_IP}:${EXT_PORT}/upgrade?channel=$channel"
  268. )
  269. grep -qs '"status":"ok"' <<<"$res" && {
  270. _okcat "内核升级成功"
  271. return 0
  272. }
  273. grep 'already using latest version' <<<"$res" && {
  274. _okcat "已是最新版本"
  275. return 0
  276. }
  277. _failcat "升级请求失败,请检查网络或稍后重试"
  278. }
  279. function clashctl() {
  280. case "$1" in
  281. on)
  282. clashon
  283. ;;
  284. off)
  285. clashoff
  286. ;;
  287. ui)
  288. clashui
  289. ;;
  290. status)
  291. shift
  292. clashstatus "$@"
  293. ;;
  294. proxy)
  295. shift
  296. clashproxy "$@"
  297. ;;
  298. tun)
  299. shift
  300. clashtun "$@"
  301. ;;
  302. mixin)
  303. shift
  304. clashmixin "$@"
  305. ;;
  306. secret)
  307. shift
  308. clashsecret "$@"
  309. ;;
  310. update)
  311. shift
  312. clashupdate "$@"
  313. ;;
  314. upgrade)
  315. shift
  316. clashupgrade "$@"
  317. ;;
  318. *)
  319. clashhelp "$@"
  320. ;;
  321. esac
  322. }
  323. clashhelp() {
  324. cat <<EOF
  325. Usage:
  326. clashctl COMMAND [OPTION]
  327. Commands:
  328. on 开启代理
  329. off 关闭代理
  330. proxy [on|off] 系统代理
  331. ui 面板地址
  332. status 内核状况
  333. tun [on|off] Tun 模式
  334. mixin [-e|-r] Mixin 配置
  335. secret [SECRET] Web 密钥
  336. update [auto|log] 更新订阅
  337. upgrade 升级内核
  338. EOF
  339. }