mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-05 18:44:06 +08:00
update 2026-07-28 01:38:11
This commit is contained in:
1 parent
20b786ab77
commit
25d570431d
6243 files changed
+2850542
No files matched your search
@@ -0,0 +1,293 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# amlogic_check_firmware.sh — check and download OpenWrt firmware updates.
|
||||
#
|
||||
# Purpose: query the configured GitHub repository for the latest firmware
|
||||
# release, compare with the local version, and download to /tmp/amlogic
|
||||
# if a newer version is available.
|
||||
|
||||
# Set a fixed value
|
||||
check_option="${1}"
|
||||
download_version="${2}"
|
||||
TMP_CHECK_DIR="/tmp/amlogic"
|
||||
AMLOGIC_SOC_FILE="/etc/flippy-openwrt-release"
|
||||
START_LOG="${TMP_CHECK_DIR}/amlogic_check_firmware.log"
|
||||
RUNNING_LOG="${TMP_CHECK_DIR}/amlogic_running_script.log"
|
||||
LOG_FILE="${TMP_CHECK_DIR}/amlogic.log"
|
||||
support_platform=("allwinner" "rockchip" "amlogic" "qemu-aarch64")
|
||||
LOGTIME="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
[[ -d ${TMP_CHECK_DIR} ]] || mkdir -p ${TMP_CHECK_DIR}
|
||||
|
||||
# Clean the running log
|
||||
clean_running() {
|
||||
echo -e '' >${RUNNING_LOG} 2>/dev/null && sync
|
||||
}
|
||||
|
||||
# Add log
|
||||
tolog() {
|
||||
echo -e "${1}" >${START_LOG}
|
||||
echo -e "${LOGTIME} ${1}" >>${LOG_FILE}
|
||||
[[ -n "${2}" && "${2}" -eq "1" ]] && clean_running && exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the root file system
|
||||
get_root_partition_name() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_name=$(df "${path}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
[[ -n "${partition_name}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_name}" ]] && tolog "Cannot find the root partition!" "1"
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Check running scripts, prohibit running concurrently
|
||||
this_running_log="3@OpenWrt update in progress, try again later!"
|
||||
running_script="$(cat ${RUNNING_LOG} 2>/dev/null | xargs)"
|
||||
if [[ -n "${running_script}" ]]; then
|
||||
run_num="$(echo "${running_script}" | awk -F "@" '{print $1}')"
|
||||
run_log="$(echo "${running_script}" | awk -F "@" '{print $2}')"
|
||||
fi
|
||||
if [[ -n "${run_log}" && "${run_num}" -ne "3" ]]; then
|
||||
echo -e "${run_log}" >${START_LOG} 2>/dev/null && sync && exit 1
|
||||
else
|
||||
echo -e "${this_running_log}" >${RUNNING_LOG} 2>/dev/null && sync
|
||||
fi
|
||||
|
||||
# Find the partition where root is located
|
||||
ROOT_PTNAME="$(get_root_partition_name)"
|
||||
|
||||
# Find the disk where the partition is located, only supports mmcblk?p? sd?? hd?? vd?? and other formats
|
||||
case "${ROOT_PTNAME}" in
|
||||
mmcblk?p[1-4])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
PARTITION_NAME="p"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')"
|
||||
PARTITION_NAME=""
|
||||
;;
|
||||
nvme?n?p[1-4])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
PARTITION_NAME="p"
|
||||
;;
|
||||
*)
|
||||
tolog "Unable to recognize the disk type of ${ROOT_PTNAME}!" "1"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Set the default download path
|
||||
FIRMWARE_DOWNLOAD_PATH="/mnt/${EMMC_NAME}${PARTITION_NAME}4"
|
||||
[ -d "${FIRMWARE_DOWNLOAD_PATH}/.luci-app-amlogic" ] || mkdir -p "${FIRMWARE_DOWNLOAD_PATH}/.luci-app-amlogic"
|
||||
|
||||
# Check release file
|
||||
if [[ -s "${AMLOGIC_SOC_FILE}" ]]; then
|
||||
source "${AMLOGIC_SOC_FILE}" 2>/dev/null
|
||||
PLATFORM="${PLATFORM}"
|
||||
BOARD="${BOARD}"
|
||||
else
|
||||
tolog "${AMLOGIC_SOC_FILE} file is missing!" "1"
|
||||
fi
|
||||
if [[ -z "${PLATFORM}" || -z "$(echo "${support_platform[@]}" | grep -w "${PLATFORM}")" || -z "${BOARD}" ]]; then
|
||||
tolog "Missing [ PLATFORM / BOARD ] value in ${AMLOGIC_SOC_FILE} file." "1"
|
||||
fi
|
||||
|
||||
tolog "PLATFORM: [ ${PLATFORM} ], BOARD: [ ${BOARD} ], Use in [ ${EMMC_NAME} ]"
|
||||
sleep 2
|
||||
|
||||
# 01. Query local version information
|
||||
tolog "01. Query version information..."
|
||||
# 01.01 Query the current version
|
||||
current_kernel_v="$(uname -r 2>/dev/null | grep -oE '^[1-9].[0-9]{1,3}.[0-9]+')"
|
||||
tolog "01.01 current version: ${current_kernel_v}"
|
||||
sleep 2
|
||||
|
||||
# 01.01 Version comparison
|
||||
main_line_version="$(echo ${current_kernel_v} | awk -F '.' '{print $1"."$2}')"
|
||||
|
||||
# 01.02. Query the selected branch in the settings
|
||||
server_kernel_branch="$(uci get amlogic.config.amlogic_kernel_branch 2>/dev/null | grep -oE '^[1-9].[0-9]{1,3}')"
|
||||
if [[ -z "${server_kernel_branch}" ]]; then
|
||||
server_kernel_branch="${main_line_version}"
|
||||
uci set amlogic.config.amlogic_kernel_branch="${main_line_version}" 2>/dev/null
|
||||
uci commit amlogic 2>/dev/null
|
||||
fi
|
||||
if [[ "${server_kernel_branch}" != "${main_line_version}" ]]; then
|
||||
main_line_version="${server_kernel_branch}"
|
||||
tolog "01.02 Select branch: ${main_line_version}"
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# 01.03. Download server version documentation
|
||||
server_firmware_url="$(uci get amlogic.config.amlogic_firmware_repo 2>/dev/null)"
|
||||
[[ ! -z "${server_firmware_url}" ]] || tolog "01.03 The custom firmware download repo is invalid." "1"
|
||||
releases_tag_keywords="$(uci get amlogic.config.amlogic_firmware_tag 2>/dev/null)"
|
||||
[[ ! -z "${releases_tag_keywords}" ]] || tolog "01.04 The custom firmware tag keywords is invalid." "1"
|
||||
firmware_suffix="$(uci get amlogic.config.amlogic_firmware_suffix 2>/dev/null)"
|
||||
[[ ! -z "${firmware_suffix}" ]] || tolog "01.05 The custom firmware suffix is invalid." "1"
|
||||
|
||||
if [[ "${server_firmware_url}" == http* ]]; then
|
||||
server_firmware_url="${server_firmware_url#*com\/}"
|
||||
fi
|
||||
|
||||
# 02. Check Updated
|
||||
check_updated() {
|
||||
tolog "02.01 Search for tags in the first 5 pages of releases..."
|
||||
|
||||
# Get the tags list
|
||||
firmware_tags_array=()
|
||||
for i in {1..5}; do
|
||||
while IFS= read -r firmware_tags_name; do
|
||||
firmware_tags_name="$(echo "${firmware_tags_name}" | sed 's/releases\/tag\///g')"
|
||||
if [[ -n "${firmware_tags_name}" ]]; then
|
||||
firmware_tags_array+=("${firmware_tags_name}")
|
||||
fi
|
||||
done < <(
|
||||
curl -fsSL -m 10 \
|
||||
https://github.com/${server_firmware_url}/releases?page=${i} |
|
||||
grep -oE 'releases/tag/([^" ]+)'
|
||||
)
|
||||
done
|
||||
|
||||
if [[ "${#firmware_tags_array[*]}" -eq "0" ]]; then
|
||||
tolog "02.01.01 Unable to retrieve a list of firmware tags." "1"
|
||||
fi
|
||||
|
||||
tolog "02.02 Search for tags containing the keyword..."
|
||||
|
||||
# Search for tags containing the keyword
|
||||
for i in "${firmware_tags_array[@]}"; do
|
||||
if [[ "${i}" == *"${releases_tag_keywords}"* ]]; then
|
||||
firmware_releases_tag="${i}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "${firmware_releases_tag}" ]]; then
|
||||
tolog "02.02.01 Tags: ${firmware_releases_tag}"
|
||||
sleep 2
|
||||
else
|
||||
tolog "02.02.01 No matching tags found." "1"
|
||||
fi
|
||||
|
||||
tolog "02.03 Start searching for firmware download links..."
|
||||
|
||||
# Retrieve the HTML code of the tags list page
|
||||
html_code="$(
|
||||
curl -fsSL -m 10 \
|
||||
https://github.com/${server_firmware_url}/releases/expanded_assets/${firmware_releases_tag}
|
||||
)"
|
||||
|
||||
# Set the regular expression for the OpenWrt filename
|
||||
op_file_pattern=".*_${BOARD}_.*k${main_line_version}\.[0-9]+.*${firmware_suffix}"
|
||||
# Find the <li> list item where the OpenWrt file is located
|
||||
li_block=$(awk -v pattern="${op_file_pattern}" -v RS='</li>' '$0 ~ pattern { print $0 "</li>"; exit }' <<<"${html_code}")
|
||||
[[ -z "${li_block}" ]] && tolog "02.03.01 No matching download links found." "1"
|
||||
|
||||
# Find the OpenWrt filename
|
||||
latest_url=$(echo "${li_block}" | grep -oE "/[^\"]*_${BOARD}_.*k${main_line_version}\.[0-9]+[^\"]*${firmware_suffix}" | sort -urV | head -n 1 | xargs basename 2>/dev/null)
|
||||
tolog "02.03.02 OpenWrt file: ${latest_url}"
|
||||
|
||||
# Find the date of the latest update
|
||||
latest_updated_at=$(echo "${li_block}" | grep -o 'datetime="[^"]*"' | sed 's/datetime="//; s/"//')
|
||||
tolog "02.03.03 Latest updated at: ${latest_updated_at}"
|
||||
# Format the date for display
|
||||
date_display_format="$(echo ${latest_updated_at} | tr 'T' '(' | tr 'Z' ')')"
|
||||
[[ -z "${latest_url}" || -z "${latest_updated_at}" ]] && tolog "02.03.04 The download URL or date is invalid." "1"
|
||||
|
||||
# Find the firmware sha256 value
|
||||
latest_firmware_sha256="$(echo "${li_block}" | grep -o 'value="sha256:[^"]*' | sed 's/value="sha256://')"
|
||||
tolog "02.03.05 OpenWrt sha256: ${latest_firmware_sha256}"
|
||||
|
||||
# Check the firmware update code
|
||||
down_check_code="${latest_updated_at}.${main_line_version}"
|
||||
op_release_code="${FIRMWARE_DOWNLOAD_PATH}/.luci-app-amlogic/op_release_code"
|
||||
if [[ -s "${op_release_code}" ]]; then
|
||||
update_check_code="$(cat ${op_release_code} | xargs)"
|
||||
if [[ -n "${update_check_code}" && "${update_check_code}" == "${down_check_code}" ]]; then
|
||||
tolog "02.04 Already the latest version, no need to update." "1"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prompt to update
|
||||
if [[ -n "${latest_url}" ]]; then
|
||||
tolog '<input type="button" class="cbi-button cbi-button-reload" value="Download" onclick="return b_check_firmware(this, '"'download_${latest_updated_at}@${firmware_releases_tag}/${latest_url}@${latest_firmware_sha256}'"')"/> Latest updated: '${date_display_format}'' "1"
|
||||
else
|
||||
tolog "02.05 No OpenWrt available, please use another kernel branch." "1"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 03. Download Openwrt
|
||||
download_firmware() {
|
||||
tolog "03. Start download the Openwrt..."
|
||||
|
||||
# Get the openwrt firmware download path
|
||||
if [[ "${download_version}" == "download_"* ]]; then
|
||||
tolog "03.01 Start downloading..."
|
||||
else
|
||||
tolog "03.01 Invalid parameter." "1"
|
||||
fi
|
||||
|
||||
# Delete other residual firmware files
|
||||
rm -f ${FIRMWARE_DOWNLOAD_PATH}/*${firmware_suffix} 2>/dev/null && sync
|
||||
rm -f ${FIRMWARE_DOWNLOAD_PATH}/*.img 2>/dev/null && sync
|
||||
rm -f ${FIRMWARE_DOWNLOAD_PATH}/sha256sums 2>/dev/null && sync
|
||||
|
||||
# OpenWrt make data
|
||||
latest_updated_at="$(echo ${download_version} | awk -F'@' '{print $1}' | sed -e s'|download_||'g)"
|
||||
down_check_code="${latest_updated_at}.${main_line_version}"
|
||||
|
||||
# Download firmware
|
||||
opfile_path="$(echo ${download_version} | awk -F'@' '{print $2}')"
|
||||
# Restore converted characters in file names(%2B to +)
|
||||
firmware_download_oldname="${opfile_path//%2B/+}"
|
||||
latest_url="https://github.com/${server_firmware_url}/releases/download/${firmware_download_oldname}"
|
||||
|
||||
# Find the firmware sha256 value
|
||||
releases_firmware_sha256sums="$(echo ${download_version} | awk -F'@' '{print $3}')"
|
||||
|
||||
# Download to OpenWrt file
|
||||
firmware_download_name="openwrt_${BOARD}_k${main_line_version}_github${firmware_suffix}"
|
||||
curl -fsSL "${latest_url}" -o "${FIRMWARE_DOWNLOAD_PATH}/${firmware_download_name}"
|
||||
if [[ "$?" -eq "0" && -s "${FIRMWARE_DOWNLOAD_PATH}/${firmware_download_name}" ]]; then
|
||||
tolog "03.02 OpenWrt downloaded successfully."
|
||||
else
|
||||
tolog "03.02 OpenWrt download failed." "1"
|
||||
fi
|
||||
|
||||
# Verify sha256sums if available
|
||||
if [[ -n "${releases_firmware_sha256sums}" ]]; then
|
||||
tolog "03.03 Perform sha256 checksum verification."
|
||||
|
||||
# If there is a sha256sum file, compare it
|
||||
download_firmware_sha256sums="$(sha256sum ${FIRMWARE_DOWNLOAD_PATH}/${firmware_download_name} | awk '{print $1}')"
|
||||
if [[ "${releases_firmware_sha256sums}" != "${download_firmware_sha256sums}" ]]; then
|
||||
tolog "03.03.01 sha256sum verification mismatched." "1"
|
||||
else
|
||||
tolog "03.03.02 sha256sum verification succeeded."
|
||||
fi
|
||||
fi
|
||||
|
||||
sync && sleep 3
|
||||
|
||||
tolog "You can update."
|
||||
|
||||
tolog '<input type="button" class="cbi-button cbi-button-reload" value="Update" onclick="return amlogic_update(this, '"'${firmware_download_name}@${down_check_code}@${FIRMWARE_DOWNLOAD_PATH}'"')"/>' "1"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
getopts 'cd' opts
|
||||
case "${opts}" in
|
||||
c | check)
|
||||
check_updated
|
||||
;;
|
||||
* | download)
|
||||
download_firmware
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,271 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# amlogic_check_kernel.sh — check and download OpenWrt kernel updates.
|
||||
#
|
||||
# Purpose: query the configured GitHub repository for the latest kernel
|
||||
# packages, compare with the running kernel version, and download to
|
||||
# /tmp/amlogic if a newer version is available.
|
||||
|
||||
# Set a fixed value
|
||||
check_option="${1}"
|
||||
download_version="${2}"
|
||||
TMP_CHECK_DIR="/tmp/amlogic"
|
||||
AMLOGIC_SOC_FILE="/etc/flippy-openwrt-release"
|
||||
START_LOG="${TMP_CHECK_DIR}/amlogic_check_kernel.log"
|
||||
RUNNING_LOG="${TMP_CHECK_DIR}/amlogic_running_script.log"
|
||||
LOG_FILE="${TMP_CHECK_DIR}/amlogic.log"
|
||||
support_platform=("allwinner" "rockchip" "amlogic" "qemu-aarch64")
|
||||
LOGTIME="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
[[ -d ${TMP_CHECK_DIR} ]] || mkdir -p ${TMP_CHECK_DIR}
|
||||
|
||||
# Clean the running log
|
||||
clean_running() {
|
||||
echo -e '' >${RUNNING_LOG} 2>/dev/null && sync
|
||||
}
|
||||
|
||||
# Add log
|
||||
tolog() {
|
||||
echo -e "${1}" >${START_LOG}
|
||||
echo -e "${LOGTIME} ${1}" >>${LOG_FILE}
|
||||
[[ -n "${2}" && "${2}" -eq "1" ]] && clean_running && exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the root file system
|
||||
get_root_partition_name() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_name=$(df "${path}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
[[ -n "${partition_name}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_name}" ]] && tolog "Cannot find the root partition!" "1"
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Check running scripts, prohibit running concurrently
|
||||
this_running_log="2@Kernel update in progress, try again later!"
|
||||
running_script="$(cat ${RUNNING_LOG} 2>/dev/null | xargs)"
|
||||
if [[ -n "${running_script}" ]]; then
|
||||
run_num="$(echo "${running_script}" | awk -F "@" '{print $1}')"
|
||||
run_log="$(echo "${running_script}" | awk -F "@" '{print $2}')"
|
||||
fi
|
||||
if [[ -n "${run_log}" && "${run_num}" -ne "2" ]]; then
|
||||
echo -e "${run_log}" >${START_LOG} 2>/dev/null && sync && exit 1
|
||||
else
|
||||
echo -e "${this_running_log}" >${RUNNING_LOG} 2>/dev/null && sync
|
||||
fi
|
||||
|
||||
# Find the partition where root is located
|
||||
ROOT_PTNAME="$(get_root_partition_name)"
|
||||
|
||||
# Find the disk where the partition is located, only supports mmcblk?p? sd?? hd?? vd?? and other formats
|
||||
case "${ROOT_PTNAME}" in
|
||||
mmcblk?p[1-4])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
PARTITION_NAME="p"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')"
|
||||
PARTITION_NAME=""
|
||||
;;
|
||||
nvme?n?p[1-4])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
PARTITION_NAME="p"
|
||||
;;
|
||||
*)
|
||||
tolog "Unable to recognize the disk type of ${ROOT_PTNAME}!" "1"
|
||||
;;
|
||||
esac
|
||||
# Set the default download path
|
||||
KERNEL_DOWNLOAD_PATH="/mnt/${EMMC_NAME}${PARTITION_NAME}4"
|
||||
|
||||
# Check release file
|
||||
if [[ -s "${AMLOGIC_SOC_FILE}" ]]; then
|
||||
source "${AMLOGIC_SOC_FILE}" 2>/dev/null
|
||||
PLATFORM="${PLATFORM}"
|
||||
else
|
||||
tolog "${AMLOGIC_SOC_FILE} file is missing!" "1"
|
||||
fi
|
||||
if [[ -z "${PLATFORM}" || -z "$(echo "${support_platform[@]}" | grep -w "${PLATFORM}")" ]]; then
|
||||
tolog "Missing [ PLATFORM ] value in ${AMLOGIC_SOC_FILE} file." "1"
|
||||
fi
|
||||
|
||||
tolog "PLATFORM: [ ${PLATFORM} ], Use in [ ${EMMC_NAME} ]"
|
||||
sleep 2
|
||||
|
||||
# Step 1. Set the kernel query api
|
||||
tolog "01. Start checking the kernel repository."
|
||||
firmware_repo="$(uci get amlogic.config.amlogic_firmware_repo 2>/dev/null)"
|
||||
[[ -n "${firmware_repo}" ]] || tolog "01.01 The custom kernel download repo is invalid." "1"
|
||||
kernel_repo="$(uci get amlogic.config.amlogic_kernel_path 2>/dev/null)"
|
||||
[[ -n "${kernel_repo}" ]] || tolog "01.02 The custom kernel download repo is invalid." "1"
|
||||
|
||||
if [[ "${kernel_repo}" == "opt/kernel" ]]; then
|
||||
uci set amlogic.config.amlogic_kernel_path="${firmware_repo}" 2>/dev/null
|
||||
uci commit amlogic 2>/dev/null
|
||||
kernel_repo="${firmware_repo}"
|
||||
fi
|
||||
|
||||
# Convert kernel repo to api format
|
||||
[[ "${kernel_repo}" =~ ^https: ]] && kernel_repo="$(echo ${kernel_repo} | awk -F'/' '{print $4"/"$5}')"
|
||||
kernel_api="https://github.com/${kernel_repo}"
|
||||
tolog "01.03 Kernel repo: ${kernel_repo}"
|
||||
# Get the current kernel uname
|
||||
kernel_uname="$(uname -r 2>/dev/null)"
|
||||
tolog "01.04 Current kernel uname: ${kernel_uname}"
|
||||
|
||||
# Get the kernel tag from uci config
|
||||
op_kernel_tags="$(uci get amlogic.config.amlogic_kernel_tags 2>/dev/null)"
|
||||
# Determine the kernel tag
|
||||
if [[ -n "${op_kernel_tags}" ]]; then
|
||||
kernel_tag="${op_kernel_tags/kernel_/}"
|
||||
else
|
||||
# Determine the kernel tag based on the current kernel uname
|
||||
if [[ "${kernel_uname}" =~ -rk3588 ]]; then
|
||||
kernel_tag="rk3588"
|
||||
elif [[ "${kernel_uname}" =~ -rk35xx ]]; then
|
||||
kernel_tag="rk35xx"
|
||||
elif [[ "${kernel_uname}" =~ -flippy ]]; then
|
||||
kernel_tag="flippy"
|
||||
elif [[ "${kernel_uname}" =~ -beta ]]; then
|
||||
kernel_tag="beta"
|
||||
else
|
||||
kernel_tag="stable"
|
||||
fi
|
||||
|
||||
# Save the kernel tag to uci config
|
||||
uci set amlogic.config.amlogic_kernel_tags="kernel_${kernel_tag}" 2>/dev/null
|
||||
uci commit amlogic 2>/dev/null
|
||||
fi
|
||||
tolog "01.05 Kernel tag: kernel_${kernel_tag}"
|
||||
sleep 2
|
||||
|
||||
# Step 2: Check if there is the latest kernel version
|
||||
check_kernel() {
|
||||
# 02. Query local version information
|
||||
tolog "02. Start checking the kernel version."
|
||||
|
||||
# 02.01 Get current kernel version
|
||||
[[ ! "${kernel_tag}" =~ ^(rk3588|rk35xx)$ ]] && kernel_uname="$(echo "${kernel_uname}" | cut -d'-' -f1)"
|
||||
[[ -n "${kernel_uname}" ]] || tolog "02.01 The current kernel version is not detected." "1"
|
||||
tolog "02.01 current version: ${kernel_uname}"
|
||||
sleep 2
|
||||
|
||||
# 02.02 Version comparison
|
||||
main_line_version="$(echo ${kernel_uname} | awk -F '.' '{print $1"."$2}')"
|
||||
|
||||
# 02.03 Query the selected branch in the settings
|
||||
server_kernel_branch="$(uci get amlogic.config.amlogic_kernel_branch 2>/dev/null | grep -oE '^[1-9].[0-9]{1,3}')"
|
||||
if [[ -z "${server_kernel_branch}" ]]; then
|
||||
server_kernel_branch="${main_line_version}"
|
||||
uci set amlogic.config.amlogic_kernel_branch="${main_line_version}" 2>/dev/null
|
||||
uci commit amlogic 2>/dev/null
|
||||
fi
|
||||
|
||||
if [[ "${server_kernel_branch}" != "${main_line_version}" ]]; then
|
||||
main_line_version="${server_kernel_branch}"
|
||||
main_line_now="0"
|
||||
tolog "02.02 Select branch: ${main_line_version}"
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Check the version on the server
|
||||
latest_version="$(
|
||||
curl -fsSL -m 10 \
|
||||
${kernel_api}/releases/expanded_assets/kernel_${kernel_tag} |
|
||||
grep -oE "${main_line_version}\.[0-9]+[^\"]*\.tar\.gz" | sed 's/.tar.gz//' |
|
||||
sort -urV | head -n 1
|
||||
)"
|
||||
[[ -n "${latest_version}" ]] || tolog "02.03 No kernel available, please use another kernel branch." "1"
|
||||
|
||||
tolog "02.04 current version: ${kernel_uname}, Latest version: ${latest_version}"
|
||||
sleep 2
|
||||
|
||||
# Get the sha256 value of the latest version
|
||||
latest_kernel_sha256="$(
|
||||
curl -fsSL -m 10 \
|
||||
${kernel_api}/releases/expanded_assets/kernel_${kernel_tag} |
|
||||
awk -v pattern="${latest_version}\.tar\.gz" -v RS='</li>' '$0 ~ pattern { print $0 "</li>"; exit }' |
|
||||
grep -o 'value="sha256:[^"]*' | sed 's/value="sha256://'
|
||||
)"
|
||||
[[ -n "${latest_kernel_sha256}" ]] && tolog "02.05 Kernel sha256: ${latest_kernel_sha256}"
|
||||
|
||||
if [[ "${latest_version}" == "${kernel_uname}" ]]; then
|
||||
tolog "02.06 Already the latest version, no need to update." "1"
|
||||
sleep 2
|
||||
else
|
||||
tolog '<input type="button" class="cbi-button cbi-button-reload" value="Download" onclick="return b_check_kernel(this, '"'download_${latest_version}_${latest_kernel_sha256}'"')"/> Latest version: '${latest_version}'' "1"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Step 3: Download the latest kernel version
|
||||
download_kernel() {
|
||||
tolog "03. Start download the kernel file."
|
||||
if [[ "${download_version}" == "download_"* ]]; then
|
||||
tolog "03.01 Start downloading..."
|
||||
else
|
||||
tolog "03.01 Invalid parameter" "1"
|
||||
fi
|
||||
|
||||
# Get the kernel file name
|
||||
kernel_file_name="$(echo "${download_version}" | cut -d '_' -f2)"
|
||||
# Restore converted characters in file names(%2B to +)
|
||||
kernel_file_name="${kernel_file_name//%2B/+}"
|
||||
# Get the sha256 value
|
||||
kernel_file_sha256="$(echo "${download_version}" | cut -d '_' -f3)"
|
||||
|
||||
# Delete other residual kernel files
|
||||
rm -f ${KERNEL_DOWNLOAD_PATH}/*.tar.gz
|
||||
rm -f ${KERNEL_DOWNLOAD_PATH}/sha256sums
|
||||
rm -rf ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}*
|
||||
|
||||
kernel_down_from="https://github.com/${kernel_repo}/releases/download/kernel_${kernel_tag}/${kernel_file_name}.tar.gz"
|
||||
|
||||
curl -fsSL "${kernel_down_from}" -o ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}.tar.gz
|
||||
[[ "${?}" -ne "0" ]] && tolog "03.02 The kernel download failed." "1"
|
||||
|
||||
# Verify sha256
|
||||
if [[ -n "${kernel_file_sha256}" ]]; then
|
||||
tolog "03.03 Perform sha256 checksum verification."
|
||||
|
||||
download_kernel_sha256sums="$(sha256sum ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}.tar.gz | awk '{print $1}')"
|
||||
if [[ "${kernel_file_sha256}" != "${download_kernel_sha256sums}" ]]; then
|
||||
tolog "03.03.01 sha256sum verification mismatched." "1"
|
||||
else
|
||||
tolog "03.03.02 sha256sum verification succeeded."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Decompress the kernel package
|
||||
tolog "03.04 Start decompressing the kernel package..."
|
||||
tar -xf ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}.tar.gz -C ${KERNEL_DOWNLOAD_PATH}
|
||||
[[ "${?}" -ne "0" ]] && tolog "03.05 Kernel decompression failed." "1"
|
||||
mv -f ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}/* ${KERNEL_DOWNLOAD_PATH}/
|
||||
|
||||
sync && sleep 3
|
||||
# Delete the downloaded kernel file
|
||||
rm -f ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}.tar.gz
|
||||
rm -rf ${KERNEL_DOWNLOAD_PATH}/${kernel_file_name}
|
||||
|
||||
tolog "03.06 The kernel is ready, you can update."
|
||||
sleep 2
|
||||
|
||||
#echo '<a href="javascript:;" onclick="return amlogic_kernel(this)">Update</a>' >$START_LOG
|
||||
tolog '<input type="button" class="cbi-button cbi-button-reload" value="Update" onclick="return amlogic_kernel(this)"/>' "1"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
getopts 'cd' opts
|
||||
case "${opts}" in
|
||||
c | check)
|
||||
check_kernel
|
||||
;;
|
||||
* | download)
|
||||
download_kernel
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,271 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# amlogic_check_plugin.sh — check and download luci-app-amlogic plugin updates.
|
||||
#
|
||||
# Purpose: query the configured GitHub repository for the latest plugin
|
||||
# release (JS or Lua branch), compare with the installed version, and
|
||||
# download the IPK/APK package to /tmp/amlogic if a newer version is available.
|
||||
|
||||
# Set a fixed value
|
||||
check_option="${1}"
|
||||
download_version="${2}"
|
||||
TMP_CHECK_DIR="/tmp/amlogic"
|
||||
AMLOGIC_SOC_FILE="/etc/flippy-openwrt-release"
|
||||
AMLOGIC_CONFIG_FILE="/etc/config/amlogic"
|
||||
START_LOG="${TMP_CHECK_DIR}/amlogic_check_plugin.log"
|
||||
RUNNING_LOG="${TMP_CHECK_DIR}/amlogic_running_script.log"
|
||||
LOG_FILE="${TMP_CHECK_DIR}/amlogic.log"
|
||||
support_platform=("allwinner" "rockchip" "amlogic" "qemu-aarch64")
|
||||
LOGTIME="$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
[[ -d ${TMP_CHECK_DIR} ]] || mkdir -p ${TMP_CHECK_DIR}
|
||||
|
||||
# Clean the running log
|
||||
clean_running() {
|
||||
echo -e '' >${RUNNING_LOG} 2>/dev/null && sync
|
||||
}
|
||||
|
||||
# Add log
|
||||
tolog() {
|
||||
echo -e "${1}" >${START_LOG}
|
||||
echo -e "${LOGTIME} ${1}" >>${LOG_FILE}
|
||||
[[ -n "${2}" && "${2}" -eq "1" ]] && clean_running && exit 1
|
||||
}
|
||||
|
||||
# Check running scripts, prohibit running concurrently
|
||||
this_running_log="1@Plugin update in progress, try again later!"
|
||||
running_script="$(cat ${RUNNING_LOG} 2>/dev/null | xargs)"
|
||||
if [[ -n "${running_script}" ]]; then
|
||||
run_num="$(echo "${running_script}" | awk -F "@" '{print $1}')"
|
||||
run_log="$(echo "${running_script}" | awk -F "@" '{print $2}')"
|
||||
fi
|
||||
if [[ -n "${run_log}" && "${run_num}" -ne "1" ]]; then
|
||||
echo -e "${run_log}" >${START_LOG} 2>/dev/null && sync && exit 1
|
||||
else
|
||||
echo -e "${this_running_log}" >${RUNNING_LOG} 2>/dev/null && sync
|
||||
fi
|
||||
|
||||
# Check release file
|
||||
if [[ -s "${AMLOGIC_SOC_FILE}" ]]; then
|
||||
source "${AMLOGIC_SOC_FILE}" 2>/dev/null
|
||||
PLATFORM="${PLATFORM}"
|
||||
else
|
||||
tolog "${AMLOGIC_SOC_FILE} file is missing!" "1"
|
||||
fi
|
||||
if [[ -z "${PLATFORM}" || -z "$(echo "${support_platform[@]}" | grep -w "${PLATFORM}")" ]]; then
|
||||
tolog "Missing [ PLATFORM ] value in ${AMLOGIC_SOC_FILE} file." "1"
|
||||
fi
|
||||
|
||||
tolog "PLATFORM: [ ${PLATFORM} ]"
|
||||
sleep 2
|
||||
|
||||
# Read and resolve plugin_branch from UCI config.
|
||||
# Normalise to canonical values: "main" (JS) or "lua".
|
||||
# Priority: UCI value → auto-detect from system.
|
||||
plugin_branch="$(uci get amlogic.config.amlogic_plugin_branch 2>/dev/null | tr '[:upper:]' '[:lower:]' | xargs)"
|
||||
|
||||
# Normalize aliases (js / javascript / main → "main"; anything else → "lua")
|
||||
case "${plugin_branch}" in
|
||||
js | javascript | main)
|
||||
plugin_branch="main"
|
||||
;;
|
||||
lua)
|
||||
plugin_branch="lua"
|
||||
;;
|
||||
*)
|
||||
# Empty, unknown, or missing → auto-detect from system
|
||||
if [[ -f "/www/luci-static/resources/luci.js" ]]; then
|
||||
plugin_branch="main"
|
||||
else
|
||||
plugin_branch="lua"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Safety check: "main" requires JS LuCI; fall back when absent
|
||||
if [[ "${plugin_branch}" == "main" && ! -f "/www/luci-static/resources/luci.js" ]]; then
|
||||
plugin_branch="lua"
|
||||
tolog "Warning: JS LuCI not found, falling back to lua branch."
|
||||
fi
|
||||
|
||||
# Persist the resolved value back to UCI (covers migration + branch-switch cases)
|
||||
uci set amlogic.config.amlogic_plugin_branch="${plugin_branch}" 2>/dev/null
|
||||
uci commit amlogic 2>/dev/null
|
||||
|
||||
tolog "Plugin branch: [ ${plugin_branch} ]"
|
||||
sleep 1
|
||||
get_plugin_info() {
|
||||
package_manager=""
|
||||
current_plugin_v=""
|
||||
current_plugin_release=""
|
||||
if command -v opkg >/dev/null 2>&1; then
|
||||
package_manager="ipk"
|
||||
# Full version string e.g. "3.1.295-1" or "3.1.295-2"
|
||||
local full_v
|
||||
full_v="$(opkg list-installed | grep '^luci-app-amlogic ' | awk '{print $3}')"
|
||||
current_plugin_v="$(echo "${full_v}" | cut -d'-' -f1)"
|
||||
# Strip optional "r" prefix from release (e.g. "r2" -> "2")
|
||||
current_plugin_release="$(echo "${full_v}" | cut -d'-' -f2 | sed 's/^r//')"
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
package_manager="apk"
|
||||
# Package name e.g. "luci-app-amlogic-3.1.295-r2"
|
||||
# Fields: luci(1) app(2) amlogic(3) 3.1.295(4) r2(5)
|
||||
local pkg_name
|
||||
pkg_name="$(apk list --installed | grep '^luci-app-amlogic-' | awk '{print $1}')"
|
||||
current_plugin_v="$(echo "${pkg_name}" | cut -d'-' -f4)"
|
||||
# Extract release number: "r2" -> "2"
|
||||
current_plugin_release="$(echo "${pkg_name}" | cut -d'-' -f5 | sed 's/^r//')"
|
||||
fi
|
||||
}
|
||||
|
||||
# Step 2: Check if there is the latest plugin version
|
||||
check_plugin() {
|
||||
tolog "01. Query current version information."
|
||||
get_plugin_info
|
||||
if [[ -z "${package_manager}" || -z "${current_plugin_v}" ]]; then
|
||||
tolog "01.01 Plugin 'luci-app-amlogic' not found or package manager unknown." "1"
|
||||
else
|
||||
tolog "01.01 Using [${package_manager}]. Current version: ${current_plugin_v}, Release: ${current_plugin_release:-unknown}"
|
||||
fi
|
||||
sleep 2
|
||||
|
||||
tolog "02. Start querying plugin version..."
|
||||
if [[ "${plugin_branch}" == "main" ]]; then
|
||||
# JS branch: match tags ending with -js (e.g. 3.1.305-js)
|
||||
latest_version="$(
|
||||
curl -fsSL -m 10 \
|
||||
https://github.com/ophub/luci-app-amlogic/releases |
|
||||
grep -oE 'expanded_assets/[0-9]+\.[0-9]+\.[0-9]+-js' | sed 's|expanded_assets/||g' |
|
||||
sort -urV | head -n 1
|
||||
)"
|
||||
else
|
||||
# Lua branch: match tags with digits only, no suffix (e.g. 3.1.305)
|
||||
latest_version="$(
|
||||
curl -fsSL -m 10 \
|
||||
https://github.com/ophub/luci-app-amlogic/releases |
|
||||
grep -oE 'expanded_assets/[0-9]+\.[0-9]+\.[0-9]+' | sed 's|expanded_assets/||g' |
|
||||
grep -v -- '-' |
|
||||
sort -urV | head -n 1
|
||||
)"
|
||||
fi
|
||||
if [[ -z "${latest_version}" ]]; then
|
||||
tolog "02.01 Query failed, please try again." "1"
|
||||
fi
|
||||
|
||||
tolog "02.01 Current version: ${current_plugin_v}, Latest version: ${latest_version}"
|
||||
sleep 2
|
||||
|
||||
# Strip variant suffix (e.g. "-lua") from latest_version to get the numeric part.
|
||||
latest_version_base="${latest_version%%-*}"
|
||||
|
||||
# Determine target PKG_RELEASE for the selected branch:
|
||||
# main branch -> release 2 (tag: 3.x.xxx-js)
|
||||
# lua branch -> release 1 (tag: 3.x.xxx, no suffix)
|
||||
if [[ "${plugin_branch}" == "main" ]]; then
|
||||
target_release="2"
|
||||
else
|
||||
target_release="1"
|
||||
fi
|
||||
|
||||
# Only report "already latest" when BOTH the version number AND the installed
|
||||
# branch (PKG_RELEASE) match the selected branch. If the user switched branches
|
||||
# (same version number but different release), we still offer an update.
|
||||
if [[ "${current_plugin_v}" == "${latest_version_base}" && "${current_plugin_release}" == "${target_release}" ]]; then
|
||||
tolog "02.02 Already the latest version, no need to update." "1"
|
||||
else
|
||||
tolog '<input type="button" class="cbi-button cbi-button-reload" value="Download" onclick="return b_check_plugin(this, '"'download_${latest_version}'"')"/> Latest version: '${latest_version}'' "1"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Step 3: Download the latest plugin version
|
||||
download_plugin() {
|
||||
tolog "03. Start downloading the plugin file."
|
||||
if [[ "${download_version}" == "download_"* ]]; then
|
||||
tolog "03.01 Start downloading..."
|
||||
else
|
||||
tolog "03.01 Invalid parameter." "1"
|
||||
fi
|
||||
|
||||
# Extract version from parameter (e.g. "download_3.1.290" -> "3.1.290")
|
||||
latest_version="$(echo "${download_version}" | cut -d '_' -f2-)"
|
||||
|
||||
get_plugin_info
|
||||
if [[ -z "${package_manager}" ]]; then
|
||||
tolog "03.02 Package manager not found." "1"
|
||||
fi
|
||||
|
||||
tolog "03.02 Package manager: ${package_manager}, Version to download: ${latest_version}"
|
||||
|
||||
# Clean up previous downloads
|
||||
rm -f ${TMP_CHECK_DIR}/*.ipk 2>/dev/null
|
||||
rm -f ${TMP_CHECK_DIR}/*.apk 2>/dev/null
|
||||
rm -f ${TMP_CHECK_DIR}/sha256sums 2>/dev/null
|
||||
sync
|
||||
|
||||
download_repo="https://github.com/ophub/luci-app-amlogic/releases/download"
|
||||
|
||||
# Use GitHub API to find exact filenames
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
tolog "03.03 jq not found, cannot query GitHub API." "1"
|
||||
fi
|
||||
|
||||
tolog "03.03 Querying GitHub API for release assets..."
|
||||
api_url="https://api.github.com/repos/ophub/luci-app-amlogic/releases/tags/${latest_version}"
|
||||
asset_list="$(curl -fsSL -m 15 "${api_url}" | jq -r '.assets[].name' | xargs)"
|
||||
if [[ -z "${asset_list}" ]]; then
|
||||
tolog "03.03 Failed to fetch release assets from GitHub API." "1"
|
||||
fi
|
||||
|
||||
plugin_file_name="$(echo "${asset_list}" | tr ' ' '\n' | grep -oE "^luci-app-amlogic.*${package_manager}$" | head -n 1)"
|
||||
lang_file_list=($(echo "${asset_list}" | tr ' ' '\n' | grep -oE "^luci-i18n-amlogic.*${package_manager}$"))
|
||||
|
||||
if [[ -z "${plugin_file_name}" ]]; then
|
||||
tolog "03.04 Could not find plugin file (.${package_manager}) in release assets." "1"
|
||||
fi
|
||||
|
||||
tolog "03.04 Plugin file: ${plugin_file_name}"
|
||||
|
||||
# Download the main plugin file
|
||||
plugin_full_url="${download_repo}/${latest_version}/${plugin_file_name}"
|
||||
tolog "03.05 Downloading main plugin..."
|
||||
curl -fsSL "${plugin_full_url}" -o "${TMP_CHECK_DIR}/${plugin_file_name}"
|
||||
[[ "${?}" -ne "0" ]] && tolog "03.05 Plugin [ ${plugin_file_name} ] download failed." "1"
|
||||
|
||||
# Download language packs
|
||||
for langfile in "${lang_file_list[@]}"; do
|
||||
lang_full_url="${download_repo}/${latest_version}/${langfile}"
|
||||
tolog "03.06 Downloading language pack [ ${langfile} ]..."
|
||||
curl -fsSL "${lang_full_url}" -o "${TMP_CHECK_DIR}/${langfile}"
|
||||
[[ "${?}" -ne "0" ]] && tolog "03.06 Language pack [ ${langfile} ] download failed." "1"
|
||||
done
|
||||
|
||||
# The .apk filename uses tilde (~) instead of dot before the hash suffix
|
||||
for file in ${TMP_CHECK_DIR}/*.apk; do
|
||||
[[ -f "${file}" ]] || continue
|
||||
base_name="$(basename "${file}")"
|
||||
new_name="$(echo "${base_name}" | sed -E 's/\.([a-f0-9]{7}\.apk)/~\1/')"
|
||||
if [[ "${base_name}" != "${new_name}" ]]; then
|
||||
mv -f "${file}" "${TMP_CHECK_DIR}/${new_name}" || true
|
||||
fi
|
||||
done
|
||||
|
||||
sync && sleep 2
|
||||
|
||||
tolog "03.07 The plugin is ready, you can update."
|
||||
sleep 2
|
||||
|
||||
tolog '<input type="button" class="cbi-button cbi-button-reload" value="Update" onclick="return amlogic_plugin(this)"/> Latest version: '${latest_version}'' "1"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
getopts 'cd' opts
|
||||
case "${opts}" in
|
||||
c | check)
|
||||
check_plugin
|
||||
;;
|
||||
* | download)
|
||||
download_plugin
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"admin/system/amlogic": {
|
||||
"title": "Amlogic Service",
|
||||
"order": 88,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-amlogic" ],
|
||||
"fs": { "/etc/config/amlogic": "file" }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/system/amlogic/info": {
|
||||
"title": "Amlogic Service",
|
||||
"order": 1,
|
||||
"action": { "type": "view", "path": "amlogic/info" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/install": {
|
||||
"title": "Install OpenWrt",
|
||||
"order": 2,
|
||||
"action": { "type": "view", "path": "amlogic/install" },
|
||||
"depends": {
|
||||
"uci": { "amlogic": { "config": { "menu_install": "yes" } } }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/system/amlogic/upload": {
|
||||
"title": "Manually Upload Update",
|
||||
"order": 3,
|
||||
"action": { "type": "view", "path": "amlogic/upload" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/check": {
|
||||
"title": "Online Download Update",
|
||||
"order": 4,
|
||||
"action": { "type": "view", "path": "amlogic/check" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/backup": {
|
||||
"title": "Backup Firmware Config",
|
||||
"order": 5,
|
||||
"action": { "type": "view", "path": "amlogic/backup" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/backuplist": {
|
||||
"action": { "type": "view", "path": "amlogic/backuplist" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/armcpu": {
|
||||
"title": "CPU Settings",
|
||||
"order": 6,
|
||||
"action": { "type": "view", "path": "amlogic/armcpu" },
|
||||
"depends": {
|
||||
"uci": { "amlogic": { "config": { "menu_armcpu": "yes" } } }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/system/amlogic/config": {
|
||||
"title": "Plugin Settings",
|
||||
"order": 7,
|
||||
"action": { "type": "view", "path": "amlogic/config" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/log": {
|
||||
"title": "Server Logs",
|
||||
"order": 8,
|
||||
"action": { "type": "view", "path": "amlogic/log" }
|
||||
},
|
||||
|
||||
"admin/system/amlogic/poweroff": {
|
||||
"title": "PowerOff",
|
||||
"order": 9,
|
||||
"action": { "type": "view", "path": "amlogic/poweroff" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"luci-app-amlogic": {
|
||||
"description": "Grant UCI/file/RPC access for the Amlogic Service plugin",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.amlogic": [
|
||||
"state",
|
||||
"author",
|
||||
"model_database",
|
||||
"platform_info",
|
||||
"snapshot_list",
|
||||
"check_upfiles",
|
||||
"upload_path",
|
||||
"list_uploads",
|
||||
"read_log_tail",
|
||||
"read_log_full",
|
||||
"cpu_policies"
|
||||
]
|
||||
},
|
||||
"uci": [ "amlogic" ],
|
||||
"file": {
|
||||
"/tmp/amlogic/amlogic.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_check_install.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_check_upfiles.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_check_plugin.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_check_kernel.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_check_firmware.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_check_rescue.log": [ "read" ],
|
||||
"/tmp/amlogic/amlogic_running_script.log": [ "read" ],
|
||||
"/etc/amlogic_backup_list.conf": [ "read" ],
|
||||
"/etc/flippy-openwrt-release": [ "read" ],
|
||||
"/etc/model_database.txt": [ "read" ],
|
||||
"/.reserved/openwrt_config.tar.gz": [ "read" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.amlogic": [
|
||||
"snapshot_create",
|
||||
"snapshot_delete",
|
||||
"snapshot_restore",
|
||||
"check_plugin",
|
||||
"check_kernel",
|
||||
"check_firmware",
|
||||
"start_plugin",
|
||||
"start_kernel",
|
||||
"start_update",
|
||||
"start_install",
|
||||
"start_rescue",
|
||||
"del_log",
|
||||
"poweroff",
|
||||
"kvm_switch",
|
||||
"delete_upload",
|
||||
"install_upload",
|
||||
"upload_chunk",
|
||||
"backup_create",
|
||||
"save_backup_list",
|
||||
"prime_backup_list",
|
||||
"reload_cpu",
|
||||
"sync_menu"
|
||||
]
|
||||
},
|
||||
"uci": [ "amlogic" ],
|
||||
"file": {
|
||||
"/etc/amlogic_backup_list.conf": [ "write" ],
|
||||
"/tmp/upload/*": [ "write" ],
|
||||
"/tmp/amlogic/*": [ "write" ],
|
||||
"/mnt/mmcblk*p4/*": [ "write" ],
|
||||
"/mnt/sd*4/*": [ "write" ],
|
||||
"/mnt/hd*4/*": [ "write" ],
|
||||
"/mnt/vd*4/*": [ "write" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,785 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// luci.amlogic — ucode RPC bridge for luci-app-amlogic (JS branch).
|
||||
//
|
||||
// Purpose: expose all plugin operations (install, update, backup, snapshot,
|
||||
// CPU policy, log read, upload) as ubus methods consumed by the JS views.
|
||||
// Each method is registered in the `methods` table at the bottom of this file.
|
||||
'use strict';
|
||||
|
||||
import { popen, readfile, writefile, mkstemp, stat, glob, error as fserror,
|
||||
unlink, mkdir, access, open as fsopen } from 'fs';
|
||||
|
||||
const LOG_DIR = '/tmp/amlogic';
|
||||
const LOG_FILE = LOG_DIR + '/amlogic.log';
|
||||
const LOG_RUNNING = LOG_DIR + '/amlogic_running_script.log';
|
||||
const LOG_INSTALL = LOG_DIR + '/amlogic_check_install.log';
|
||||
const LOG_UPFILES = LOG_DIR + '/amlogic_check_upfiles.log';
|
||||
const LOG_PLUGIN = LOG_DIR + '/amlogic_check_plugin.log';
|
||||
const LOG_KERNEL = LOG_DIR + '/amlogic_check_kernel.log';
|
||||
const LOG_FIRMWARE = LOG_DIR + '/amlogic_check_firmware.log';
|
||||
const LOG_RESCUE = LOG_DIR + '/amlogic_check_rescue.log';
|
||||
const UPLOAD_TMP = '/tmp/upload';
|
||||
const PKG_NAME = 'luci-app-amlogic';
|
||||
const BACKUP_LIST = '/etc/amlogic_backup_list.conf';
|
||||
|
||||
// Map of safe log names -> file paths (for read_log_tail/read_log_full)
|
||||
const LOG_MAP = {
|
||||
'main': LOG_FILE,
|
||||
'install': LOG_INSTALL,
|
||||
'upfiles': LOG_UPFILES,
|
||||
'plugin': LOG_PLUGIN,
|
||||
'kernel': LOG_KERNEL,
|
||||
'firmware': LOG_FIRMWARE,
|
||||
'rescue': LOG_RESCUE,
|
||||
'running': LOG_RUNNING
|
||||
};
|
||||
|
||||
// Run a shell command, return trimmed stdout
|
||||
function sh(cmd) {
|
||||
const p = popen(cmd, 'r');
|
||||
if (!p) return '';
|
||||
const out = p.read('all') ?? '';
|
||||
p.close();
|
||||
return trim(out);
|
||||
}
|
||||
|
||||
// Run a shell command, return exit code (0 on success)
|
||||
function shcall(cmd) {
|
||||
const out = sh('(' + cmd + ') >/dev/null 2>&1; echo $?');
|
||||
const lines = split(out, '\n');
|
||||
const last = lines[length(lines) - 1] ?? '1';
|
||||
return int(last);
|
||||
}
|
||||
|
||||
// Run cmd in the background; close stdin so shbg does not block rpcd.
|
||||
function shbg(cmd) {
|
||||
const wrapper = '(' + cmd + ') </dev/null &';
|
||||
const p = popen(wrapper, 'r');
|
||||
if (p) p.close();
|
||||
}
|
||||
|
||||
// Shell-quote a string for embedding into a /bin/sh command line
|
||||
function shq(s) {
|
||||
return "'" + replace(s ?? '', "'", "'\\''") + "'";
|
||||
}
|
||||
|
||||
// Ensure runtime tmp dirs exist.
|
||||
function ensure_dirs() {
|
||||
mkdir(LOG_DIR);
|
||||
mkdir(UPLOAD_TMP);
|
||||
}
|
||||
|
||||
// Read flippy-openwrt-release platform identifier
|
||||
function flippy_var(name) {
|
||||
return sh("cat /etc/flippy-openwrt-release 2>/dev/null | grep " + shq(name) +
|
||||
" | awk -F= '{print $2}' | tr -d '\"' | xargs");
|
||||
}
|
||||
|
||||
function platform() {
|
||||
const v = flippy_var('PLATFORM');
|
||||
const m = match(v, /(amlogic|rockchip|allwinner|qemu)/);
|
||||
return m ? m[1] : 'Unknown';
|
||||
}
|
||||
|
||||
function show_install_menu() {
|
||||
const v = flippy_var('SHOW_INSTALL_MENU');
|
||||
const m = match(v, /(yes|no)/);
|
||||
return m ? m[1] : 'Unknown';
|
||||
}
|
||||
|
||||
// Detect whether the root fs is on internal storage (eMMC/NVMe/fixed disk).
|
||||
// Returns true if already installed; sd* removable flag distinguishes USB vs SATA.
|
||||
function root_on_internal_storage() {
|
||||
const root_pt = trim(sh("df / | tail -n1 | awk '{print $1}' | awk -F '/' '{print $3}'"));
|
||||
if (!root_pt) return false;
|
||||
|
||||
// Only hide the install menu when root is already on eMMC (device/type == "MMC").
|
||||
// All other boot media (TF/SD, NVMe, SATA, USB) should still show the install menu.
|
||||
if (match(root_pt, /^mmcblk[0-9]+p[0-9]+$/)) {
|
||||
const base_dev = replace(root_pt, /p[0-9]+$/, ''); // e.g. mmcblk1p2 → mmcblk1
|
||||
const mmc_type = trim(sh('cat /sys/block/' + shq(base_dev) + '/device/type 2>/dev/null'));
|
||||
return (mmc_type == 'MMC');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return platform-specific helper script names for install/update/kernel operations.
|
||||
function platform_scripts() {
|
||||
const p = platform();
|
||||
if (index(p, 'rockchip') >= 0)
|
||||
return { install: '', update: 'openwrt-update-rockchip', kernel: 'openwrt-kernel' };
|
||||
if (index(p, 'allwinner') >= 0)
|
||||
return { install: 'openwrt-install-allwinner', update: 'openwrt-update-allwinner', kernel: 'openwrt-kernel' };
|
||||
if (index(p, 'qemu') >= 0)
|
||||
return { install: '', update: 'openwrt-update-kvm', kernel: 'openwrt-kernel' };
|
||||
return { install: 'openwrt-install-amlogic', update: 'openwrt-update-amlogic', kernel: 'openwrt-kernel' };
|
||||
}
|
||||
|
||||
// Read an option from the [config] section of /etc/config/amlogic
|
||||
function uci_cfg(opt) {
|
||||
return sh('uci -q get amlogic.config.' + opt);
|
||||
}
|
||||
|
||||
// --- Version helpers (identical logic to controller's *_version() funcs) ---
|
||||
function current_firmware_version() {
|
||||
const v = sh("uname -r 2>/dev/null | grep -oE '^[1-9]\\.[0-9]{1,3}\\.[0-9]+'");
|
||||
return v != '' ? v : 'Invalid value.';
|
||||
}
|
||||
function current_kernel_version() { return current_firmware_version(); }
|
||||
function current_kernel_branch() {
|
||||
const def = sh("uname -r 2>/dev/null | grep -oE '^[1-9]\\.[0-9]{1,3}'");
|
||||
const cfg = sh("uci -q get amlogic.config.amlogic_kernel_branch | grep -oE '^[1-9]\\.[0-9]{1,3}'");
|
||||
return cfg != '' ? cfg : def;
|
||||
}
|
||||
function current_plugin_version() {
|
||||
let v = '';
|
||||
if (shcall('command -v opkg') == 0)
|
||||
v = sh("opkg list-installed | grep '^" + PKG_NAME + "' | awk '{print $3}' | cut -d'-' -f1");
|
||||
if (v == '' && shcall('command -v apk') == 0)
|
||||
v = sh("apk list --installed 2>/dev/null | grep '^" + PKG_NAME + "' | awk '{print $1}' | cut -d'-' -f4");
|
||||
return v != '' ? v : 'Invalid value.';
|
||||
}
|
||||
|
||||
// --- Config-driven flags used when invoking helper scripts ---
|
||||
function update_restore_config() {
|
||||
const v = uci_cfg('amlogic_firmware_config');
|
||||
return (v == '' || int(v) != 0) ? 'restore' : 'no-restore';
|
||||
}
|
||||
function auto_write_bootloader() {
|
||||
const v = uci_cfg('amlogic_write_bootloader');
|
||||
return (v == '' || int(v) != 0) ? 'yes' : 'no';
|
||||
}
|
||||
function auto_shared_fstype() {
|
||||
const v = trim(uci_cfg('amlogic_shared_fstype'));
|
||||
return v != '' ? v : 'ext4';
|
||||
}
|
||||
|
||||
// Read tail / full content of a known log file
|
||||
function _safe_log_path(name) {
|
||||
return LOG_MAP[name] ?? null;
|
||||
}
|
||||
function read_log_tail(name) {
|
||||
const p = _safe_log_path(name);
|
||||
if (!p) return '';
|
||||
const pipe = popen('cat ' + p + ' 2>/dev/null | tail -c 4096', 'r');
|
||||
if (!pipe) return '';
|
||||
const content = pipe.read('all') ?? '';
|
||||
pipe.close();
|
||||
if (!content) return '';
|
||||
// Find the last non-empty line
|
||||
const lines = split(content, '\n');
|
||||
for (let i = length(lines) - 1; i >= 0; i--) {
|
||||
const line = trim(lines[i]);
|
||||
if (line !== '') return line;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function read_log_full(name) {
|
||||
const p = _safe_log_path(name ?? 'main');
|
||||
if (!p) return '';
|
||||
// Use popen+cat to bypass rpcd process-level file cache.
|
||||
const pipe = popen('cat ' + p + ' 2>/dev/null', 'r');
|
||||
if (!pipe) return '';
|
||||
const t = pipe.read('all') ?? '';
|
||||
pipe.close();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Lazily seed the main log on first read (matches the Lua action_refresh_log
|
||||
// behaviour: when the log is missing, populate it with `uname -a`).
|
||||
function ensure_main_log() {
|
||||
ensure_dirs();
|
||||
if (!access(LOG_FILE)) {
|
||||
shcall("uname -a > " + LOG_FILE);
|
||||
let _logs1 = [LOG_INSTALL, LOG_UPFILES, LOG_PLUGIN, LOG_KERNEL,
|
||||
LOG_FIRMWARE, LOG_RESCUE, LOG_RUNNING];
|
||||
for (let i = 0; i < length(_logs1); i++)
|
||||
writefile(_logs1[i], '');
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: clear (truncate) a file
|
||||
function clear(path) { writefile(path, ''); }
|
||||
|
||||
// Cast/sanitise an `amlogic_install_sel` token: form is
|
||||
// "<soc_id>@<dtb>:<socname>:<ubootname>" (from old install view)
|
||||
// The Lua original passed res[2] verbatim (colon-string) to the install
|
||||
// script as the dtb_filename argument when soc_id == 99.
|
||||
function parse_install_sel(s) {
|
||||
const parts = split(s ?? '', '@');
|
||||
const soc_id = (parts[0] ?? '99');
|
||||
let dtb = 'auto_dtb';
|
||||
if (int(parts[0]) == 99 && length(parts) > 1 && parts[1] != '')
|
||||
dtb = parts[1];
|
||||
return { soc_id, dtb };
|
||||
}
|
||||
|
||||
// Helper: parse `amlogic_update_sel` form
|
||||
// "<firmware_name>@<updated>@<write_path>"
|
||||
function parse_update_sel(s) {
|
||||
const a = split(s ?? '', '@');
|
||||
return {
|
||||
name: a[0] ?? 'auto',
|
||||
updated: a[1] ?? 'updated',
|
||||
path: a[2] ?? '/tmp'
|
||||
};
|
||||
}
|
||||
|
||||
// === ubus method bodies ===
|
||||
|
||||
function m_state() {
|
||||
return {
|
||||
current_firmware_version: current_firmware_version(),
|
||||
current_plugin_version: current_plugin_version(),
|
||||
current_kernel_version: current_kernel_version(),
|
||||
current_kernel_branch: current_kernel_branch(),
|
||||
kernel_release: sh('uname -r'),
|
||||
has_luci_js: access('/www/luci-static/resources/luci.js') ? true : false,
|
||||
plugin_branch: access('/usr/lib/lua/luci/controller/amlogic.lua') ? 'lua' : 'main'
|
||||
};
|
||||
}
|
||||
|
||||
function m_author() {
|
||||
const r = uci_cfg('amlogic_firmware_repo');
|
||||
return { openwrt_author: r != '' ? r : 'Invalid value.' };
|
||||
}
|
||||
|
||||
function m_platform_info() {
|
||||
const p = platform();
|
||||
const m = show_install_menu();
|
||||
const has_kvm = access('/boot/efi/EFI/BOOT/grub.cfg.prev') ? true : false;
|
||||
// can_install: amlogic or allwinner have an install script; also forced by SHOW_INSTALL_MENU=yes
|
||||
const can_install = (index(p, 'amlogic') >= 0 || index(p, 'allwinner') >= 0 || index(m, 'yes') >= 0);
|
||||
// can_armcpu: all platforms except qemu support CPU frequency settings
|
||||
const can_armcpu = (index(p, 'qemu') < 0);
|
||||
// is_installed: root fs on internal storage means already installed; hide Install menu.
|
||||
const is_installed = root_on_internal_storage();
|
||||
return {
|
||||
platform: p,
|
||||
install_menu: m,
|
||||
can_install,
|
||||
can_armcpu,
|
||||
has_kvm,
|
||||
is_installed: is_installed ? true : false
|
||||
};
|
||||
}
|
||||
|
||||
function m_model_database() {
|
||||
const p = platform();
|
||||
let pat;
|
||||
if (index(p, 'allwinner') >= 0)
|
||||
pat = "^w[0-9]{1,9}.*:";
|
||||
else
|
||||
pat = "^[0-9]{1,9}.*:";
|
||||
const out = sh("cat /etc/model_database.txt 2>/dev/null | grep -E " + shq(pat) +
|
||||
" | awk -F ':' '{print $1\"###\"$2}' | tr ' ' '~'");
|
||||
const entries = [];
|
||||
for (let line in split(out, '\n')) {
|
||||
const t = trim(line);
|
||||
if (t == '') continue;
|
||||
const kv = split(t, '###');
|
||||
if (length(kv) >= 2)
|
||||
push(entries, { id: trim(kv[0]), name: replace(trim(kv[1]), '~', ' ') });
|
||||
}
|
||||
return { entries };
|
||||
}
|
||||
|
||||
function m_snapshot_list() {
|
||||
const out = sh("btrfs subvolume list -rt / 2>/dev/null | awk '{print $4}' | grep .snapshots");
|
||||
const names = [];
|
||||
for (let l in split(out, '\n')) {
|
||||
const n = replace(trim(l), '.snapshots/', '');
|
||||
if (n != '') push(names, n);
|
||||
}
|
||||
return { names };
|
||||
}
|
||||
|
||||
// Seed /etc/amlogic_backup_list.conf from the default BACKUP_LIST in openwrt-backup if absent.
|
||||
function m_prime_backup_list() {
|
||||
shcall("[ -s " + BACKUP_LIST + " ] || sed -n \"/BACKUP_LIST='/,/.*'$/p\" /usr/sbin/openwrt-backup | sed -e \"s/BACKUP_LIST=\\(.*\\)/\\1/; s/'//g; s/\\\\\\\\//g; s/ //g\" > " + BACKUP_LIST + " 2>/dev/null");
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
// Create a read-only btrfs snapshot of /etc into /.snapshots/.
|
||||
function m_snapshot_create() {
|
||||
shcall("btrfs subvolume snapshot -r /etc /.snapshots/etc-$(date +%m.%d.%H%M%S) && sync");
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_snapshot_delete(req) {
|
||||
const n = req.args?.name ?? '';
|
||||
if (!match(n, /^[A-Za-z0-9._-]+$/))
|
||||
return { code: 1, error: 'invalid name' };
|
||||
const rc = shcall("btrfs subvolume delete -c /.snapshots/etc-" + n + " && sync");
|
||||
return { code: rc };
|
||||
}
|
||||
|
||||
function m_snapshot_restore(req) {
|
||||
const n = req.args?.name ?? '';
|
||||
if (!match(n, /^[A-Za-z0-9._-]+$/))
|
||||
return { error: 'invalid name' };
|
||||
shcall("btrfs subvolume snapshot /.snapshots/etc-" + n + " /etc && sync");
|
||||
// trigger a reboot like the Lua version (echo b > /proc/sysrq-trigger)
|
||||
shcall("(sleep 1 && echo b > /proc/sysrq-trigger) &");
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_check_plugin(req) {
|
||||
ensure_dirs();
|
||||
const opt = req.args?.options ?? 'check';
|
||||
writefile(LOG_PLUGIN, '');
|
||||
shcall('chmod +x /usr/share/amlogic/amlogic_check_plugin.sh');
|
||||
if (opt === 'check') {
|
||||
shbg('/usr/share/amlogic/amlogic_check_plugin.sh -check');
|
||||
return { code: 0 };
|
||||
}
|
||||
// download option: e.g. "download_3.1.290" — pass verbatim to the script
|
||||
shbg('/usr/share/amlogic/amlogic_check_plugin.sh -download ' + shq(opt));
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_check_kernel(req) {
|
||||
ensure_dirs();
|
||||
const opt = req.args?.options ?? 'check';
|
||||
writefile(LOG_KERNEL, '');
|
||||
shcall('chmod +x /usr/share/amlogic/amlogic_check_kernel.sh');
|
||||
if (opt == 'check') {
|
||||
shbg('/usr/share/amlogic/amlogic_check_kernel.sh -check');
|
||||
return { code: 0 };
|
||||
}
|
||||
// download option: pass the full param string verbatim to the script.
|
||||
shbg('/usr/share/amlogic/amlogic_check_kernel.sh -download ' + shq(opt));
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_check_firmware(req) {
|
||||
ensure_dirs();
|
||||
const opt = req.args?.options ?? 'check';
|
||||
writefile(LOG_FIRMWARE, '');
|
||||
shcall('chmod +x /usr/share/amlogic/amlogic_check_firmware.sh');
|
||||
if (opt == 'check') {
|
||||
shbg('/usr/share/amlogic/amlogic_check_firmware.sh -check');
|
||||
return { code: 0 };
|
||||
}
|
||||
// download option: may contain @, /, dots, etc. — pass via shq
|
||||
shbg('/usr/share/amlogic/amlogic_check_firmware.sh -download ' + shq(opt));
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_start_plugin() {
|
||||
ensure_dirs();
|
||||
const cfg = '/etc/config/amlogic';
|
||||
const cfg_bak = '/etc/config/amlogic_bak';
|
||||
const script_path = LOG_DIR + '/amlogic_install_plugin.sh';
|
||||
writefile(LOG_RUNNING, '1@Plugin update in progress, try again later!\n');
|
||||
writefile(LOG_PLUGIN, '');
|
||||
// Write install script to a temp file; shbg closes stdin so heredoc cannot be used.
|
||||
const script = '#!/bin/sh\n'
|
||||
+ 'LOG=' + shq(LOG_PLUGIN) + '\n'
|
||||
+ 'LOG_RUNNING=' + shq(LOG_RUNNING) + '\n'
|
||||
+ 'cfg=' + shq(cfg) + '\n'
|
||||
+ 'cfg_bak=' + shq(cfg_bak) + '\n'
|
||||
+ '[ -f "$cfg" ] && cp -f "$cfg" "$cfg_bak"\n'
|
||||
+ '# Detect new release from downloaded file name (r1=lua, r2=js)\n'
|
||||
+ 'new_release=""\n'
|
||||
+ 'cur_release=""\n'
|
||||
+ 'if command -v opkg >/dev/null 2>&1; then\n'
|
||||
+ ' _ipk="$(ls /tmp/amlogic/luci-app-amlogic_*.ipk 2>/dev/null | head -n 1)"\n'
|
||||
+ ' new_release="$(echo "${_ipk}" | grep -oE \'\\-r[0-9]+_\' | grep -oE \'[0-9]+\')"\n'
|
||||
+ ' _raw="$(opkg list-installed 2>/dev/null | grep \'^luci-app-amlogic \' | awk \'{print $3}\' | cut -d\'-\' -f2)"\n'
|
||||
+ ' cur_release="${_raw#r}"\n'
|
||||
+ 'elif command -v apk >/dev/null 2>&1; then\n'
|
||||
+ ' _apk="$(ls /tmp/amlogic/luci-app-amlogic_*.apk 2>/dev/null | head -n 1)"\n'
|
||||
+ ' new_release="$(echo "${_apk}" | grep -oE \'\\-r[0-9]+[-~]\' | grep -oE \'[0-9]+\')"\n'
|
||||
+ ' cur_release="$(apk list --installed 2>/dev/null | grep \'^luci-app-amlogic-\' | awk \'{print $1}\' | cut -d\'-\' -f5 | sed \'s/^r//\')"\n'
|
||||
+ 'fi\n'
|
||||
+ '# When switching branches (r1<->r2), remove old package first so that\n'
|
||||
+ '# opkg/apk registers all new files. plain remove does NOT touch dep packages.\n'
|
||||
+ 'if [ -n "$cur_release" ] && [ -n "$new_release" ] && [ "$cur_release" != "$new_release" ]; then\n'
|
||||
+ ' echo "Branch switch detected (r${cur_release} -> r${new_release}), removing old package first..." >> "$LOG"\n'
|
||||
+ ' if command -v opkg >/dev/null 2>&1; then\n'
|
||||
+ ' opkg remove luci-app-amlogic --force-depends >> "$LOG" 2>&1 || true\n'
|
||||
+ ' elif command -v apk >/dev/null 2>&1; then\n'
|
||||
+ ' apk del luci-app-amlogic >> "$LOG" 2>&1 || true\n'
|
||||
+ ' fi\n'
|
||||
+ ' echo "Old package removed (dependencies kept)." >> "$LOG"\n'
|
||||
+ 'fi\n'
|
||||
+ 'if command -v opkg >/dev/null 2>&1; then\n'
|
||||
+ ' echo "System uses opkg." >> "$LOG"\n'
|
||||
+ ' opkg --force-reinstall --force-downgrade install /tmp/amlogic/*.ipk >> "$LOG" 2>&1\n'
|
||||
+ ' rc=$?\n'
|
||||
+ 'elif command -v apk >/dev/null 2>&1; then\n'
|
||||
+ ' echo "System uses apk." >> "$LOG"\n'
|
||||
+ ' apk add --force-overwrite --allow-untrusted /tmp/amlogic/*.apk >> "$LOG" 2>&1\n'
|
||||
+ ' rc=$?\n'
|
||||
+ 'else\n'
|
||||
+ ' echo "Error: No package manager." >> "$LOG"\n'
|
||||
+ ' rc=1\n'
|
||||
+ 'fi\n'
|
||||
+ 'if [ "$rc" = "0" ]; then\n'
|
||||
+ ' [ -f "$cfg_bak" ] && cp -f "$cfg_bak" "$cfg"\n'
|
||||
+ ' rm -f /etc/config/amlogic.apk-new /etc/config/amlogic.ipk-old /etc/config/amlogic-opkg\n'
|
||||
+ ' rm -rf /tmp/luci-indexcache /tmp/luci-modulecache/* "$cfg_bak"\n'
|
||||
+ ' echo "" > "$LOG_RUNNING"\n'
|
||||
+ ' echo "Successful Update" > "$LOG"\n'
|
||||
+ ' # Cross-branch cleanup: run in background after a delay so the browser\n'
|
||||
+ ' # can poll "Successful Update" before any files are removed.\n'
|
||||
+ ' (sleep 3\n'
|
||||
+ ' new_release=""\n'
|
||||
+ ' if command -v opkg >/dev/null 2>&1; then\n'
|
||||
+ ' _raw="$(opkg list-installed | grep \'^luci-app-amlogic \' | awk \'{print $3}\' | cut -d\'-\' -f2)"\n'
|
||||
+ ' new_release="${_raw#r}"\n'
|
||||
+ ' elif command -v apk >/dev/null 2>&1; then\n'
|
||||
+ ' new_release="$(apk list --installed 2>/dev/null | grep \'^luci-app-amlogic-\' | awk \'{print $1}\' | cut -d\'-\' -f5 | sed \'s/^r//\')"\n'
|
||||
+ ' fi\n'
|
||||
+ ' if [ "${new_release}" = "2" ]; then\n'
|
||||
+ ' rm -f /usr/lib/lua/luci/controller/amlogic.lua\n'
|
||||
+ ' rm -rf /usr/lib/lua/luci/model/cbi/amlogic\n'
|
||||
+ ' rm -rf /usr/lib/lua/luci/view/amlogic\n'
|
||||
+ ' elif [ "${new_release}" = "1" ]; then\n'
|
||||
+ ' rm -f /usr/share/rpcd/ucode/luci.amlogic\n'
|
||||
+ ' rm -f /www/luci-static/resources/view/amlogic/*.js\n'
|
||||
+ ' rm -f /usr/share/luci/menu.d/luci-app-amlogic.json\n'
|
||||
+ ' fi\n'
|
||||
+ ' # Auto-set plugin branch in UCI config if not already configured\n'
|
||||
+ ' _cur_branch="$(uci -q get amlogic.config.amlogic_plugin_branch 2>/dev/null)"\n'
|
||||
+ ' if [ -z "${_cur_branch}" ]; then\n'
|
||||
+ ' if [ -f "/www/luci-static/resources/luci.js" ]; then\n'
|
||||
+ ' uci set amlogic.config.amlogic_plugin_branch=\'main\' 2>/dev/null\n'
|
||||
+ ' else\n'
|
||||
+ ' uci set amlogic.config.amlogic_plugin_branch=\'lua\' 2>/dev/null\n'
|
||||
+ ' fi\n'
|
||||
+ ' uci commit amlogic 2>/dev/null\n'
|
||||
+ ' fi\n'
|
||||
+ ' # Restart services so the newly installed files take effect immediately,\n'
|
||||
+ ' # avoiding the need for users to manually clear browser cache.\n'
|
||||
+ ' /etc/init.d/rpcd restart >> "$LOG" 2>&1\n'
|
||||
+ ' /etc/init.d/uhttpd restart >> "$LOG" 2>&1\n'
|
||||
+ ' ) &\n'
|
||||
+ 'else\n'
|
||||
+ ' echo "--- INSTALLATION FAILED! ---" >> "$LOG"\n'
|
||||
+ ' [ -f "$cfg_bak" ] && cp -f "$cfg_bak" "$cfg"\n'
|
||||
+ ' rm -f "$cfg_bak"\n'
|
||||
+ ' echo "" > "$LOG_RUNNING"\n'
|
||||
+ 'fi\n';
|
||||
writefile(script_path, script);
|
||||
shbg('sh ' + shq(script_path));
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_start_kernel() {
|
||||
ensure_dirs();
|
||||
const sc = platform_scripts();
|
||||
writefile(LOG_RUNNING, '2@Kernel update in progress, try again later!\n');
|
||||
shcall('chmod +x /usr/sbin/' + sc.kernel);
|
||||
shbg('/usr/sbin/' + sc.kernel + ' ' + auto_write_bootloader() + ' > ' + LOG_KERNEL + ' 2>&1 && sync && echo "" > ' + LOG_RUNNING);
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_start_update(req) {
|
||||
ensure_dirs();
|
||||
const sc = platform_scripts();
|
||||
writefile(LOG_RUNNING, '3@OpenWrt update in progress, try again later!\n');
|
||||
shcall('chmod +x /usr/sbin/' + sc.update);
|
||||
const u = parse_update_sel(req.args?.amlogic_update_sel);
|
||||
shcall('mkdir -p ' + shq(u.path + '/.luci-app-amlogic'));
|
||||
shcall('echo ' + shq(u.updated) + ' > ' + shq(u.path + '/.luci-app-amlogic/op_release_code') + ' && sync');
|
||||
shbg('/usr/sbin/' + sc.update + ' ' + shq(u.name) + ' ' +
|
||||
auto_write_bootloader() + ' ' + update_restore_config() +
|
||||
' > ' + LOG_FIRMWARE + ' 2>&1 && sync && echo "" > ' + LOG_RUNNING);
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_start_install(req) {
|
||||
ensure_dirs();
|
||||
const sc = platform_scripts();
|
||||
if (sc.install == '') return { error: 'install not supported on platform' };
|
||||
shcall('chmod +x /usr/sbin/' + sc.install);
|
||||
const i = parse_install_sel(req.args?.amlogic_install_sel);
|
||||
if (!match(i.soc_id, /^[0-9]+$/) || !match(i.dtb, /^[A-Za-z0-9._:-]+$/))
|
||||
return { error: 'invalid install sel' };
|
||||
writefile(LOG_INSTALL, '');
|
||||
shbg('/usr/sbin/' + sc.install + ' ' + auto_write_bootloader() + ' ' +
|
||||
i.soc_id + ' ' + i.dtb + ' ' + auto_shared_fstype() +
|
||||
' > ' + LOG_INSTALL + ' 2>&1 && sync');
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_start_rescue() {
|
||||
ensure_dirs();
|
||||
const sc = platform_scripts();
|
||||
writefile(LOG_RUNNING, '4@Kernel rescue in progress, try again later!\n');
|
||||
shcall('chmod +x /usr/sbin/' + sc.kernel);
|
||||
shbg('/usr/sbin/' + sc.kernel + ' -s > ' + LOG_RESCUE + ' 2>&1 && sync && echo "" > ' + LOG_RUNNING);
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_read_log_tail(req) {
|
||||
return { line: read_log_tail(req.args?.name ?? '') };
|
||||
}
|
||||
|
||||
function m_read_log_full(req) {
|
||||
ensure_main_log();
|
||||
return { content: read_log_full(req.args?.name ?? 'main') };
|
||||
}
|
||||
|
||||
function m_del_log() {
|
||||
let _logs2 = [LOG_FILE, LOG_INSTALL, LOG_UPFILES, LOG_PLUGIN, LOG_KERNEL,
|
||||
LOG_FIRMWARE, LOG_RESCUE, LOG_RUNNING];
|
||||
for (let i = 0; i < length(_logs2); i++)
|
||||
writefile(_logs2[i], '');
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_poweroff() {
|
||||
shcall('(sleep 1 && /sbin/poweroff) &');
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_kvm_switch() {
|
||||
shcall('[ -f /boot/efi/EFI/BOOT/grub.cfg.prev ] && (cd /boot/efi/EFI/BOOT/ && ' +
|
||||
'mv -f grub.cfg grub.cfg.bak && mv -f grub.cfg.prev grub.cfg && ' +
|
||||
'mv -f grub.cfg.bak grub.cfg.prev)');
|
||||
shcall('(sync && sleep 1 && reboot) &');
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
function m_check_upfiles() {
|
||||
const t = readfile(LOG_UPFILES) ?? '';
|
||||
return { value: trim(t) };
|
||||
}
|
||||
|
||||
// Pick upload directory using exactly the same heuristic as the Lua version
|
||||
function compute_upload_path() {
|
||||
const root_pt = sh("df / | tail -n1 | awk '{print $1}' | awk -F '/' '{print $3}'");
|
||||
if (root_pt != '' && match(root_pt, /^mmcblk[0-4]p[1-4]$/)) {
|
||||
const emmc = sh('echo ' + shq(root_pt) + " | awk '{print substr($1,1,length($1)-2)}'");
|
||||
return '/mnt/' + emmc + 'p4/';
|
||||
}
|
||||
if (root_pt != '' && match(root_pt, /^[hsv]d[a-z][0-9]+$/)) {
|
||||
const emmc = sh('echo ' + shq(root_pt) + " | awk '{print substr($1,1,length($1)-1)}'");
|
||||
return '/mnt/' + emmc + '4/';
|
||||
}
|
||||
return '/tmp/upload/';
|
||||
}
|
||||
|
||||
function m_upload_path() {
|
||||
const p = compute_upload_path();
|
||||
mkdir(p);
|
||||
return { path: p };
|
||||
}
|
||||
|
||||
function _human_size(n) {
|
||||
const units = [' kB', ' MB', ' GB', ' TB'];
|
||||
let i = -1;
|
||||
let s = +n;
|
||||
while (s > 1024 && i < length(units) - 1) {
|
||||
s = s / 1024;
|
||||
i++;
|
||||
}
|
||||
if (i < 0) { s = n / 1024; i = 0; }
|
||||
return sprintf('%.1f', s) + units[i];
|
||||
}
|
||||
|
||||
function m_list_uploads() {
|
||||
const dir = compute_upload_path();
|
||||
mkdir(dir);
|
||||
const items = [];
|
||||
let firmware = false, boot = false, dtb = false, modules = false, cfg = false;
|
||||
const files = glob(dir + '*') ?? [];
|
||||
for (let f in files) {
|
||||
const st = stat(f);
|
||||
if (!st || st.type != 'file') continue;
|
||||
const base = replace(f, /^.*\//, '');
|
||||
const lcname = lc(base);
|
||||
if (match(lcname, /\.img\.gz$/) || match(lcname, /\.img\.xz$/) ||
|
||||
match(lcname, /\.7z$/) || match(lcname, /\.img$/))
|
||||
firmware = true;
|
||||
if (match(lcname, /^boot-/)) boot = true;
|
||||
if (match(lcname, /^dtb-/)) dtb = true;
|
||||
if (match(lcname, /^modules-/)) modules = true;
|
||||
if (lcname == 'openwrt_config.tar.gz') cfg = true;
|
||||
push(items, {
|
||||
name: base,
|
||||
mtime: sprintf('%s', sh('date -d @' + st.mtime + ' +"%Y-%m-%d %H:%M:%S"')),
|
||||
modestr: st.modestr ?? '',
|
||||
size: _human_size(st.size),
|
||||
ipk: match(lcname, /\.(ipk|apk)$/) ? true : false,
|
||||
cfg: (lcname == 'openwrt_config.tar.gz')
|
||||
});
|
||||
}
|
||||
// Match the Lua page side-effect: write the upfiles status flag.
|
||||
ensure_dirs();
|
||||
if (firmware) writefile(LOG_UPFILES, 'firmware\n');
|
||||
else if (boot && dtb && modules) writefile(LOG_UPFILES, 'kernel\n');
|
||||
else writefile(LOG_UPFILES, '');
|
||||
return { path: dir, items, has_firmware: firmware,
|
||||
has_kernel: (boot && dtb && modules),
|
||||
has_config: cfg };
|
||||
}
|
||||
|
||||
// Strip directory prefix; reject empty, dot/dotdot, or paths still containing separators.
|
||||
function _safe_basename(s) {
|
||||
const b = replace(s ?? '', /^.*[\\\/]/, '');
|
||||
if (b == '' || b == '.' || b == '..') return null;
|
||||
if (index(b, '/') >= 0 || index(b, '\\') >= 0) return null;
|
||||
return b;
|
||||
}
|
||||
|
||||
// Receive one base64-encoded chunk and write (or append) it to the upload file.
|
||||
// First call (append=false) truncates/creates; subsequent calls append.
|
||||
function m_upload_chunk(req) {
|
||||
const dir = compute_upload_path();
|
||||
mkdir(dir);
|
||||
const b = _safe_basename(req.args?.name);
|
||||
if (!b) return { ok: false, error: 'invalid name: ' + (req.args?.name ?? '') };
|
||||
const full = dir + b;
|
||||
const data = b64dec(req.args?.data ?? '');
|
||||
if (data === null) return { ok: false, error: 'invalid data' };
|
||||
const append = !!req.args?.append;
|
||||
const fp = fsopen(full, append ? 'a' : 'w');
|
||||
if (!fp) return { ok: false, error: 'open failed: ' + (fserror() ?? '') };
|
||||
const n = fp.write(data);
|
||||
fp.close();
|
||||
if (n === null) return { ok: false, error: 'write failed: ' + (fserror() ?? '') };
|
||||
return { ok: true, written: n };
|
||||
}
|
||||
|
||||
function m_delete_upload(req) {
|
||||
const dir = compute_upload_path();
|
||||
const b = _safe_basename(req.args?.name);
|
||||
if (!b) return { error: 'invalid name' };
|
||||
return { ok: (unlink(dir + b) ? true : false) };
|
||||
}
|
||||
|
||||
function m_install_upload(req) {
|
||||
const dir = compute_upload_path();
|
||||
const b = _safe_basename(req.args?.name);
|
||||
if (!b) return { error: 'invalid name' };
|
||||
const full = dir + b;
|
||||
const lc_b = lc(b);
|
||||
if (lc_b == 'openwrt_config.tar.gz') {
|
||||
shcall('chmod +x /usr/sbin/openwrt-backup');
|
||||
// Use shbg so the restore+reboot does not block the rpcd thread.
|
||||
shbg('/usr/sbin/openwrt-backup -r > ' + LOG_FILE + ' 2>&1 && sync');
|
||||
return { code: 0, kind: 'config' };
|
||||
}
|
||||
if (match(lc_b, /\.ipk$/) || match(lc_b, /\.apk$/)) {
|
||||
let out = '';
|
||||
if (shcall('command -v opkg') == 0)
|
||||
out = sh('opkg --force-reinstall --force-downgrade install ' + shq(full) + ' 2>&1');
|
||||
else if (shcall('command -v apk') == 0)
|
||||
out = sh('apk add --force-overwrite --allow-untrusted ' + shq(full) + ' 2>&1');
|
||||
else
|
||||
out = "Error: Neither 'opkg' nor 'apk' package manager found on the system.";
|
||||
shcall('rm -rf /tmp/luci-indexcache /tmp/luci-modulecache/*');
|
||||
return { code: 0, kind: 'package', output: out };
|
||||
}
|
||||
return { error: 'unsupported file type' };
|
||||
}
|
||||
|
||||
function m_backup_create() {
|
||||
ensure_dirs();
|
||||
shcall('chmod +x /usr/sbin/openwrt-backup');
|
||||
shcall('/usr/sbin/openwrt-backup -b > ' + LOG_FILE + ' && sync');
|
||||
const path = '/.reserved/openwrt_config.tar.gz';
|
||||
const ok = access(path);
|
||||
return { ok: ok ? true : false, path };
|
||||
}
|
||||
|
||||
function m_save_backup_list(req) {
|
||||
let v = req.args?.content ?? '';
|
||||
// strip leading whitespace per line
|
||||
v = replace(v, /\r/g, '');
|
||||
const lines = split(v, '\n');
|
||||
const out = [];
|
||||
for (let l in lines) {
|
||||
let t = replace(l, /^\s+/, '');
|
||||
// strip trailing backslash continuation
|
||||
t = replace(t, /\s*\\\s*$/, '');
|
||||
if (t != '') push(out, t);
|
||||
}
|
||||
writefile(BACKUP_LIST, join('\n', out) + '\n');
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
function m_reload_cpu() {
|
||||
// Apply governor / freq settings synchronously so they take effect before
|
||||
// the RPC response is sent back to the browser.
|
||||
const rc = shcall('/etc/init.d/amlogic start');
|
||||
return { code: rc };
|
||||
}
|
||||
|
||||
// Sync menu_install and menu_armcpu UCI flags based on runtime platform detection.
|
||||
function m_sync_menu() {
|
||||
const p = platform();
|
||||
const m = show_install_menu();
|
||||
// Detect whether OpenWrt has already been installed to internal storage
|
||||
const is_installed = root_on_internal_storage();
|
||||
const can_install = (index(p, 'amlogic') >= 0 || index(p, 'allwinner') >= 0 || index(m, 'yes') >= 0);
|
||||
const show_install = (can_install && !is_installed) ? 'yes' : 'no';
|
||||
const show_armcpu = (index(p, 'qemu') < 0) ? 'yes' : 'no';
|
||||
shcall('uci -q set amlogic.config.menu_install=' + shq(show_install) +
|
||||
' && uci -q set amlogic.config.menu_armcpu=' + shq(show_armcpu) +
|
||||
' && uci commit amlogic' +
|
||||
' && rm -f /tmp/luci-indexcache* && rm -rf /tmp/luci-modulecache/*');
|
||||
return { code: 0, show_install, show_armcpu };
|
||||
}
|
||||
|
||||
// Enumerate cpufreq policies and their available frequencies and governors.
|
||||
function m_cpu_policies() {
|
||||
const policies_str = sh("ls /sys/devices/system/cpu/cpufreq 2>/dev/null | grep -E 'policy[0-9]{1,3}' | xargs");
|
||||
let names = split(policies_str, ' ');
|
||||
if (length(names) == 1 && names[0] == '') names = ['policy0'];
|
||||
const out = [];
|
||||
for (let n in names) {
|
||||
const name = trim(n);
|
||||
if (name == '') continue;
|
||||
const id = replace(name, 'policy', '');
|
||||
const freqs_raw = readfile('/sys/devices/system/cpu/cpufreq/' + name + '/scaling_available_frequencies') ?? '';
|
||||
const govs_raw = readfile('/sys/devices/system/cpu/cpufreq/' + name + '/scaling_available_governors') ?? '';
|
||||
const freqs = []; for (let v in split(trim(freqs_raw), ' ')) if (v != '') push(freqs, v);
|
||||
const govs = []; for (let v in split(trim(govs_raw), ' ')) if (v != '') push(govs, v);
|
||||
const core_type = sh('cat /sys/devices/system/cpu/cpu' + id + "/uevent | grep -E '^OF_COMPATIBLE_0' | sed 's/^OF_COMPATIBLE_0=//' | xargs");
|
||||
push(out, { name, id, freqs, governors: govs, core_type: core_type != '' ? core_type : 'Unknown' });
|
||||
}
|
||||
return { policies: out };
|
||||
}
|
||||
|
||||
const methods = {
|
||||
state: { call: m_state },
|
||||
author: { call: m_author },
|
||||
platform_info: { call: m_platform_info },
|
||||
model_database: { call: m_model_database },
|
||||
snapshot_list: { call: m_snapshot_list },
|
||||
snapshot_create: { call: m_snapshot_create },
|
||||
snapshot_delete: { args: { name: '' }, call: m_snapshot_delete },
|
||||
snapshot_restore:{ args: { name: '' }, call: m_snapshot_restore },
|
||||
check_plugin: { args: { options: '' }, call: m_check_plugin },
|
||||
check_kernel: { args: { options: '' }, call: m_check_kernel },
|
||||
check_firmware: { args: { options: '' }, call: m_check_firmware },
|
||||
start_plugin: { call: m_start_plugin },
|
||||
start_kernel: { call: m_start_kernel },
|
||||
start_update: { args: { amlogic_update_sel: '' }, call: m_start_update },
|
||||
start_install: { args: { amlogic_install_sel: '' }, call: m_start_install },
|
||||
start_rescue: { call: m_start_rescue },
|
||||
read_log_tail: { args: { name: '' }, call: m_read_log_tail },
|
||||
read_log_full: { args: { name: '' }, call: m_read_log_full },
|
||||
del_log: { call: m_del_log },
|
||||
poweroff: { call: m_poweroff },
|
||||
kvm_switch: { call: m_kvm_switch },
|
||||
check_upfiles: { call: m_check_upfiles },
|
||||
upload_path: { call: m_upload_path },
|
||||
upload_chunk: { args: { name: '', data: '', append: false }, call: m_upload_chunk },
|
||||
list_uploads: { call: m_list_uploads },
|
||||
delete_upload: { args: { name: '' }, call: m_delete_upload },
|
||||
install_upload: { args: { name: '' }, call: m_install_upload },
|
||||
backup_create: { call: m_backup_create },
|
||||
save_backup_list:{ args: { content: '' }, call: m_save_backup_list },
|
||||
prime_backup_list:{ call: m_prime_backup_list },
|
||||
reload_cpu: { call: m_reload_cpu },
|
||||
sync_menu: { call: m_sync_menu },
|
||||
cpu_policies: { call: m_cpu_policies }
|
||||
};
|
||||
|
||||
return { 'luci.amlogic': methods };
|
||||
Reference in new issue
Block a user