Fix static ARM builds and OpenWrt VoWiFi setup

This commit is contained in:
MengMengCode
2026-08-12 15:58:02 +08:00
parent 296f963885
commit f17d925c4c
18 changed files with 980 additions and 136 deletions
+197 -11
View File
@@ -1,12 +1,11 @@
#!/usr/bin/env bash
#
# vocat install / update script for binary + systemd deployments.
# vocat install / update script for systemd and OpenWrt/procd deployments.
#
# Usage:
# sudo bash install.sh [version] # install a specific version
# sudo bash install.sh # install latest release
# sudo bash install.sh --force # reinstall even at the same version
# curl -fsSL <raw url> | sudo bash # one-liner (latest)
# bash install.sh [version] # run directly when already root
# sudo bash install.sh [version] # run through sudo as a normal user
# bash install.sh --check-env # check VoWiFi host prerequisites
#
# Behavior:
# - Prompts for script language (中文 / English) as soon as it runs.
@@ -14,7 +13,9 @@
# - On first install, generates a random 32-char admin password, writes it to
# /etc/vocat/env (0600, loaded by the systemd unit), and prints it ONCE.
# - On update, preserves the existing env file and credentials.
# - (Re)writes the systemd unit and restarts the service.
# - Verifies Linux XFRM/IPsec support required by IMS; on OpenWrt it tries
# the matching opkg packages first.
# - (Re)writes a systemd or OpenWrt/procd service and restarts it.
#
# Published script: must contain no secrets, IPs, or passwords.
@@ -31,6 +32,7 @@ LINK_PATH="/usr/local/bin/vocat"
ENV_DIR="/etc/vocat"
ENV_FILE="${ENV_DIR}/env"
UNIT_PATH="/etc/systemd/system/vocat.service"
OPENWRT_INIT_PATH="/etc/init.d/vocat"
# --- Language ----------------------------------------------------------------
LANG_CHOICE=""
@@ -76,14 +78,44 @@ die() {
prompt_language
# BusyBox/OpenWrt images often omit coreutils' install(1). Provide the small
# subset used by this script so the same installer works on router firmware.
if ! command -v install >/dev/null 2>&1; then
install() {
if [ "${1:-}" = "-d" ]; then
shift
local mode="0755"
if [ "${1:-}" = "-m" ]; then
mode="$2"
shift 2
fi
mkdir -p "$@"
chmod "$mode" "$@"
return
fi
local mode="0755"
if [ "${1:-}" = "-m" ]; then
mode="$2"
shift 2
fi
[ "$#" -eq 2 ] || return 2
cp "$1" "$2"
chmod "$mode" "$2"
}
fi
# --- Parse args --------------------------------------------------------------
FORCE=0
CHECK_ENV=0
SKIP_VOWIFI_CHECK="${VOCAT_SKIP_VOWIFI_CHECK:-0}"
TARGET_VERSION=""
for arg in "$@"; do
case "$arg" in
--force) FORCE=1 ;;
--check-env) CHECK_ENV=1 ;;
--skip-vowifi-check) SKIP_VOWIFI_CHECK=1 ;;
-h|--help)
msg "用法: sudo bash install.sh [--force] [版本]" "Usage: sudo bash install.sh [--force] [version]"
msg "用法: bash install.sh [--force] [--check-env] [--skip-vowifi-check] [版本]" "Usage: bash install.sh [--force] [--check-env] [--skip-vowifi-check] [version]"
exit 0
;;
*) TARGET_VERSION="${arg#v}" ;;
@@ -110,6 +142,89 @@ resolve_target_version() {
TARGET_VERSION="${tag#v}"
}
# --- Host prerequisites ------------------------------------------------------
is_openwrt() {
[ -f /etc/openwrt_release ] || [ -x /sbin/procd ]
}
xfrm_works() {
command -v ip >/dev/null 2>&1 && ip xfrm state list >/dev/null 2>&1
}
opkg_has_package() {
opkg list "$1" 2>/dev/null | grep -q "^$1 -"
}
install_openwrt_vowifi_packages() {
msg "正在检查 OpenWrt/Kwrt 的 VoWiFi 内核组件..." "Checking OpenWrt/Kwrt VoWiFi kernel components..."
opkg update >/dev/null 2>&1 || msg \
"警告:opkg 软件源更新失败,将使用现有索引继续检查。" \
"Warning: opkg feed update failed; checking the existing index."
local packages=""
local package
for package in \
ip-full \
kmod-ipsec kmod-ipsec4 kmod-ipsec6 \
kmod-crypto-authenc kmod-crypto-cbc kmod-crypto-aes \
kmod-crypto-hmac kmod-crypto-sha1; do
if opkg_has_package "$package"; then
packages="$packages $package"
fi
done
if [ -n "$packages" ]; then
# Kernel packages must come from this firmware's own feed. opkg checks
# the kernel ABI and refuses mismatched modules; never bypass that check.
# shellcheck disable=SC2086
opkg install $packages >/dev/null 2>&1 || true
fi
}
install_linux_ip_tool() {
command -v ip >/dev/null 2>&1 && return 0
if command -v apt-get >/dev/null 2>&1; then
apt-get update -qq && apt-get install -y iproute2
elif command -v dnf >/dev/null 2>&1; then
dnf install -y iproute
elif command -v yum >/dev/null 2>&1; then
yum install -y iproute
elif command -v pacman >/dev/null 2>&1; then
pacman -Sy --noconfirm iproute2
elif command -v apk >/dev/null 2>&1; then
apk add --no-cache iproute2
fi
}
check_vowifi_environment() {
if [ "$SKIP_VOWIFI_CHECK" = "1" ]; then
msg \
"已跳过 VoWiFi 内核环境检查;IMS 通话和短信可能不可用。" \
"Skipped the VoWiFi kernel check; IMS calls and SMS may not work."
return
fi
if is_openwrt && command -v opkg >/dev/null 2>&1; then
# Install the crypto algorithms even when NETLINK_XFRM already works;
# some minimal images provide xfrm_user but omit AES-CBC/authenc.
install_openwrt_vowifi_packages
elif ! xfrm_works; then
install_linux_ip_tool
fi
if xfrm_works; then
msg "VoWiFi XFRM/IPsec 环境安装并验证成功。" "VoWiFi XFRM/IPsec environment installed and verified."
return
fi
if is_openwrt; then
die \
"当前 OpenWrt/Kwrt 内核 $(uname -r) 不支持 NETLINK_XFRM,且软件源没有匹配的 kmod-ipsec。请使用包含 kmod-ipsec、kmod-ipsec4、kmod-ipsec6、kmod-crypto-authenc、kmod-crypto-cbc、kmod-crypto-aes 和 kmod-crypto-sha1 的同版本固件;严禁安装其他内核版本的 kmod。仅使用非 VoWiFi 功能时可加 --skip-vowifi-check。" \
"The OpenWrt/Kwrt kernel $(uname -r) lacks NETLINK_XFRM and its feed has no matching kmod-ipsec. Use a firmware built with matching kmod-ipsec, kmod-ipsec4/6, crypto-authenc, CBC, AES and SHA1 modules. Never force kmods from another kernel. Use --skip-vowifi-check only for non-VoWiFi operation."
fi
die \
"当前 Linux 内核不支持 XFRM/IPsecVoWiFi IMS 无法工作。请启用 CONFIG_XFRM、CONFIG_XFRM_USER、CONFIG_INET_ESP、CONFIG_INET6_ESP、AES-CBC 和 HMAC-SHA1。" \
"This Linux kernel lacks XFRM/IPsec required by VoWiFi IMS. Enable CONFIG_XFRM, CONFIG_XFRM_USER, CONFIG_INET_ESP, CONFIG_INET6_ESP, AES-CBC and HMAC-SHA1."
}
# --- Skip if already installed at the same version ---------------------------
skip_if_equal() {
[ -x "$BINARY_PATH" ] || return 0
@@ -159,6 +274,10 @@ download_and_verify() {
[ -n "$expected" ] || die "SHA256SUMS 中找不到 $asset 的校验行。" "$asset not found in SHA256SUMS."
actual=$(sha256sum "${VOCAT_TMP}/vocat" | awk '{print $1}')
[ "$actual" = "$expected" ] || die "SHA-256 校验失败。" "SHA-256 verification failed."
chmod 0755 "${VOCAT_TMP}/vocat"
"${VOCAT_TMP}/vocat" version >/dev/null 2>&1 || die \
"Downloaded binary cannot run on this system; keeping the installed version." \
"The downloaded binary cannot run on this host; the installed version was not changed."
}
# --- Install binary ----------------------------------------------------------
@@ -246,11 +365,73 @@ EOF
chmod 0644 "$UNIT_PATH"
}
write_openwrt_init() {
cat > "$OPENWRT_INIT_PATH" <<'EOF'
#!/bin/sh /etc/rc.common
START=95
STOP=10
USE_PROCD=1
PROCD_TERM_TIMEOUT=40
PROGRAM=/opt/vocat/bin/vocat
ENV_FILE=/etc/vocat/env
start_service() {
procd_open_instance
procd_set_param command "$PROGRAM" serve
procd_set_param env VOCAT_DATABASE_PATH=/opt/vocat/data/vocat.db
if [ -r "$ENV_FILE" ]; then
while IFS='=' read -r name value; do
case "$name" in VOCAT_*) procd_append_param env "$name=$value" ;; esac
done < "$ENV_FILE"
fi
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
service_triggers() { procd_add_reload_trigger vocat; }
EOF
chmod 0755 "$OPENWRT_INIT_PATH"
}
write_service() {
if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then
write_unit
return
fi
if [ -x /sbin/procd ] || [ -x /sbin/ubusd ]; then
write_openwrt_init
return
fi
die "Unsupported service manager." "Neither systemd nor OpenWrt procd was detected."
}
enable_and_start() {
if [ -x "$OPENWRT_INIT_PATH" ] && { [ -x /sbin/procd ] || [ -x /sbin/ubusd ]; }; then
"$OPENWRT_INIT_PATH" enable
if "$OPENWRT_INIT_PATH" restart; then
sleep 2
if "$OPENWRT_INIT_PATH" running; then
rm -f "${BINARY_PATH}.bak"
return
fi
fi
if [ -e "${BINARY_PATH}.bak" ]; then
cp -a "${BINARY_PATH}.bak" "$BINARY_PATH"
"$OPENWRT_INIT_PATH" restart || true
fi
die "OpenWrt vocat service failed to start." "The OpenWrt vocat service failed to start."
fi
systemctl daemon-reload
systemctl enable vocat
if systemctl restart vocat; then
return
local attempt
for attempt in 1 2 3 4 5; do
if systemctl is-active --quiet vocat; then
rm -f "${BINARY_PATH}.bak"
return
fi
sleep 1
done
fi
if [ -e "${BINARY_PATH}.bak" ]; then
msg "新版本启动失败,正在恢复旧二进制。" "The new version failed to start; restoring the previous binary."
@@ -261,14 +442,19 @@ enable_and_start() {
}
# --- Main --------------------------------------------------------------------
resolve_target_version
detect_arch
check_vowifi_environment
if [ "$CHECK_ENV" -eq 1 ]; then
msg "VoCat 运行环境检查完成。" "VoCat host environment check completed."
exit 0
fi
resolve_target_version
skip_if_equal
download_and_verify
install_binary
ensure_data_dir
setup_env
write_unit
write_service
enable_and_start
if [ "$FIRST_INSTALL" -eq 1 ]; then
@@ -281,7 +467,7 @@ if [ "$FIRST_INSTALL" -eq 1 ]; then
echo
msg "用户名为 admin。请立即记录此密码。" "Username is admin. Record this password now."
msg "登录后或运行以下命令修改密码:" "Change it via the web UI or run:"
echo " sudo vocat menu"
echo " vocat menu"
msg "==========================================" "=============================================="
else
echo
+30
View File
@@ -0,0 +1,30 @@
#!/bin/sh /etc/rc.common
START=95
STOP=10
USE_PROCD=1
PROCD_TERM_TIMEOUT=40
PROGRAM=/opt/vocat/bin/vocat
ENV_FILE=/etc/vocat/env
start_service() {
procd_open_instance
procd_set_param command "$PROGRAM" serve
procd_set_param env VOCAT_DATABASE_PATH=/opt/vocat/data/vocat.db
if [ -r "$ENV_FILE" ]; then
while IFS='=' read -r name value; do
case "$name" in
VOCAT_*) procd_append_param env "$name=$value" ;;
esac
done < "$ENV_FILE"
fi
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger vocat
}