539 lines
18 KiB
Shell
539 lines
18 KiB
Shell
#!/system/bin/sh
|
|
# Shared RKNnoVPN module installer flow for Magisk/KSU/APatch installs.
|
|
# Keep customize.sh thin; release/update code validates this file as required.
|
|
|
|
RKNNOVPN_DIR="${RKNNOVPN_DIR:-/data/adb/modules/rknnovpn}"
|
|
RKNNOVPN_GID="${RKNNOVPN_GID:-23333}"
|
|
SUBDIRS="${SUBDIRS:-bin config config/rendered scripts run logs releases}"
|
|
|
|
abort_install() {
|
|
ui_print " [!] FATAL: $1"
|
|
abort "$1"
|
|
}
|
|
|
|
module_version() {
|
|
if command -v rknnovpn_module_version >/dev/null 2>&1; then
|
|
rknnovpn_module_version "$MODPATH"
|
|
return $?
|
|
fi
|
|
if [ -f "${MODPATH}/module.prop" ]; then
|
|
awk -F= '$1=="version"{print $2; exit}' "${MODPATH}/module.prop" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
ui_print_header() {
|
|
HEADER_VERSION="$(module_version 2>/dev/null)"
|
|
[ -n "$HEADER_VERSION" ] || HEADER_VERSION="unknown"
|
|
ui_print ""
|
|
ui_print " ==========================================="
|
|
ui_print " RKNnoVPN ${HEADER_VERSION} — Transparent Proxy"
|
|
ui_print " tproxy + iptables + sing-box"
|
|
ui_print " ==========================================="
|
|
ui_print ""
|
|
}
|
|
|
|
detect_root_manager() {
|
|
if [ -n "$KSU" ] && [ "$KSU" = "true" ]; then
|
|
ROOT_MGR="kernelsu"
|
|
BUSYBOX="/data/adb/ksu/bin/busybox"
|
|
ui_print " [*] Root manager: KernelSU"
|
|
return
|
|
fi
|
|
|
|
if [ -n "$APATCH" ] && [ "$APATCH" = "true" ]; then
|
|
ROOT_MGR="apatch"
|
|
BUSYBOX="/data/adb/ap/bin/busybox"
|
|
ui_print " [*] Root manager: APatch"
|
|
return
|
|
fi
|
|
|
|
if [ -n "$MAGISK_VER" ] || [ -d "/data/adb/magisk" ]; then
|
|
ROOT_MGR="magisk"
|
|
BUSYBOX="/data/adb/magisk/busybox"
|
|
ui_print " [*] Root manager: Magisk $MAGISK_VER"
|
|
return
|
|
fi
|
|
|
|
ROOT_MGR="unknown"
|
|
BUSYBOX="busybox"
|
|
ui_print " [!] Root manager: unknown (assuming Magisk-compatible)"
|
|
}
|
|
|
|
check_architecture() {
|
|
ARCH="$(getprop ro.product.cpu.abi)"
|
|
case "$ARCH" in
|
|
arm64-v8a|arm64*)
|
|
ARCH_DIR="arm64"
|
|
ui_print " [*] Architecture: $ARCH -> ${ARCH_DIR} (OK)"
|
|
;;
|
|
armeabi-v7a|armeabi|armv7*|arm*)
|
|
ARCH_DIR="armv7"
|
|
ui_print " [*] Architecture: $ARCH -> ${ARCH_DIR} (OK)"
|
|
;;
|
|
*)
|
|
abort_install "Unsupported architecture: $ARCH. Supported module binaries: arm64 and armv7."
|
|
;;
|
|
esac
|
|
}
|
|
|
|
find_binaries_dir() {
|
|
[ -n "${ARCH_DIR:-}" ] || return 1
|
|
SRC_BIN="${MODPATH}/binaries/${ARCH_DIR}"
|
|
[ -d "$SRC_BIN" ] || return 1
|
|
echo "$SRC_BIN"
|
|
}
|
|
|
|
check_api_level() {
|
|
API="$(getprop ro.build.version.sdk)"
|
|
if [ -z "$API" ]; then
|
|
abort_install "Cannot determine Android API level."
|
|
fi
|
|
if [ "$API" -lt 28 ]; then
|
|
abort_install "Android API $API < 28. Minimum Android 9 (Pie) required."
|
|
fi
|
|
ui_print " [*] API level: $API (>= 28, OK)"
|
|
}
|
|
|
|
check_tproxy_support() {
|
|
ui_print " [*] Checking TPROXY kernel support..."
|
|
|
|
TPROXY_OK=0
|
|
|
|
if [ -f "/proc/config.gz" ]; then
|
|
if command -v zcat >/dev/null 2>&1; then
|
|
if zcat /proc/config.gz 2>/dev/null | grep -q "CONFIG_NETFILTER_XT_TARGET_TPROXY="; then
|
|
TPROXY_RESULT="$(zcat /proc/config.gz 2>/dev/null | grep 'CONFIG_NETFILTER_XT_TARGET_TPROXY=')"
|
|
case "$TPROXY_RESULT" in
|
|
*=y|*=m)
|
|
TPROXY_OK=1
|
|
ui_print " [*] TPROXY: $TPROXY_RESULT (OK)"
|
|
;;
|
|
*)
|
|
ui_print " [!] TPROXY: $TPROXY_RESULT (disabled)"
|
|
;;
|
|
esac
|
|
else
|
|
ui_print " [!] TPROXY config not found in /proc/config.gz"
|
|
fi
|
|
elif command -v gzip >/dev/null 2>&1; then
|
|
if gzip -dc /proc/config.gz 2>/dev/null | grep -q "CONFIG_NETFILTER_XT_TARGET_TPROXY="; then
|
|
TPROXY_RESULT="$(gzip -dc /proc/config.gz 2>/dev/null | grep 'CONFIG_NETFILTER_XT_TARGET_TPROXY=')"
|
|
case "$TPROXY_RESULT" in
|
|
*=y|*=m)
|
|
TPROXY_OK=1
|
|
ui_print " [*] TPROXY: $TPROXY_RESULT (OK)"
|
|
;;
|
|
*)
|
|
ui_print " [!] TPROXY: $TPROXY_RESULT (disabled)"
|
|
;;
|
|
esac
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ "$TPROXY_OK" -eq 0 ]; then
|
|
if [ -f "/proc/net/ip_tables_targets" ]; then
|
|
if grep -q "TPROXY" /proc/net/ip_tables_targets 2>/dev/null; then
|
|
TPROXY_OK=1
|
|
ui_print " [*] TPROXY: found in ip_tables_targets (OK)"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ "$TPROXY_OK" -eq 0 ]; then
|
|
modprobe xt_TPROXY 2>/dev/null
|
|
if [ -f "/proc/net/ip_tables_targets" ] && grep -q "TPROXY" /proc/net/ip_tables_targets 2>/dev/null; then
|
|
TPROXY_OK=1
|
|
ui_print " [*] TPROXY: loaded via modprobe (OK)"
|
|
fi
|
|
fi
|
|
|
|
if [ "$TPROXY_OK" -eq 0 ]; then
|
|
ui_print ""
|
|
ui_print " [!] WARNING: TPROXY support not confirmed."
|
|
ui_print " [!] The module will be installed, but may not"
|
|
ui_print " [!] work if your kernel lacks TPROXY."
|
|
ui_print ""
|
|
fi
|
|
}
|
|
|
|
create_directory_structure() {
|
|
ui_print " [*] Creating directory structure..."
|
|
if command -v rknnovpn_ensure_layout >/dev/null 2>&1; then
|
|
rknnovpn_ensure_layout || abort_install "Failed to create ${RKNNOVPN_DIR} layout"
|
|
else
|
|
for subdir in $SUBDIRS; do
|
|
mkdir -p "${RKNNOVPN_DIR}/${subdir}" 2>/dev/null
|
|
if [ ! -d "${RKNNOVPN_DIR}/${subdir}" ]; then
|
|
abort_install "Failed to create ${RKNNOVPN_DIR}/${subdir}"
|
|
fi
|
|
done
|
|
fi
|
|
ui_print " [*] Directories created under ${RKNNOVPN_DIR}/"
|
|
}
|
|
|
|
prepare_install_logs() {
|
|
INSTALL_LOG="${RKNNOVPN_DIR}/logs/install.log"
|
|
mkdir -p "${RKNNOVPN_DIR}/logs" 2>/dev/null || return 0
|
|
touch "$INSTALL_LOG" \
|
|
"${RKNNOVPN_DIR}/logs/service.log" \
|
|
"${RKNNOVPN_DIR}/logs/sing-box.log" 2>/dev/null || true
|
|
chown 0:0 "${RKNNOVPN_DIR}/logs" "$INSTALL_LOG" \
|
|
"${RKNNOVPN_DIR}/logs/service.log" \
|
|
"${RKNNOVPN_DIR}/logs/sing-box.log" 2>/dev/null || true
|
|
chmod 0700 "${RKNNOVPN_DIR}/logs" 2>/dev/null || true
|
|
chmod 0600 "$INSTALL_LOG" \
|
|
"${RKNNOVPN_DIR}/logs/service.log" \
|
|
"${RKNNOVPN_DIR}/logs/sing-box.log" 2>/dev/null || true
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo '----') install started: $(module_version 2>/dev/null)" >> "$INSTALL_LOG" 2>/dev/null || true
|
|
ui_print " [*] Install logs prepared at ${RKNNOVPN_DIR}/logs/"
|
|
}
|
|
|
|
prepare_fresh_runtime_state() {
|
|
ui_print " [*] Fresh local-first install; app profile stays APK-owned"
|
|
mkdir -p "${RKNNOVPN_DIR}/config" "${RKNNOVPN_DIR}/config/rendered" 2>/dev/null || abort_install "Failed to create runtime config directories"
|
|
chown 0:0 "${RKNNOVPN_DIR}/config" "${RKNNOVPN_DIR}/config/rendered" 2>/dev/null || true
|
|
chmod 0700 "${RKNNOVPN_DIR}/config" "${RKNNOVPN_DIR}/config/rendered" 2>/dev/null || true
|
|
}
|
|
|
|
install_default_config() {
|
|
CONFIG_FILE="${RKNNOVPN_DIR}/config/config.json"
|
|
|
|
if [ -f "${MODPATH}/defaults/config.json" ]; then
|
|
cp -f "${MODPATH}/defaults/config.json" "$CONFIG_FILE"
|
|
ui_print " [*] Default config.json installed"
|
|
else
|
|
abort_install "defaults/config.json missing from module archive"
|
|
fi
|
|
|
|
if [ -f "${MODPATH}/defaults/config.json" ]; then
|
|
cp -f "${MODPATH}/defaults/config.json" "${RKNNOVPN_DIR}/config/config.defaults.json"
|
|
fi
|
|
|
|
# New installs let the APK create the user profile in private storage.
|
|
}
|
|
|
|
mark_manual_start_required() {
|
|
if command -v rknnovpn_set_manual_mode >/dev/null 2>&1; then
|
|
rknnovpn_set_manual_mode
|
|
else
|
|
mkdir -p "${RKNNOVPN_DIR}/config" "${RKNNOVPN_DIR}/run" 2>/dev/null
|
|
touch "${RKNNOVPN_DIR}/config/manual" 2>/dev/null
|
|
rm -f "${RKNNOVPN_DIR}/run/active" 2>/dev/null
|
|
fi
|
|
ui_print " [*] Boot autostart disabled until the app starts the proxy"
|
|
}
|
|
|
|
install_binaries() {
|
|
SRC_BIN="$(find_binaries_dir 2>/dev/null)" ||
|
|
abort_install "No canonical binaries directory for ${ARCH:-unknown}; expected ${MODPATH}/binaries/${ARCH_DIR:-unknown}"
|
|
|
|
ui_print " [*] Installing ${ARCH_DIR:-arm64} binaries from ${SRC_BIN#$MODPATH/}..."
|
|
for bin_file in "$SRC_BIN"/*; do
|
|
[ ! -f "$bin_file" ] && continue
|
|
bin_name="$(basename "$bin_file")"
|
|
cp -f "$bin_file" "${RKNNOVPN_DIR}/bin/${bin_name}"
|
|
ui_print " [*] -> ${bin_name}"
|
|
done
|
|
}
|
|
|
|
install_scripts() {
|
|
SRC_SCRIPTS="${MODPATH}/scripts"
|
|
if [ ! -d "$SRC_SCRIPTS" ]; then
|
|
ui_print " [!] No scripts directory at ${SRC_SCRIPTS}"
|
|
ui_print " [!] Skipping scripts installation"
|
|
return
|
|
fi
|
|
|
|
ui_print " [*] Installing scripts..."
|
|
if command -v rknnovpn_install_copy_tree >/dev/null 2>&1; then
|
|
rknnovpn_install_copy_tree "$SRC_SCRIPTS" "${RKNNOVPN_DIR}/scripts"
|
|
else
|
|
find "$SRC_SCRIPTS" -type f 2>/dev/null | while IFS= read -r script_file; do
|
|
[ ! -f "$script_file" ] && continue
|
|
script_rel="${script_file#$SRC_SCRIPTS/}"
|
|
script_dst="${RKNNOVPN_DIR}/scripts/${script_rel}"
|
|
mkdir -p "$(dirname "$script_dst")" 2>/dev/null
|
|
cp -f "$script_file" "$script_dst"
|
|
done
|
|
fi
|
|
find "$SRC_SCRIPTS" -type f 2>/dev/null | while IFS= read -r script_file; do
|
|
[ -f "$script_file" ] && ui_print " [*] -> ${script_file#$SRC_SCRIPTS/}"
|
|
done
|
|
}
|
|
|
|
remove_obsolete_external_cleanup() {
|
|
obsolete_roll="roll""back"
|
|
obsolete_deferred="uninstall-clean""up"
|
|
|
|
ui_print " [*] Removing obsolete rollback/deferred cleanup hooks..."
|
|
rm -f \
|
|
"/data/adb/service.d/99-rknnovpn-${obsolete_roll}-guard.sh" \
|
|
"/data/adb/service.d/99-rknnovpn-${obsolete_deferred}.sh" 2>/dev/null || true
|
|
rm -rf \
|
|
"/data/adb/rknnovpn-${obsolete_roll}" \
|
|
"/data/adb/rknnovpn-${obsolete_deferred}" 2>/dev/null || true
|
|
}
|
|
|
|
file_sha256() {
|
|
file="$1"
|
|
if command -v rknnovpn_install_file_sha256 >/dev/null 2>&1; then
|
|
rknnovpn_install_file_sha256 "$file"
|
|
return $?
|
|
fi
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "$file" 2>/dev/null | awk '{print $1}'
|
|
return $?
|
|
fi
|
|
if [ -n "$BUSYBOX" ] && "$BUSYBOX" sha256sum "$file" >/dev/null 2>&1; then
|
|
"$BUSYBOX" sha256sum "$file" 2>/dev/null | awk '{print $1}'
|
|
return $?
|
|
fi
|
|
if command -v toybox >/dev/null 2>&1; then
|
|
toybox sha256sum "$file" 2>/dev/null | awk '{print $1}'
|
|
return $?
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
safe_release_name() {
|
|
if command -v rknnovpn_safe_release_name >/dev/null 2>&1; then
|
|
rknnovpn_safe_release_name "$1"
|
|
return $?
|
|
fi
|
|
raw="$1"
|
|
safe="$(echo "$raw" | sed 's/[^A-Za-z0-9._-]/_/g; s/^[._-]*//; s/[._-]*$//')"
|
|
[ -n "$safe" ] || safe="unknown"
|
|
echo "$safe"
|
|
}
|
|
|
|
copy_if_present() {
|
|
if command -v rknnovpn_copy_if_present >/dev/null 2>&1; then
|
|
rknnovpn_copy_if_present "$1" "$2"
|
|
return $?
|
|
fi
|
|
src="$1"
|
|
dst="$2"
|
|
if [ -f "$src" ]; then
|
|
mkdir -p "$(dirname "$dst")" 2>/dev/null
|
|
cp -f "$src" "$dst"
|
|
fi
|
|
}
|
|
|
|
append_manifest_hash() {
|
|
if command -v rknnovpn_manifest_hash_entry >/dev/null 2>&1; then
|
|
rknnovpn_manifest_hash_entry "$1" "$2" "$3"
|
|
return $?
|
|
fi
|
|
rel="$1"
|
|
file="$2"
|
|
manifest="$3"
|
|
[ -f "$file" ] || return 0
|
|
hash="$(file_sha256 "$file")" || return 1
|
|
if [ "$MANIFEST_FIRST" -eq 0 ]; then
|
|
printf ',\n' >> "$manifest"
|
|
fi
|
|
MANIFEST_FIRST=0
|
|
printf ' "%s": "%s"' "$rel" "$hash" >> "$manifest"
|
|
}
|
|
|
|
write_install_manifest() {
|
|
release_dir="$1"
|
|
version="$2"
|
|
manifest="${release_dir}/install-manifest.json"
|
|
tmp_manifest="${manifest}.tmp"
|
|
MANIFEST_FIRST=1
|
|
|
|
{
|
|
printf '{\n'
|
|
printf ' "version": "%s",\n' "$version"
|
|
printf ' "installed_at": "%s",\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date '+%Y-%m-%dT%H:%M:%SZ')"
|
|
printf ' "files_sha256": {\n'
|
|
} > "$tmp_manifest" || return 1
|
|
|
|
for rel in \
|
|
bin/rknnovpn-runtime \
|
|
bin/sing-box \
|
|
bin/xray \
|
|
module/OWNERSHIP.md \
|
|
module/module.prop \
|
|
module/service.sh \
|
|
module/post-fs-data.sh \
|
|
module/uninstall.sh \
|
|
module/customize.sh \
|
|
module/sepolicy.rule \
|
|
module/scripts/dns.sh \
|
|
module/scripts/iptables.sh \
|
|
module/scripts/rescue_reset.sh \
|
|
module/scripts/routing.sh \
|
|
module/scripts/lib/rknnovpn_env.sh \
|
|
module/scripts/lib/rknnovpn_install.sh \
|
|
module/scripts/lib/rknnovpn_installer_flow.sh \
|
|
module/scripts/lib/rknnovpn_netstack.sh \
|
|
module/scripts/lib/rknnovpn_iptables_rules.sh \
|
|
module/defaults/config.json; do
|
|
append_manifest_hash "$rel" "${release_dir}/${rel}" "$tmp_manifest" || return 1
|
|
done
|
|
|
|
{
|
|
printf '\n'
|
|
printf ' }\n'
|
|
printf '}\n'
|
|
} >> "$tmp_manifest" || return 1
|
|
mv -f "$tmp_manifest" "$manifest"
|
|
}
|
|
|
|
update_current_release_link() {
|
|
release_dir="$1"
|
|
current="${RKNNOVPN_DIR}/current"
|
|
|
|
if [ -e "$current" ] && [ ! -L "$current" ]; then
|
|
rm -rf "$current" 2>/dev/null || {
|
|
ui_print " [!] Failed to remove unsupported current release path"
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
rm -f "$current" 2>/dev/null
|
|
ln -s "$release_dir" "$current" 2>/dev/null || {
|
|
ui_print " [!] Failed to update current release link"
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
install_release_catalog() {
|
|
version="$(module_version)"
|
|
[ -n "$version" ] || version="unknown"
|
|
safe_version="$(safe_release_name "$version")"
|
|
release_dir="${RKNNOVPN_DIR}/releases/${safe_version}"
|
|
if [ -e "$release_dir" ]; then
|
|
suffix="$(date +%s 2>/dev/null || echo $$)"
|
|
release_dir="${release_dir}-${suffix}"
|
|
fi
|
|
|
|
ui_print " [*] Recording versioned release: ${version}"
|
|
mkdir -p "${release_dir}/bin" "${release_dir}/module/scripts" "${release_dir}/module/defaults" || {
|
|
ui_print " [!] Failed to create release catalog"
|
|
return
|
|
}
|
|
|
|
for bin_name in rknnovpn-runtime sing-box xray; do
|
|
copy_if_present "${RKNNOVPN_DIR}/bin/${bin_name}" "${release_dir}/bin/${bin_name}"
|
|
done
|
|
for name in OWNERSHIP.md module.prop service.sh post-fs-data.sh uninstall.sh customize.sh sepolicy.rule; do
|
|
copy_if_present "${MODPATH}/${name}" "${release_dir}/module/${name}"
|
|
done
|
|
if [ -d "${MODPATH}/scripts" ]; then
|
|
find "${MODPATH}/scripts" -type f 2>/dev/null | while IFS= read -r script_file; do
|
|
[ -f "$script_file" ] || continue
|
|
script_rel="${script_file#${MODPATH}/scripts/}"
|
|
copy_if_present "$script_file" "${release_dir}/module/scripts/${script_rel}"
|
|
done
|
|
fi
|
|
if [ -d "${MODPATH}/defaults" ]; then
|
|
for default_file in "${MODPATH}/defaults"/*; do
|
|
[ -f "$default_file" ] && copy_if_present "$default_file" "${release_dir}/module/defaults/$(basename "$default_file")"
|
|
done
|
|
fi
|
|
|
|
if write_install_manifest "$release_dir" "$version"; then
|
|
update_current_release_link "$release_dir"
|
|
else
|
|
ui_print " [!] Failed to write release manifest"
|
|
fi
|
|
}
|
|
|
|
set_capabilities() {
|
|
SETCAP=""
|
|
if command -v setcap >/dev/null 2>&1; then
|
|
SETCAP="setcap"
|
|
elif [ -x "/system/bin/setcap" ]; then
|
|
SETCAP="/system/bin/setcap"
|
|
elif [ -x "${RKNNOVPN_DIR}/bin/setcap" ]; then
|
|
SETCAP="${RKNNOVPN_DIR}/bin/setcap"
|
|
fi
|
|
|
|
if [ -z "$SETCAP" ]; then
|
|
ui_print " [!] setcap not found — skipping capability assignment"
|
|
ui_print " [!] Runtime will rely on running as root"
|
|
return
|
|
fi
|
|
|
|
ui_print " [*] Setting capabilities with setcap..."
|
|
CAPS="cap_net_admin,cap_net_raw,cap_net_bind_service+ep"
|
|
|
|
for bin_name in rknnovpn-runtime sing-box xray; do
|
|
bin_path="${RKNNOVPN_DIR}/bin/${bin_name}"
|
|
if [ -f "$bin_path" ]; then
|
|
$SETCAP "$CAPS" "$bin_path" 2>/dev/null
|
|
if [ $? -eq 0 ]; then
|
|
ui_print " [*] ${bin_name}: capabilities set"
|
|
else
|
|
ui_print " [!] ${bin_name}: setcap failed (will run as root)"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
set_permissions_and_caps() {
|
|
ui_print " [*] Setting permissions..."
|
|
if command -v rknnovpn_apply_data_permissions >/dev/null 2>&1; then
|
|
rknnovpn_apply_data_permissions
|
|
else
|
|
chown -R 0:${RKNNOVPN_GID} "${RKNNOVPN_DIR}/bin" 2>/dev/null
|
|
chmod 0750 "${RKNNOVPN_DIR}/bin" 2>/dev/null
|
|
chown -R 0:0 "${RKNNOVPN_DIR}/scripts" "${RKNNOVPN_DIR}/config" "${RKNNOVPN_DIR}/logs" 2>/dev/null
|
|
chmod 0755 "${RKNNOVPN_DIR}/scripts" 2>/dev/null
|
|
chmod 0700 "${RKNNOVPN_DIR}/config" "${RKNNOVPN_DIR}/logs" 2>/dev/null
|
|
chown -R 0:${RKNNOVPN_GID} "${RKNNOVPN_DIR}/run" 2>/dev/null
|
|
chmod 0750 "${RKNNOVPN_DIR}/run" 2>/dev/null
|
|
fi
|
|
ui_print " [*] Permissions set"
|
|
set_capabilities
|
|
}
|
|
|
|
set_module_permissions() {
|
|
set_perm_recursive "$MODPATH" 0 0 0755 0644
|
|
for f in "$MODPATH"/*.sh; do
|
|
[ -f "$f" ] && set_perm "$f" 0 0 0755
|
|
done
|
|
}
|
|
|
|
rknnovpn_installer_run() {
|
|
ui_print_header
|
|
|
|
detect_root_manager
|
|
|
|
ui_print " --- Pre-flight checks ---"
|
|
check_architecture
|
|
check_api_level
|
|
check_tproxy_support
|
|
|
|
ui_print ""
|
|
ui_print " --- Installation ---"
|
|
create_directory_structure
|
|
prepare_install_logs
|
|
prepare_fresh_runtime_state
|
|
install_default_config
|
|
mark_manual_start_required
|
|
install_binaries
|
|
install_scripts
|
|
remove_obsolete_external_cleanup
|
|
set_permissions_and_caps
|
|
install_release_catalog
|
|
set_module_permissions
|
|
|
|
ui_print ""
|
|
ui_print " --- Installation complete ---"
|
|
ui_print ""
|
|
ui_print " Data directory: ${RKNNOVPN_DIR}/"
|
|
ui_print " Config file: ${RKNNOVPN_DIR}/config/config.json"
|
|
ui_print " Runtime CLI: ${RKNNOVPN_DIR}/bin/rknnovpn-runtime"
|
|
ui_print " Core binary: ${RKNNOVPN_DIR}/bin/sing-box"
|
|
ui_print " XHTTP core: ${RKNNOVPN_DIR}/bin/xray"
|
|
ui_print ""
|
|
ui_print ""
|
|
ui_print " Reboot to activate RKNnoVPN."
|
|
ui_print ""
|
|
}
|