Files
clash-for-linux-install/script/common.sh
T

364 lines
11 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# shellcheck disable=SC2148
# shellcheck disable=SC2034
# shellcheck disable=SC2155
[ -n "$BASH_VERSION" ] && set +o noglob
[ -n "$ZSH_VERSION" ] && setopt glob no_nomatch
URL_GH_PROXY='https://gh-proxy.com/'
URL_CLASH_UI="http://board.zash.run.place"
SCRIPT_BASE_DIR='./script'
SCRIPT_FISH="${SCRIPT_BASE_DIR}/clashctl.fish"
RESOURCES_BASE_DIR='./resources'
RESOURCES_BIN_DIR="${RESOURCES_BASE_DIR}/bin"
RESOURCES_CONFIG="${RESOURCES_BASE_DIR}/config.yaml"
RESOURCES_CONFIG_MIXIN="${RESOURCES_BASE_DIR}/mixin.yaml"
ZIP_BASE_DIR="${RESOURCES_BASE_DIR}/zip"
ZIP_CLASH=$(echo ${ZIP_BASE_DIR}/clash*)
ZIP_MIHOMO=$(echo ${ZIP_BASE_DIR}/mihomo*)
ZIP_YQ=$(echo ${ZIP_BASE_DIR}/yq*)
ZIP_SUBCONVERTER=$(echo ${ZIP_BASE_DIR}/subconverter*)
ZIP_UI="${ZIP_BASE_DIR}/yacd.tar.xz"
CLASH_BASE_DIR='/opt/clash'
CLASH_SCRIPT_DIR="${CLASH_BASE_DIR}/$(basename $SCRIPT_BASE_DIR)"
CLASH_CONFIG_URL="${CLASH_BASE_DIR}/url"
CLASH_CONFIG_RAW="${CLASH_BASE_DIR}/$(basename $RESOURCES_CONFIG)"
CLASH_CONFIG_RAW_BAK="${CLASH_CONFIG_RAW}.bak"
CLASH_CONFIG_MIXIN="${CLASH_BASE_DIR}/$(basename $RESOURCES_CONFIG_MIXIN)"
CLASH_CONFIG_RUNTIME="${CLASH_BASE_DIR}/runtime.yaml"
CLASH_UPDATE_LOG="${CLASH_BASE_DIR}/clashupdate.log"
_set_var() {
local user=$USER
local home=$HOME
[ -n "$SUDO_USER" ] && {
user=$SUDO_USER
home=$(awk -F: -v user="$SUDO_USER" '$1==user{print $6}' /etc/passwd)
}
[ -n "$BASH_VERSION" ] && {
_SHELL=bash
}
[ -n "$ZSH_VERSION" ] && {
_SHELL=zsh
}
[ -n "$fish_version" ] && {
_SHELL=fish
}
# rc文件路径
command -v bash >&/dev/null && {
SHELL_RC_BASH="${home}/.bashrc"
}
command -v zsh >&/dev/null && {
SHELL_RC_ZSH="${home}/.zshrc"
}
command -v fish >&/dev/null && {
SHELL_RC_FISH="${home}/.config/fish/conf.d/clashctl.fish"
}
# 定时任务路径
local os_info=$(cat /etc/os-release)
echo "$os_info" | grep -iqsE "rhel|centos" && CLASH_CRON_TAB="/var/spool/cron/$user"
echo "$os_info" | grep -iqsE "debian|ubuntu" && CLASH_CRON_TAB="/var/spool/cron/crontabs/$user"
}
_set_var
# shellcheck disable=SC2120
_set_bin() {
local bin_base_dir="${CLASH_BASE_DIR}/bin"
[ -n "$1" ] && bin_base_dir=$1
BIN_CLASH="${bin_base_dir}/clash"
BIN_MIHOMO="${bin_base_dir}/mihomo"
BIN_YQ="${bin_base_dir}/yq"
BIN_SUBCONVERTER_DIR="${bin_base_dir}/subconverter"
BIN_SUBCONVERTER_CONFIG="$BIN_SUBCONVERTER_DIR/pref.yml"
BIN_SUBCONVERTER_PORT="25500"
BIN_SUBCONVERTER="${BIN_SUBCONVERTER_DIR}/subconverter"
BIN_SUBCONVERTER_LOG="${BIN_SUBCONVERTER_DIR}/latest.log"
[ -f "$BIN_CLASH" ] && {
BIN_KERNEL=$BIN_CLASH
}
[ -f "$BIN_MIHOMO" ] && {
BIN_KERNEL=$BIN_MIHOMO
}
BIN_KERNEL_NAME=$(basename "$BIN_KERNEL")
}
_set_bin
_set_rc() {
[ "$1" = "unset" ] && {
sed -i "\|$CLASH_SCRIPT_DIR|d" "$SHELL_RC_BASH" "$SHELL_RC_ZSH" 2>/dev/null
rm -f "$SHELL_RC_FISH" 2>/dev/null
return
}
echo "source $CLASH_SCRIPT_DIR/common.sh && source $CLASH_SCRIPT_DIR/clashctl.sh && watch_proxy" |
tee -a "$SHELL_RC_BASH" "$SHELL_RC_ZSH" >&/dev/null
[ -n "$SHELL_RC_FISH" ] && /usr/bin/install $SCRIPT_FISH "$SHELL_RC_FISH"
}
# 默认集成、安装mihomo内核
# 移除/删除mihomo:下载安装clash内核
function _get_kernel() {
[ -f "$ZIP_CLASH" ] && {
ZIP_KERNEL=$ZIP_CLASH
BIN_KERNEL=$BIN_CLASH
}
[ -f "$ZIP_MIHOMO" ] && {
ZIP_KERNEL=$ZIP_MIHOMO
BIN_KERNEL=$BIN_MIHOMO
}
[ ! -f "$ZIP_MIHOMO" ] && [ ! -f "$ZIP_CLASH" ] && {
local arch=$(uname -m)
_failcat "${ZIP_BASE_DIR}:未检测到可用的内核压缩包"
_download_clash "$arch"
ZIP_KERNEL=$ZIP_CLASH
BIN_KERNEL=$BIN_CLASH
}
BIN_KERNEL_NAME=$(basename "$BIN_KERNEL")
_okcat "安装内核:$BIN_KERNEL_NAME"
}
_get_random_port() {
local randomPort=$(shuf -i 1024-65535 -n 1)
! _is_bind "$randomPort" && { echo "$randomPort" && return; }
_get_random_port
}
function _get_proxy_port() {
MIXED_PORT=$(sudo "$BIN_YQ" '.mixed-port' $CLASH_CONFIG_RUNTIME)
_is_already_in_use "$MIXED_PORT" "$BIN_KERNEL_NAME" && {
local newPort=$(_get_random_port)
local msg="端口占用:${MIXED_PORT} 🎲 随机分配:$newPort"
sudo "$BIN_YQ" -i ".mixed-port = $newPort" $CLASH_CONFIG_RUNTIME
MIXED_PORT=$newPort
_failcat '🎯' "$msg"
}
}
function _get_ui_port() {
local ext_addr=$(sudo "$BIN_YQ" '.external-controller // ""' $CLASH_CONFIG_RUNTIME)
local ext_port=${ext_addr##*:}
UI_PORT=${ext_port:-9090}
_is_already_in_use "$UI_PORT" "$BIN_KERNEL_NAME" && {
local newPort=$(_get_random_port)
local msg="端口占用:${UI_PORT} 🎲 随机分配:$newPort"
sudo "$BIN_YQ" -i ".external-controller = \"0.0.0.0:$newPort\"" $CLASH_CONFIG_RUNTIME
UI_PORT=$newPort
_failcat '🎯' "$msg"
}
}
_get_color() {
local hex="${1#\#}"
local r=$((16#${hex:0:2}))
local g=$((16#${hex:2:2}))
local b=$((16#${hex:4:2}))
printf "\e[38;2;%d;%d;%dm" "$r" "$g" "$b"
}
_get_color_msg() {
local color=$(_get_color "$1")
local msg=$2
local reset="\033[0m"
printf "%b%s%b\n" "$color" "$msg" "$reset"
}
_get_random_val() {
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 6
}
function _okcat() {
local color=#c8d6e5
local emoji=😼
[ $# -gt 1 ] && emoji=$1 && shift
local msg="${emoji} $1"
_get_color_msg "$color" "$msg" && return 0
}
function _failcat() {
local color=#fd79a8
local emoji=😾
[ $# -gt 1 ] && emoji=$1 && shift
local msg="${emoji} $1"
_get_color_msg "$color" "$msg" >&2 && return 1
}
function _quit() {
local user=root
[ -n "$SUDO_USER" ] && user=$SUDO_USER
exec sudo -u "$user" -- "$_SHELL" -i
}
function _error_quit() {
[ $# -gt 0 ] && {
local color=#f92f60
local emoji=📢
[ $# -gt 1 ] && emoji=$1 && shift
local msg="${emoji} $1"
_get_color_msg "$color" "$msg"
}
exec $_SHELL -i
}
_is_bind() {
local port=$1
{ sudo ss -lnptu || sudo netstat -lnptu; } | grep ":${port}\b"
}
_is_already_in_use() {
local port=$1
local progress=$2
_is_bind "$port" | grep -qs -v "$progress"
}
function _is_root() {
[ "$(whoami)" = "root" ]
}
function _valid_env() {
_is_root || _error_quit "需要 root 或 sudo 权限执行"
[ -n "$ZSH_VERSION" ] && [ -n "$BASH_VERSION" ] && _error_quit "仅支持:bash、zsh"
[ "$(ps -p 1 -o comm=)" != "systemd" ] && _error_quit "系统不具备 systemd"
}
function _valid_config() {
[ -e "$1" ] && [ "$(wc -l <"$1")" -gt 1 ] && {
local cmd msg
cmd="$BIN_KERNEL -d $(dirname "$1") -f $1 -t"
msg=$(eval "$cmd") || {
eval "$cmd"
echo "$msg" | grep -qs "unsupport proxy type" && {
local prefix="检测到订阅中包含不受支持的代理协议"
[ "$BIN_KERNEL_NAME" = "clash" ] && _error_quit "${prefix}, 推荐安装使用 mihomo 内核"
_error_quit "${prefix}, 请检查并升级内核版本"
}
}
}
}
_download_clash() {
local arch=$1
local url sha256sum
case "$arch" in
x86_64)
url=https://downloads.clash.wiki/ClashPremium/clash-linux-amd64-2023.08.17.gz
sha256sum='92380f053f083e3794c1681583be013a57b160292d1d9e1056e7fa1c2d948747'
;;
*86*)
url=https://downloads.clash.wiki/ClashPremium/clash-linux-386-2023.08.17.gz
sha256sum='254125efa731ade3c1bf7cfd83ae09a824e1361592ccd7c0cccd2a266dcb92b5'
;;
armv*)
url=https://downloads.clash.wiki/ClashPremium/clash-linux-armv5-2023.08.17.gz
sha256sum='622f5e774847782b6d54066f0716114a088f143f9bdd37edf3394ae8253062e8'
;;
aarch64)
url=https://downloads.clash.wiki/ClashPremium/clash-linux-arm64-2023.08.17.gz
sha256sum='c45b39bb241e270ae5f4498e2af75cecc0f03c9db3c0db5e55c8c4919f01afdd'
;;
*)
_error_quit "未知的架构版本:$arch,请自行下载对应版本至 ${ZIP_BASE_DIR} 目录下:https://downloads.clash.wiki/ClashPremium/"
;;
esac
_okcat '⏳' "正在下载:clash:${arch} 架构..."
ZIP_CLASH="${ZIP_BASE_DIR}/$(basename $url)"
curl \
--progress-bar \
--show-error \
--fail \
--insecure \
--connect-timeout 15 \
--retry 1 \
--output "$ZIP_CLASH" \
"$url"
echo $sha256sum "$ZIP_CLASH" | sha256sum -c ||
_error_quit "下载失败:请自行下载对应版本至 ${ZIP_BASE_DIR} 目录下:https://downloads.clash.wiki/ClashPremium/"
}
_download_raw_config() {
local dest=$1
local url=$2
local agent='clash-verge/v2.0.4'
sudo curl \
--silent \
--show-error \
--insecure \
--connect-timeout 4 \
--retry 1 \
--user-agent "$agent" \
--output "$dest" \
"$url" ||
sudo wget \
--no-verbose \
--no-check-certificate \
--timeout 3 \
--tries 1 \
--user-agent "$agent" \
--output-document "$dest" \
"$url"
}
_download_convert_config() {
local dest=$1
local url=$2
_start_convert
local convert_url=$(
target='clash'
base_url="http://127.0.0.1:${BIN_SUBCONVERTER_PORT}/sub"
curl \
--get \
--silent \
--output /dev/null \
--data-urlencode "target=$target" \
--data-urlencode "url=$url" \
--write-out '%{url_effective}' \
"$base_url"
)
_download_raw_config "$dest" "$convert_url"
_stop_convert
}
function _download_config() {
local dest=$1
local url=$2
[ "${url:0:4}" = 'file' ] && return 0
_download_raw_config "$dest" "$url" || return 1
_okcat '🍃' '下载成功:内核验证配置...'
_valid_config "$dest" || {
_failcat '🍂' "验证失败:尝试订阅转换..."
_download_convert_config "$dest" "$url" || _failcat '🍂' "转换失败:请检查日志:$BIN_SUBCONVERTER_LOG"
}
}
_start_convert() {
_is_already_in_use $BIN_SUBCONVERTER_PORT 'subconverter' && {
local newPort=$(_get_random_port)
_failcat '🎯' "端口占用:$BIN_SUBCONVERTER_PORT 🎲 随机分配:$newPort"
[ ! -e "$BIN_SUBCONVERTER_CONFIG" ] && {
sudo /bin/cp -f "$BIN_SUBCONVERTER_DIR/pref.example.yml" "$BIN_SUBCONVERTER_CONFIG"
}
sudo "$BIN_YQ" -i ".server.port = $newPort" "$BIN_SUBCONVERTER_CONFIG"
BIN_SUBCONVERTER_PORT=$newPort
}
local start=$(date +%s)
# 子shell运行,屏蔽kill时的输出
(sudo "$BIN_SUBCONVERTER" 2>&1 | sudo tee "$BIN_SUBCONVERTER_LOG" >/dev/null &)
while ! _is_bind "$BIN_SUBCONVERTER_PORT" >&/dev/null; do
sleep 1s
local now=$(date +%s)
[ $((now - start)) -gt 1 ] && _error_quit "订阅转换服务未启动,请检查日志:$BIN_SUBCONVERTER_LOG"
done
}
_stop_convert() {
pkill -9 -f "$BIN_SUBCONVERTER" >&/dev/null
}