959 lines
35 KiB
Desktop File
Executable File
959 lines
35 KiB
Desktop File
Executable File
#!/system/bin/sh
|
|
# Validate settings.ini
|
|
if ! /system/bin/sh -n /data/adb/box/settings.ini 2>"/data/adb/box/run/settings_err.log"; then
|
|
echo "Err: settings.ini contains a syntax error" | tee -a "/data/adb/box/run/settings_err.log"
|
|
exit 1
|
|
fi
|
|
|
|
scripts_dir="${0%/*}"
|
|
source /data/adb/box/settings.ini
|
|
|
|
PROPFILE="/data/adb/modules/box_for_root/module.prop"
|
|
log_backup="false"
|
|
|
|
box_check_logs() {
|
|
ts=$(date '+%Y-%m-%d_%H-%M-%S')
|
|
log Info "Cleaning and backing up logs"
|
|
|
|
# Backup logs for each bin in bin_list
|
|
if [ "${log_backup}" = "true" ]; then
|
|
for bin in "${bin_name}"; do
|
|
log_file="${box_run}/${bin}.log"
|
|
if [ -f "${log_file}" ]; then
|
|
mv "${log_file}" "${box_run}/${bin}_$ts.log.bak"
|
|
log Info "Backup: ${bin}.log → ${bin}_${ts}.log.bak"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Remove specific unnecessary files
|
|
find "${box_run}" -maxdepth 1 -type f \( -name "root" -o -name "*.list" -o -name "*.inotify.log" \) -exec rm -f {} +
|
|
|
|
# Remove old logs (>1 days)
|
|
find "${box_run}" -maxdepth 1 -type f \( -name "*.log" -o -name "*.bak" \) -mtime +1 -exec rm -f {} +
|
|
}
|
|
|
|
box_bin_alive() {
|
|
local PID=$(<"${box_pid}" 2>/dev/null)
|
|
if ! kill -0 "$PID" >/dev/null; then
|
|
log Error "$(<"${box_run}/${bin_name}.log")"
|
|
log Error "${bin_name} service is not running."
|
|
log Error "please check ${bin_name}.log for more information."
|
|
log Error "killing stale pid $PID"
|
|
for bin in "${bin_list[@]}"; do
|
|
killall -15 "${bin}" >/dev/null || busybox pkill -15 "${bin}" >/dev/null 2>&1
|
|
done
|
|
"${scripts_dir}/box.iptables" disable >/dev/null 2>&1
|
|
[ -f "${box_pid}" ] && rm -f "${box_pid}"
|
|
exit 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
box_run_crontab() {
|
|
# Stop crond if it is running (optional)
|
|
pkill -f "busybox crond" >/dev/null 2>&1
|
|
# Prepare crontab
|
|
busybox crontab -c "${box_run}" -r
|
|
echo "# cron tasks for root" > "${box_run}/root"
|
|
|
|
if [ "${run_crontab}" = "true" ]; then
|
|
log Debug "crond enable"
|
|
if [ "$update_subscription" = "true" ] || [ "$update_geo" = "true" ]; then
|
|
if [ -n "${interva_update}" ]; then
|
|
log Debug "Crontab interval: ${interva_update}."
|
|
echo "${interva_update} ${scripts_dir}/box.tool geosub" >> "${box_run}/root"
|
|
log Info "${bin_name} geox updates: ${update_geo}."
|
|
if [ "${bin_name}" = "clash" ]; then
|
|
log Info "${bin_name} subscription update: ${update_subscription}."
|
|
fi
|
|
else
|
|
log Debug "Crontab interval is empty, skipping Geox & Subscription schedule setup."
|
|
fi
|
|
fi
|
|
cat "${box_dir}/crontab.cfg" >> "${box_run}/root"
|
|
chmod 0644 "${box_run}/root"
|
|
# start crond with the "-c" option and keep it in the background
|
|
nohup busybox crond -c "${box_run}" >/dev/null 2>&1 &
|
|
else
|
|
log Info "crond disable"
|
|
fi
|
|
}
|
|
|
|
xclash() {
|
|
local xclash_option="${xclash_option:-mihomo}"
|
|
# default to "mihomo" if xclash_option is not set
|
|
current_clash="$(readlink "${bin_dir}/clash")"
|
|
|
|
if [ "$current_clash" != "${bin_dir}/xclash/${xclash_option}" ]; then
|
|
if [ -f "${bin_dir}/xclash/${xclash_option}" ]; then
|
|
if ! ln -sf "${bin_dir}/xclash/${xclash_option}" "${bin_dir}/clash"; then
|
|
log Error "Failed to use ${xclash_option}"
|
|
return 1
|
|
fi
|
|
else
|
|
mkdir -p "${bin_dir}/xclash"
|
|
log Error "${bin_dir}/xclash/${xclash_option} file not found"
|
|
return 1
|
|
fi
|
|
fi
|
|
log Info "xclash [ $xclash_option ] setup completed"
|
|
return 0
|
|
}
|
|
|
|
box_ownership() {
|
|
# Set ownership and permission of kernel directory
|
|
chown -R ${box_user_group} ${box_dir}
|
|
chmod -R 644 ${box_dir}/${bin_name}
|
|
|
|
chown ${box_user_group} ${bin_path}
|
|
chmod 6755 ${bin_path}
|
|
|
|
chmod 0700 ${box_dir}/bin/yq
|
|
chmod 0700 ${box_dir}/bin/curl
|
|
}
|
|
|
|
box_permission() {
|
|
if [[ "${box_user_group}" == @(root:net_admin|0:3005) && -f "${bin_path}" ]]; then
|
|
# Set ownership and permission of kernel directory
|
|
box_ownership
|
|
log Info "Using kernel in ${bin_path}."
|
|
else
|
|
if [[ "${box_user_group}" != @(root:net_admin|0:3005) ]]; then
|
|
log Error "does't support user_group [ $box_user_group ]"
|
|
sed -i "s/box_user_group=.*/box_user_group=\"root:net_admin\"/g" ${settings}
|
|
log Debug "automatically changed to [ root:net_admin ], restart box"
|
|
exit 1
|
|
fi
|
|
log Error "Kernel [ ${bin_name} ] is missing."
|
|
log Error "Please download the [ ${bin_name} ] kernel and place it in the ${bin_dir}/ directory."
|
|
log Debug "exec 'su -c /data/adb/box/scripts/box.tool upkernel' in terminal"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
box_check_bin() {
|
|
if [ ! -x "${bin_path}" ]; then
|
|
log Error "${bin_path} is not executable."
|
|
exit 1
|
|
fi
|
|
|
|
local version_output
|
|
case "${bin_name}" in
|
|
clash)
|
|
version_output=$("${bin_path}" -v 2>/dev/null) || {
|
|
log Error "${bin_name} version information not available."
|
|
exit 1
|
|
}
|
|
;;
|
|
*)
|
|
version_output=$("${bin_path}" version 2>/dev/null) || {
|
|
log Error "${bin_name} version information not available."
|
|
exit 1
|
|
}
|
|
;;
|
|
esac
|
|
|
|
log Info "${version_output}"
|
|
}
|
|
|
|
# create_tun_link() {
|
|
# mkdir -p /dev/net
|
|
# [ ! -L /dev/net/tun ] && ln -s /dev/tun /dev/net/tun
|
|
|
|
# if [ ! -c "/dev/net/tun" ]; then
|
|
# log Error "Cannot create /dev/net/tun. Possible reasons:"
|
|
# log Warning "Your system does not support the TUN/TAP driver."
|
|
# log Warning "Your system kernel version is not compatible with the TUN/TAP driver."
|
|
# sed -i 's/network_mode=.*/network_mode="tproxy"/g' "${settings}"
|
|
# exit 1
|
|
# fi
|
|
# }
|
|
|
|
prepare_stealth_singbox() {
|
|
[ "${stealth_mode}" != "true" ] && return 0
|
|
|
|
yq_bin="${box_dir}/bin/yq"
|
|
command -v yq >/dev/null 2>&1 || yq_bin="yq"
|
|
|
|
if [ "${stealth_disable_api}" = "true" ]; then
|
|
${yq_bin} 'del(.experimental.clash_api)' -i --output-format=json "${sing_config}" 2>/dev/null
|
|
log Debug "[Stealth] sing-box REST API removed"
|
|
elif [ "${stealth_api_bind}" != "disable" ] && [ -n "${stealth_api_bind}" ]; then
|
|
${yq_bin} ".experimental.clash_api.external_controller = \"${stealth_api_bind}:${stealth_api_port}\"" -i --output-format=json "${sing_config}" 2>/dev/null
|
|
log Debug "[Stealth] sing-box API -> ${stealth_api_bind}:${stealth_api_port}"
|
|
fi
|
|
}
|
|
|
|
prepare_stealth_clash() {
|
|
[ "${stealth_mode}" != "true" ] && return 0
|
|
|
|
if [ "${stealth_disable_api}" = "true" ]; then
|
|
sed -i '/^external-controller:/d' "${clash_config}"
|
|
sed -i '/^external-ui:/d' "${clash_config}"
|
|
log Debug "[Stealth] Clash REST API removed"
|
|
elif [ "${stealth_api_bind}" != "disable" ] && [ -n "${stealth_api_bind}" ]; then
|
|
if busybox grep -q '^external-controller:' "${clash_config}"; then
|
|
sed -i "s|^external-controller:.*|external-controller: ${stealth_api_bind}:${stealth_api_port}|" "${clash_config}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
stealth_guard_network_mode() {
|
|
[ "${stealth_mode}" != "true" ] && return 0
|
|
[ "${stealth_forbid_tun}" != "true" ] && return 0
|
|
if [[ "${network_mode}" == @(mixed|tun) ]]; then
|
|
log Warning "[Stealth] TUN is detectable - switching network_mode to tproxy"
|
|
sed -i 's/\(network_mode=\)\"[^\"]*\"/\1"tproxy"/g' ${settings}
|
|
network_mode="tproxy"
|
|
fi
|
|
}
|
|
|
|
prepare_singbox() {
|
|
# Check configuration file
|
|
if ! [ -f "${sing_config}" ]; then
|
|
log Error "Configuration file ${sing_config} not found"
|
|
exit 1
|
|
else
|
|
log Info "Config ${sing_config} found"
|
|
fi
|
|
|
|
# Check yq
|
|
yq="yq"
|
|
if ! command -v yq &>/dev/null; then
|
|
if [ ! -e "${box_dir}/bin/yq" ]; then
|
|
log Debug "yq file not found, starting download from GitHub"
|
|
${scripts_dir}/box.tool upyq
|
|
fi
|
|
yq="${box_dir}/bin/yq"
|
|
fi
|
|
|
|
# Set auto_detect_interface/auto_route
|
|
${yq} '.route.auto_detect_interface = true' -i --output-format=json "${sing_config}"
|
|
${yq} '(.inbounds[] | select(.type == "tun") | .auto_route) |= true' -i --output-format=json "${sing_config}"
|
|
|
|
# Format sing-box configuration
|
|
if ${bin_path} format -w -D "${box_dir}/${bin_name}" -C "${box_dir}/${bin_name}" > "${box_run}/${bin_name}.log" 2>&1; then
|
|
# Set auto_route based on network_mode
|
|
if [[ "${network_mode}" == @(mixed|tun) ]]; then
|
|
# Check if "type" is "tun" in configuration
|
|
if ! busybox grep -q '"type": "tun"' "${sing_config}"; then
|
|
# Add "tun" configuration if missing
|
|
${yq} '.inbounds += [{
|
|
"type": "tun",
|
|
"tag": "tun-in",
|
|
"interface_name": "sing",
|
|
"address": ["172.18.0.1/30","fdfe:dcba:9876::1/126"],
|
|
"mtu": 9000,
|
|
"stack": "mixed",
|
|
"auto_route": true,
|
|
"strict_route": true,
|
|
"auto_redirect": true,
|
|
"include_android_user": [0,10,999],
|
|
"include_package": [],
|
|
"exclude_package": []
|
|
}]' -i --output-format=json "${sing_config}"
|
|
log Debug "[Tun] configuration added to ${sing_config}"
|
|
fi
|
|
else
|
|
# Set auto_route to false for non-"tun" network_mode
|
|
sed -i 's/auto_route": true/auto_route": false/g' "${box_dir}/sing-box/"*.json
|
|
# Set auto_detect_interface to false
|
|
sed -i 's/"auto_detect_interface": true/"auto_detect_interface": false/g' "${box_dir}/sing-box/"*.json
|
|
|
|
# Check if "type" is "tproxy" in configuration
|
|
if ! busybox grep -q '"type": "tproxy"' "${sing_config}"; then
|
|
# Add "tproxy" configuration if missing
|
|
${yq} '.inbounds += [{
|
|
"type": "tproxy",
|
|
"tag": "tproxy-in",
|
|
"listen": "::",
|
|
"listen_port": '"${tproxy_port}"'
|
|
}]' -i --output-format=json "${sing_config}"
|
|
log Debug "[Tproxy] configuration added to ${sing_config}"
|
|
fi
|
|
|
|
# del tun
|
|
${yq} 'del(.inbounds[] | select(.type == "tun"))' -i --output-format=json "${sing_config}"
|
|
|
|
# Sync tproxy port in sing-box configuration
|
|
for file in "${box_dir}/sing-box/"*.json; do
|
|
if busybox grep -q '"type": "tproxy"' "${file}"; then
|
|
${yq} '(.inbounds[] | select(.type == "tproxy") | .listen_port) = '"${tproxy_port}" -i --output-format=json "${file}"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# add exclude_package/include_package for tun
|
|
"${yq}" '(.inbounds[] | select(.type == "tun") | .include_package) = []' -i --output-format=json "${sing_config}"
|
|
"${yq}" '(.inbounds[] | select(.type == "tun") | .exclude_package) = []' -i --output-format=json "${sing_config}"
|
|
[[ ${proxy_mode} = "blacklist" || ${proxy_mode} = "black" ]] && mode="exclude" || mode="include"
|
|
|
|
# Clean up the "0:" prefix and insert one by one into the config
|
|
for raw in "${packages_list[@]}"; do
|
|
package=$(echo "$raw" | cut -d':' -f2)
|
|
"${yq}" eval '(.inbounds[] | select(.type == "tun") | .'${mode}'_package) += ["'${package}'"]' -i --output-format=json "${sing_config}"
|
|
done
|
|
|
|
# Add "redirect" configuration based on network_mode
|
|
if [[ "${network_mode}" == @(mixed|enhance|redirect) ]]; then
|
|
if ! busybox grep -q '"type": "redirect"' "${sing_config}"; then
|
|
# Add "redirect" configuration if missing
|
|
${yq} '.inbounds += [{
|
|
"type": "redirect",
|
|
"tag": "redirect-in",
|
|
"listen": "::",
|
|
"listen_port": '"${redir_port}"'
|
|
}]' -i --output-format=json "${sing_config}"
|
|
log Debug "[Redirect] configuration added to ${sing_config}"
|
|
fi
|
|
|
|
# Sync redir_port port in sing-box configuration
|
|
for file in "${box_dir}/sing-box/"*.json; do
|
|
if busybox grep -q '"type": "redirect"' "${file}"; then
|
|
${yq} '(.inbounds[] | select(.type == "redirect") | .listen_port) = '"${redir_port}" -i --output-format=json "${file}"
|
|
fi
|
|
done
|
|
fi
|
|
prepare_stealth_singbox
|
|
else
|
|
log Error "$(<"${box_run}/${bin_name}.log")"
|
|
log Error "Configuration failed. Please check the ${box_run}/${bin_name}.log file."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
prepare_clash() {
|
|
# check configuration file
|
|
if ! [ -f "${clash_config}" ]; then
|
|
log Error "configuration file ${clash_config} not found"
|
|
exit 1
|
|
else
|
|
log Info "config ${clash_config}"
|
|
fi
|
|
|
|
# ipv6=$(busybox awk '/ipv6:/ { print $2; found=1; exit } END{ if(!found) print "false" }' "${clash_config}" | head -n 1 2>/dev/null)
|
|
# sed -i "s/ipv6=.*/ipv6=\"${ipv6}\"/g" ${settings}
|
|
# sed -i "s/ipv6:.*/ipv6: ${ipv6}/g" "${clash_config}"
|
|
|
|
# write external_controller, if not in $clash_config
|
|
clash_external_controller=$(busybox awk '!/^ *#/ && /external-controller: /{print $1}' "${clash_config}")
|
|
if [ -z "${clash_external_controller}" ] && [ "${stealth_mode}" != "true" ]; then
|
|
printf "\nexternal-controller: 0.0.0.0:9090" >> "${clash_config}"
|
|
elif [ -z "${clash_external_controller}" ] && [ "${stealth_disable_api}" != "true" ]; then
|
|
printf "\nexternal-controller: %s:%s" "${stealth_api_bind:-127.0.0.1}" "${stealth_api_port:-19090}" >> "${clash_config}"
|
|
fi
|
|
|
|
# write external_ui, if not in $clash_config
|
|
clash_external_ui=$(busybox awk '!/^ *#/ && /external-ui: /{print $1}' "${clash_config}")
|
|
if [ -z "${clash_external_ui}" ]; then
|
|
printf "\nexternal-ui: ./dashboard" >> "${clash_config}"
|
|
fi
|
|
|
|
# write tproxy-port, if not in $clash_config
|
|
clash_tproxy_port=$(busybox awk '!/^ *#/ && /tproxy-port: /{print $1}' "${clash_config}")
|
|
if [ -z "${clash_tproxy_port}" ]; then
|
|
printf "\ntproxy-port: ${tproxy_port}" >> "${clash_config}"
|
|
fi
|
|
|
|
# write redir-port, if not in $clash_config
|
|
clash_redir_port=$(busybox awk '!/^ *#/ && /redir-port: /{print $1}' "${clash_config}")
|
|
if [ -z "${clash_redir_port}" ]; then
|
|
printf "\nredir-port: ${redir_port}" >> "${clash_config}"
|
|
fi
|
|
|
|
if [[ "${network_mode}" == @(mixed|tun) ]]; then
|
|
clash_tun_status=$(busybox awk '!/^ *#/ && /tun:/ { getline; split($0, arr, ": "); print arr[2]; found=1; exit } END{ if (!found) print "" }' "${clash_config}" 2>/dev/null)
|
|
# write TUN settings, if not in $clash_config
|
|
if [ -z "${clash_tun_status}" ]; then
|
|
printf '%s\n' '' 'tun:' \
|
|
' enable: true' \
|
|
' mtu: 9000' \
|
|
' device: meta' \
|
|
' stack: mixed # / gvisor / system' \
|
|
' dns-hijack:' \
|
|
' - any:53' \
|
|
' - tcp://any:53' \
|
|
' auto-route: true' \
|
|
' strict-route: true' \
|
|
' auto-redirect: true' \
|
|
' auto-detect-interface: true' \
|
|
' include-android-user: [0, 10, 999]' \
|
|
' exclude-package: []' \
|
|
' include-package: []' \ >> "${clash_config}"
|
|
log Debug "[tun] configuration has been added to ${clash_config}"
|
|
else
|
|
log Info "type [tun] already exists in ${clash_config}"
|
|
fi
|
|
|
|
# add exclude-package/include-package for tun
|
|
package=$(IFS=","; echo "${packages_list[*]}" | tr ' ' ',')
|
|
list_package="${package}"
|
|
|
|
if [[ "${proxy_mode}" = "whitelist" || "${proxy_mode}" = "white" ]]; then
|
|
mode="include"
|
|
elif [[ "${proxy_mode}" = "blacklist" || "${proxy_mode}" = "black" ]]; then
|
|
mode="exclude"
|
|
fi
|
|
|
|
# Check if include-package is inside a tun block:
|
|
include_package_found=$(busybox awk '/^tun:/{f=1} f && /'"$mode-package:"'/{print $0; exit}' "$clash_config")
|
|
if [ -z "$include_package_found" ]; then
|
|
sed -i "/^tun:/a \ $mode-package: []" "$clash_config"
|
|
fi
|
|
|
|
sed -i "s/exclude-package:.*/exclude-package: []/g" "${clash_config}"
|
|
sed -i "s/include-package:.*/include-package: []/g" "${clash_config}"
|
|
|
|
if [ -n "${list_package}" ]; then
|
|
list_package_clean=$(echo "$list_package" | sed 's/999://g' | sed 's/10://g' | sed 's/0://g' | busybox paste -sd, -)
|
|
|
|
# list_package_clean=$(echo "$list_package" | sed 's/999://g' | sed 's/10://g' | sed 's/0://g' | tr '\n' ',' | sed 's/,$//')
|
|
|
|
sed -i "s/${mode}-package:.*/${mode}-package: [\"${list_package_clean//,/\",\"}\"]/g" "${clash_config}"
|
|
fi
|
|
|
|
sed -i "/tun:/,/enable:/ { /enable: false/ s/enable: false/enable: true/ }" "${clash_config}"
|
|
else
|
|
sed -i "/tun:/,/enable:/ { /enable: true/ s/enable: true/enable: false/ }" "${clash_config}"
|
|
fi
|
|
|
|
# sync tproxy/redir port
|
|
sed -i -E "s/(tproxy-port: )[0-9]+/\1${tproxy_port}/" "${clash_config}"
|
|
sed -i -E "s/(redir-port: )[0-9]+/\1${redir_port}/" "${clash_config}"
|
|
|
|
clash_enhanced_mode=$(busybox awk '!/^ *#/ && /enhanced-mode: / { print $2 }' "${clash_config}" 2>/dev/null)
|
|
if [ -z "${clash_enhanced_mode}" ]; then
|
|
# Add enhanced-mode: fake-ip
|
|
sed -i "/^dns:/a \ enhanced-mode: fake-ip" "$clash_config"
|
|
log Debug "enhanced-mode: fake-ip add success"
|
|
fi
|
|
|
|
if [[ "${network_mode}" == @(mixed|tproxy|redirect|enhance) ]]; then
|
|
if [[ -n "${packages_list[*]}" || -n "${ignore_out_list[*]}" || -n "${gid_list[*]}" ]] && [ "${clash_enhanced_mode}" = "fake-ip" ]; then
|
|
log Warning "${proxy_mode} Only works in enhanced-mode: redir-host xclash[mihomo]"
|
|
log Warning "replace fake-ip to redir-host in clash config"
|
|
sed -i "s/enhanced-mode:.*/enhanced-mode: redir-host/g" "${clash_config}"
|
|
sed -i "/sniffer:/,/enable:/ { /enable: false/ s/enable: false/enable: true/ }" "${clash_config}"
|
|
fi
|
|
fi
|
|
prepare_stealth_clash
|
|
}
|
|
|
|
prop_description() {
|
|
sed -Ei "s/^description=(\[.*][[:space:]]*)?/description=[ $current_time | ⚠️ Your $bin_name configuration has an error, check the log for more details ] /g" "$PROPFILE"
|
|
}
|
|
|
|
box_run_bin() {
|
|
log Info "client-list: [ ${bin_list[*]} ]"
|
|
log Info "choose: ${bin_name}, start the service."
|
|
ulimit -SHn 1000000
|
|
# Use ulimit to limit the memory usage of a process to 200MB
|
|
# ulimit -v 200000 # Set the virtual memory limit in KB
|
|
case "${bin_name}" in
|
|
hysteria)
|
|
# sync port
|
|
if [ -f "${hysteria_config}" ]; then
|
|
sed -i -e "/tcpTProxy:/,/listen:/s/listen: :.*/listen: :${tproxy_port}/" "${hysteria_config}"
|
|
sed -i -e "/udpTProxy:/,/listen:/s/listen: :.*/listen: :${tproxy_port}/" "${hysteria_config}"
|
|
sed -i -e "/tcpRedirect:/,/listen:/s/listen: :.*/listen: :${redir_port}/" "${hysteria_config}"
|
|
fi
|
|
|
|
case "${network_mode}" in
|
|
redirect|tproxy|enhance)
|
|
# do nothing
|
|
;;
|
|
*)
|
|
sed -i 's/\(network_mode=\)\"[^\"]*\"/\1"tproxy"/g' ${settings}
|
|
;;
|
|
esac
|
|
|
|
nohup busybox setuidgid "${box_user_group}" "${bin_path}" -c "${hysteria_config}" > "${bin_log}" 2>&1 &
|
|
PID=$!
|
|
|
|
sleep 1
|
|
|
|
# Cek apakah proses benar-benar berjalan
|
|
if kill -0 "${PID}" 2>/dev/null; then
|
|
echo -n "${PID}" > "${box_pid}"
|
|
else
|
|
prop_description
|
|
log Error "$(cat "${box_run}/${bin_name}.log")"
|
|
log Error "Configuration failed. Please check the log file: ${box_run}/${bin_name}.log"
|
|
exit 1
|
|
fi
|
|
;;
|
|
sing-box)
|
|
prepare_singbox
|
|
# Auto-enable WG obfuscator when Manage Profiles picks config-wg-obfs.json
|
|
case "${name_sing_config}" in
|
|
*wg-obfs*|*wgobfs*)
|
|
wgobfs_enable="true"
|
|
;;
|
|
esac
|
|
if [ "${wgobfs_enable}" = "true" ]; then
|
|
"${scripts_dir}/box.wgobfs" start || exit 1
|
|
prepare_stealth_singbox
|
|
fi
|
|
if [ "${sidecar_enable}" = "true" ]; then
|
|
"${scripts_dir}/box.sidecar" start || exit 1
|
|
prepare_stealth_singbox
|
|
fi
|
|
if ${bin_path} check -D "${box_dir}/${bin_name}" -c "${sing_config}" > "${box_run}/${bin_name}.log" 2>&1; then
|
|
nohup busybox setuidgid "${box_user_group}" "${bin_path}" run -D "${box_dir}/${bin_name}" -c "${sing_config}" > "${bin_log}" 2>&1 &
|
|
PID=$!
|
|
echo -n $PID > "${box_pid}"
|
|
sleep 1
|
|
else
|
|
prop_description
|
|
log Error "$(<"${box_run}/${bin_name}.log")"
|
|
log Error "configuration failed. Please check the ${box_run}/${bin_name}.log file."
|
|
exit 1
|
|
fi
|
|
;;
|
|
clash)
|
|
prepare_clash
|
|
if ${bin_path} -t -d "${box_dir}/${bin_name}" -f "${clash_config}" > "${box_run}/${bin_name}.log" 2>&1; then
|
|
nohup busybox setuidgid "${box_user_group}" "${bin_path}" -d "${box_dir}/${bin_name}" -f "${clash_config}" > "${bin_log}" 2>&1 &
|
|
PID=$!
|
|
echo -n $PID > "${box_pid}"
|
|
sleep 1
|
|
else
|
|
prop_description
|
|
log Error "$(<"${box_run}/${bin_name}.log")"
|
|
log Error "configuration failed. Please check the ${box_run}/${bin_name}.log file."
|
|
exit 1
|
|
fi
|
|
;;
|
|
xray)
|
|
# set network_mode variable value to "tproxy"
|
|
if [[ "${network_mode}" != "tproxy" ]]; then
|
|
sed -i 's/\(network_mode=\)\"[^\"]*\"/\1"tproxy"/g' ${settings}
|
|
fi
|
|
|
|
# check configuration file
|
|
if [ ! -f "${xray_config}" ]; then
|
|
log Error "No configuration file found in ${xray_config}"
|
|
exit 1
|
|
else
|
|
# sync port
|
|
if [[ "${xray_config}" == *.yaml ]]; then
|
|
${box_dir}/bin/yq '(.inbounds[] | select(.protocol == "dokodemo-door") | .port) = '"${tproxy_port}"'' -i --output-format=yaml "${xray_config}" >/dev/null 2>&1
|
|
elif [[ "${xray_config}" == *.json ]]; then
|
|
${box_dir}/bin/yq '(.inbounds[] | select(.protocol == "dokodemo-door") | select(.tag == "tproxy-in") | .port) = '"${tproxy_port}" -i --output-format=json "${xray_config}" >/dev/null 2>&1
|
|
fi
|
|
log Info "Configuration file located in ${xray_config}"
|
|
fi
|
|
|
|
# run xray
|
|
export XRAY_LOCATION_ASSET="${box_dir}/${bin_name}"
|
|
if ${bin_path} -test -confdir "${box_dir}/${bin_name}" > "${box_run}/${bin_name}.log" 2>&1; then
|
|
nohup busybox setuidgid "${box_user_group}" "${bin_path}" run -confdir "${box_dir}/${bin_name}" > "${bin_log}" 2>&1 &
|
|
PID=$!
|
|
echo -n $PID > "${box_pid}"
|
|
sleep 1
|
|
else
|
|
prop_description
|
|
log Error "$(<"${box_run}/${bin_name}.log")"
|
|
log Error "configuration failed. Please check the ${box_run}/${bin_name}.log file."
|
|
exit 1
|
|
fi
|
|
;;
|
|
v2fly)
|
|
# set network_mode variable value to "tproxy"
|
|
if [[ "${network_mode}" != "tproxy" ]]; then
|
|
sed -i 's/\(network_mode=\)\"[^\"]*\"/\1"tproxy"/g' ${settings}
|
|
fi
|
|
|
|
# check configuration file
|
|
if [ ! -f "${v2fly_config}" ]; then
|
|
log Error "No configuration file found in ${v2fly_config}"
|
|
exit 1
|
|
else
|
|
# sync port
|
|
if [[ "${v2fly_config}" == *.yaml ]]; then
|
|
${box_dir}/bin/yq '(.inbounds[] | select(.protocol == "dokodemo-door") | .port) = '"${tproxy_port}"'' -i --output-format=yaml "${v2fly_config}" >/dev/null 2>&1
|
|
elif [[ "${v2fly_config}" == *.json ]]; then
|
|
${box_dir}/bin/yq '(.inbounds[] | select(.protocol == "dokodemo-door") | select(.tag == "tproxy-in") | .port) = '"${tproxy_port}" -i --output-format=json "${v2fly_config}" >/dev/null 2>&1
|
|
fi
|
|
log Info "Configuration file located in ${v2fly_config}"
|
|
fi
|
|
|
|
# run v2ray
|
|
export V2RAY_LOCATION_ASSET="${box_dir}/${bin_name}"
|
|
if ${bin_path} test -d "${box_dir}/${bin_name}" > "${box_run}/${bin_name}.log" 2>&1; then
|
|
nohup busybox setuidgid "${box_user_group}" "${bin_path}" run -d "${box_dir}/${bin_name}" > "${bin_log}" 2>&1 &
|
|
PID=$!
|
|
echo -n $PID > "${box_pid}"
|
|
sleep 1
|
|
else
|
|
prop_description
|
|
log Error "$(<"${box_run}/${bin_name}.log")"
|
|
log Error "configuration failed. Please check the ${box_run}/${bin_name}.log file."
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
log Error "[ ${bin_name} ] unknown binary."
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
box_cgroup() {
|
|
set_cgroup_config() {
|
|
local cgroup_attr="$1"
|
|
local cgroup_value="$2"
|
|
|
|
if [ "${cgroup_value}" = "true" ]; then
|
|
if ${scripts_dir}/box.tool "${cgroup_attr}"; then
|
|
true
|
|
else
|
|
log Warning "failed to enable ${cgroup_attr} for ${bin_name}."
|
|
log Warning "cgroups ${cgroup_attr} is turned off"
|
|
sed -i -E "/cgroup_${cgroup_attr}/ s/(true)/false/" "${settings}"
|
|
fi
|
|
fi
|
|
}
|
|
set_cgroup_config "memcg" "${cgroup_memcg}"
|
|
set_cgroup_config "cpuset" "${cgroup_cpuset}"
|
|
set_cgroup_config "blkio" "${cgroup_blkio}"
|
|
}
|
|
|
|
# Function to display the usage of a binary
|
|
# This script retrieves information about a running binary process and logs it to a log file.
|
|
box_bin_status() {
|
|
# Get the process ID of the binary
|
|
local PID=$(busybox pidof ${bin_name})
|
|
|
|
if [ -z "$PID" ]; then
|
|
log Error "${bin_name} is not running."
|
|
return 1
|
|
fi
|
|
|
|
stack=$(if [ "${bin_name}" != "clash" ]; then find "/data/adb/box/sing-box" -type f -name "*.json" -exec busybox awk -F'"' '/"stack"/{print $4}' {} +; else busybox awk '!/^ *#/ && /stack: / { print $2;found=1; exit}' "${clash_config}"; fi)
|
|
TOAST=1 log Info "${bin_name} service is running."
|
|
|
|
log Info "proxy: ${proxy_mode} + network: ${network_mode} + $(if [[ "${network_mode}" == @(mixed|tun) ]]; then echo "stack: ${stack}"; fi)"
|
|
|
|
# Get the memory usage of the binary
|
|
rss=$(grep VmRSS /proc/$PID/status | busybox awk '{ print $2 }')
|
|
[ "${rss}" -ge 1024 ] && bin_rss="$(expr ${rss} / 1024) MB" || bin_rss="${rss} KB"
|
|
swap=$(grep VmSwap /proc/$PID/status | busybox awk '{ print $2 }')
|
|
[ "${swap}" -ge 1024 ] && bin_swap="$(expr ${swap} / 1024) MB" || bin_swap="${swap} KB"
|
|
|
|
# Get the state of the binary
|
|
state=$(grep State /proc/$PID/status | busybox awk '{ print $2" "$3 }')
|
|
|
|
# Get the user and group of the binary
|
|
user_group=$(stat -c %U:%G /proc/$PID)
|
|
|
|
# Log the information
|
|
log Info "${bin_name} has started with the '${user_group}' user group."
|
|
log Info "${bin_name} status: ${state} (PID: $PID)"
|
|
log Info "${bin_name} memory usage: ${bin_rss}, swap: ${bin_swap}"
|
|
|
|
# Get the CPU usage of the binary
|
|
cpu=$(ps -p $PID -o %cpu | busybox awk 'NR==2{print $1}' 2>/dev/null)
|
|
|
|
cpus_allowed=$(grep Cpus_allowed_list /proc/$PID/status | busybox awk '{ print $2" "$3 }')
|
|
core=$(busybox awk '{print $39}' /proc/$PID/stat 2>/dev/null)
|
|
|
|
if [ -n "${cpu}" ]; then
|
|
log Info "${bin_name} CPU usage: ${cpu}%"
|
|
else
|
|
log Info "${bin_name} CPU usage: not available"
|
|
fi
|
|
if [ -n "${cpus_allowed}" ]; then
|
|
log Info "${bin_name} list of allowed CPUs : ${cpus_allowed}"
|
|
log Info "${bin_name} process $PID last ran on CPU core: ${core}"
|
|
else
|
|
log Info "${bin_name} Which CPU running on : not available"
|
|
fi
|
|
|
|
# Check battery temperature
|
|
temperature_celsius=$(($(cat /sys/class/power_supply/battery/temp) / 10))
|
|
log Info "battery temperature: ${temperature_celsius}°C"
|
|
|
|
# Get the running time of the binary
|
|
running_time=$(busybox ps -o comm,etime | grep ${bin_name} | busybox awk '{print $2}')
|
|
if [ -n "${running_time}" ]; then
|
|
log Info "${bin_name} running time: ${running_time}"
|
|
else
|
|
log Info "${bin_name} running time: not available."
|
|
fi
|
|
# local IP
|
|
localIP=($(ip -4 a | awk '/inet / && !/127.0.0.1/ { split($2, a, "/"); print a[1] }'))
|
|
log Info "local IP: ${localIP[*]}"
|
|
# local DNS
|
|
localDNS=($(dumpsys connectivity | awk -F'[ ,]' '/DnsAddresses:/ { for (i=1; i<=NF; i++) if ($i ~ /^\/.*$/) print substr($i, 2) }' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | sort -u))
|
|
log Info "local DNS: ${localDNS[*]}"
|
|
|
|
# Save the process ID to the pid file
|
|
if [ -n "$PID" ]; then
|
|
sed -Ei "s/^description=(\[.*][[:space:]]*)?/description=[ $current_time | ⭕ $bin_name service is running!!! ] /g" "$PROPFILE"
|
|
echo -n "$PID" > "${box_pid}"
|
|
fi
|
|
}
|
|
|
|
start_box() {
|
|
# Clear the log file and add the timestamp and delimiter
|
|
stealth_guard_network_mode
|
|
# cd /data/adb/box/bin; chmod 755 *
|
|
sed -Ei "s/^description=(\[.*][[:space:]]*)?/description=[ $current_time | ⚠️ Module is working! but no service is running ] /g" "$PROPFILE"
|
|
|
|
echo -n "" > "${box_log}"
|
|
box_version=$(busybox awk '!/^ *#/ && /version=/ { print $0 }' "/data/adb/modules/box_for_root/module.prop" 2>/dev/null)
|
|
|
|
if [ -t 1 ]; then
|
|
echo -e "${yellow}$(getprop persist.sys.timezone) $(date)${normal}"
|
|
echo -e "${yellow}$(getprop gsm.sim.operator.alpha) $(getprop gsm.network.type)${normal}"
|
|
echo -e "${yellow}${box_version}($(getprop ro.product.cpu.abi))${normal}"
|
|
echo -e "${white}━━━━━━━━━━━━━━━━━━${normal}"
|
|
else
|
|
{
|
|
echo "$(getprop persist.sys.timezone) $(date)"
|
|
echo "$(getprop gsm.sim.operator.alpha) $(getprop gsm.network.type)"
|
|
echo "${box_version}($(getprop ro.product.cpu.abi))"
|
|
echo "━━━━━━━━━━━━━━━━━━"
|
|
} | tee -a "${box_log}" >/dev/null 2>&1
|
|
fi
|
|
|
|
# Update iptables if bin_name is still running
|
|
# PIDS=("clash" "xray" "sing-box" "v2fly")
|
|
PIDS=("${bin_list[@]}")
|
|
PID=""
|
|
i=0
|
|
|
|
while [ -z "$PID" ] && [ "$i" -lt "${#PIDS[@]}" ]; do
|
|
PID=$(busybox pidof "${PIDS[$i]}")
|
|
i=$((i+1))
|
|
done
|
|
|
|
if [ -n "$PID" ]; then
|
|
pid_name="${box_dir}/run/pid_name.txt"
|
|
ps -p $PID -o comm= > "${pid_name}"
|
|
sed -i '/^[[:space:]]*$/d' "${pid_name}"
|
|
log Debug "$(<"${pid_name}")(PID: $PID) service is still running, auto restart BOX."
|
|
rm -f "${pid_name}"
|
|
stop_box
|
|
start_box && "${scripts_dir}/box.iptables" renew
|
|
exit 1
|
|
fi
|
|
|
|
# Checks if bin_name is defined
|
|
case "${bin_name}" in
|
|
clash|xray|sing-box|v2fly|hysteria)
|
|
log Info "Good day"
|
|
[ "${bin_name}" = "clash" ] && {
|
|
xclash || exit 1
|
|
}
|
|
;;
|
|
*)
|
|
log Error "bin_name: [ ${bin_name} ] unknown not defined."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# apk manager check
|
|
# versionName=$(dumpsys package xyz.chz.bfm | grep versionName | busybox awk -F '=' '{print $2}' | sed 's/-.*//')
|
|
# if [[ -n "${versionName}" && $(echo "${versionName}" | busybox awk '{print ($1 < 1.13)}') -eq 1 ]]; then
|
|
# log Error "Update BFR Manager Apps, Use version 1.13.+"
|
|
# log Error "current version: ${versionName}"
|
|
# exit 1
|
|
# else
|
|
# [ -n "${versionName}" ] && log Info "BFR Manager: ${versionName}"
|
|
# fi
|
|
|
|
# busybox check
|
|
busybox_code=$(busybox | busybox grep -oE '[0-9.]*' | head -n 1)
|
|
if [ "$(echo "${busybox_code}" | busybox awk -F. '{printf "%03d%03d%03d\n", $1, $2, $3}')" -lt "$(echo "1.36.1" | busybox awk -F. '{printf "%03d%03d%03d\n", $1, $2, $3}')" ]; then
|
|
log Info "Current $(which busybox) v${busybox_code}"
|
|
log Warning "Please update your busybox to v1.36.1+"
|
|
else
|
|
log Info "Current $(which busybox) v${busybox_code}"
|
|
fi
|
|
|
|
# Check permissions, check for bin existence, delete old logs, create a TUN if necessary, run box, and wait for 1 second
|
|
box_permission
|
|
box_check_bin
|
|
box_check_logs
|
|
|
|
# Execute the create_tun_link functions
|
|
# if [[ "${network_mode}" == @(mixed|tun) ]]; then
|
|
# create_tun_link
|
|
# fi
|
|
|
|
# Execute box_run_crontab if run_crontab is not equal to "false"
|
|
[ "${run_crontab}" = "true" ] && box_run_crontab || log Info "crontab disabled."
|
|
|
|
if [ -z "${proxy_mode}" ]; then
|
|
M1=$(busybox awk '!/^ *#/ && /mode:/{print $0}' "${pkg_config}")
|
|
[ -z $M1 ] && printf "\nmode:white" >> "${pkg_config}"
|
|
log Debug "prox mode is empty, add mode:white in ${pkg_config}"
|
|
fi
|
|
|
|
# Execute the box_cgroup, box_run_bin, box_detected_port, box_bin_alive,box_bin_status functions
|
|
box_run_bin
|
|
box_cgroup
|
|
|
|
count=0
|
|
while [ $count -le 10 ]; do
|
|
sleep 0.17
|
|
box_bin_alive || break
|
|
count=$((count + 1))
|
|
done
|
|
box_bin_status
|
|
|
|
# OOM Killer
|
|
# box_pid=$(cat ${box_pid})
|
|
# # Setting oom_adj
|
|
# echo -17 > /proc/$box_pid/oom_adj
|
|
# if [ $? -eq 0 ]; then
|
|
# log Info "set oom_adj for PID $box_pid to -17"
|
|
# else
|
|
# log Error "failed to set oom_adj for PID $box_pid"
|
|
# fi
|
|
# # Setting process priorities
|
|
# renice -n -20 -p $box_pid
|
|
# if [ $? -eq 0 ]; then
|
|
# log Info "set priority for PID $box_pid to -20"
|
|
# else
|
|
# log Error "failed to set priority for PID $box_pid"
|
|
# fi
|
|
|
|
true
|
|
}
|
|
|
|
stop_box() {
|
|
stop_cron
|
|
"${scripts_dir}/box.wgobfs" stop >/dev/null 2>&1
|
|
"${scripts_dir}/box.sidecar" stop >/dev/null 2>&1
|
|
"${scripts_dir}/box.stealth" disable >/dev/null 2>&1
|
|
# Kill each binary using a loop
|
|
for bin in "${bin_list[@]}"; do
|
|
# Check if the binary is running using pgrep
|
|
if busybox pgrep "${bin}" >/dev/null; then
|
|
# Use `busybox pkill` to kill the binary with signal 15, otherwise use `killall`.
|
|
if busybox pkill -15 "${bin}" >/dev/null; then
|
|
: # Do nothing if busybox pkill is successful
|
|
else
|
|
killall -15 "${bin}" >/dev/null || kill -15 "$(busybox pidof "${bin}")" >/dev/null 2>&1
|
|
fi
|
|
fi
|
|
done
|
|
# Check if the binary has stopped
|
|
sleep 0.5
|
|
if ! busybox pidof "${bin_name}" >/dev/null; then
|
|
# Delete the `box.pid` file if it exists
|
|
if [ -f "${box_pid}" ]; then
|
|
rm -f "${box_pid}"
|
|
fi
|
|
log Warning "${bin_name} shutting down, service is stopped."
|
|
TOAST=1 log Warning "${bin_name} disconnected."
|
|
|
|
[ -t 1 ] && echo -e "${white}━━━━━━━━━━━━━━━━━━${normal}"
|
|
else
|
|
log Warning "${bin_name} Not stopped; may still be shutting down or failed to shut down."
|
|
force_stop
|
|
fi
|
|
|
|
sed -Ei "s/^description=(\[.*][[:space:]]*)?/description=[ $current_time | ❌ $bin_name shutting down, service is stopped !!! ] /g" "$PROPFILE"
|
|
}
|
|
|
|
stop_cron() {
|
|
# Stop crond with pkill
|
|
if ! busybox pkill -f "busybox crond" >/dev/null; then
|
|
# If pkill fails, find and kill the process
|
|
cronkill=$(busybox pgrep -f "crond -c ${box_run}")
|
|
for cron in ${cronkill[@]}; do
|
|
kill -15 "${cron}" >/dev/null 2>&1
|
|
done
|
|
fi
|
|
}
|
|
|
|
force_stop() {
|
|
# try forcing it to shut down.
|
|
log Warning "try forcing it to shut down."
|
|
for bin in "${bin_list[@]}"; do
|
|
# Use `busybox pkill` to kill the binary with signal 9, otherwise use `killall`.
|
|
if busybox pkill -9 "${bin}"; then
|
|
: # Do nothing if busybox pkill is successful
|
|
else
|
|
if command -v killall >/dev/null; then
|
|
killall -9 "${bin}" >/dev/null || true
|
|
else
|
|
pkill -9 "${bin}" >/dev/null || true
|
|
fi
|
|
fi
|
|
done
|
|
sleep 0.5
|
|
if ! busybox pidof "${bin_name}" >/dev/null; then
|
|
log Warning "done, YOU can sleep peacefully."
|
|
rm -f "${box_pid}"
|
|
fi
|
|
}
|
|
|
|
# Check whether busybox is installed or not on the system using command -v
|
|
if ! command -v busybox &> /dev/null; then
|
|
log Error "$(which busybox) command not found."
|
|
exit 1
|
|
fi
|
|
|
|
if command -v ksud &>/dev/null; then
|
|
$scripts_dir/box.tool webroot >/dev/null 2>&1
|
|
fi
|
|
|
|
case "$1" in
|
|
start)
|
|
stop_box >/dev/null 2>&1
|
|
start_box
|
|
;;
|
|
stop)
|
|
stop_box
|
|
;;
|
|
restart)
|
|
"${scripts_dir}/box.iptables" disable && stop_box
|
|
sleep 0.5
|
|
start_box && "${scripts_dir}/box.iptables" renew
|
|
;;
|
|
status)
|
|
if busybox pidof "${bin_name}" >/dev/null; then
|
|
case "${bin_name}" in
|
|
clash) echo "${yellow}$("${bin_path}" -v)${normal}";;
|
|
*) echo "${yellow}$("${bin_path}" version)${normal}";;
|
|
esac
|
|
box_bin_status
|
|
else
|
|
log Warning "${bin_name} shutting down, service is stopped."
|
|
fi
|
|
;;
|
|
cron)
|
|
run_crontab="true"
|
|
stop_cron
|
|
sleep 0.5
|
|
box_run_crontab
|
|
;;
|
|
kcron)
|
|
stop_cron
|
|
;;
|
|
help|-h|--help|"")
|
|
echo "${yellow}Usage${normal}: ${green}$0${normal} {${yellow}start|stop|restart|status|cron|kcron${normal}}"
|
|
echo
|
|
echo "Commands:"
|
|
echo " ${yellow}start${normal} - Stop (if running) then start the service"
|
|
echo " ${yellow}stop${normal} - Stop the service"
|
|
echo " ${yellow}restart${normal} - Restart the service and refresh iptables rules"
|
|
echo " ${yellow}status${normal} - Show service status and version"
|
|
echo " ${yellow}cron${normal} - Enable and start scheduled tasks"
|
|
echo " ${yellow}kcron${normal} - Kill/disable scheduled tasks"
|
|
echo
|
|
echo "Example:"
|
|
echo " $0 start"
|
|
;;
|
|
*)
|
|
echo "${red}$0 $1 not found${normal}"
|
|
echo "Run '${green}$0 help${normal}' for usage."
|
|
;;
|
|
esac |