update 2026-10-29 11:59:06

This commit is contained in:
action committed 2026-10-29 11:59:06 +08:00
1 parent bc85557d34
commit e2f86e09f4
5033 files changed
-2043160

No files matched your search

@@ -1,308 +0,0 @@
#!/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}
)"
# Find the <li> list items where the OpenWrt file is located.
asset_records="$(awk -v board="${BOARD}" -v kver="${main_line_version}" -v suffix="${firmware_suffix}" -v RS='</li>' '
BEGIN {
href_pattern = "/releases/download/[^\"]*_" board "_[^\"]*k" kver "\\.[0-9]+[^\"]*" suffix
block_pattern = ".*_" board "_.*k" kver "\\.[0-9]+.*" suffix
}
$0 ~ block_pattern {
file_name = ""; file_date = ""; file_sha256 = ""
if (match($0, href_pattern)) {
file_path = substr($0, RSTART, RLENGTH)
file_name = file_path; sub(/.*\//, "", file_name)
}
if (match($0, /datetime="[^"]*"/)) { file_date = substr($0, RSTART + 10, RLENGTH - 11) }
if (match($0, /value="sha256:[^"]*/)) { file_sha256 = substr($0, RSTART + 14, RLENGTH - 14) }
if (file_name != "") { printf "%s\t%s\t%s\n", file_date, file_name, file_sha256 }
}' <<<"${html_code}")"
[[ -z "${asset_records}" ]] && tolog "02.03.01 No matching download links found." "1"
# Select the OpenWrt file with the newest upload date.
latest_record="$(echo "${asset_records}" | sort -t$'\t' -k1,1 | tail -n 1)"
# Find the OpenWrt filename
latest_url="$(echo "${latest_record}" | cut -f2)"
tolog "02.03.02 OpenWrt file: ${latest_url}"
# Find the date of the latest update
latest_updated_at="$(echo "${latest_record}" | cut -f1)"
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 "${latest_record}" | cut -f3)"
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
@@ -1,271 +0,0 @@
#!/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
@@ -1,271 +0,0 @@
#!/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
@@ -1,77 +0,0 @@
{
"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" }
}
}
@@ -1,76 +0,0 @@
{
"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",
"backup_status"
]
},
"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" ]
}
}
}
}
@@ -1,806 +0,0 @@
// 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 BACKUP_STATUS = LOG_DIR + '/amlogic_backup_status.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'
+ ' # Restart uhttpd before rpcd: this cleanup runs as a child of rpcd, so\n'
+ ' # restarting rpcd last avoids tearing down the bridge while uhttpd is\n'
+ ' # still being reconfigured.\n'
+ ' /etc/init.d/uhttpd restart >> "$LOG" 2>&1\n'
+ ' /etc/init.d/rpcd 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' };
}
// Kick off openwrt-backup -b in the background; the resulting tarball can be
// fetched via fs.read_direct once backup_status reports done. Packing /etc
// (docker, openclash rules, etc.) often exceeds the ~20s ubus RPC timeout, so
// this must not block the rpcd thread.
function m_backup_create() {
ensure_dirs();
shcall('chmod +x /usr/sbin/openwrt-backup');
writefile(BACKUP_STATUS, 'running\n');
shbg('/usr/sbin/openwrt-backup -b > ' + LOG_FILE + ' 2>&1 && sync && ' +
'[ -f /.reserved/openwrt_config.tar.gz ] && ' +
'echo done > ' + BACKUP_STATUS + ' || echo failed > ' + BACKUP_STATUS);
return { code: 0 };
}
function m_backup_status() {
const t = trim(readfile(BACKUP_STATUS) ?? '');
const path = '/.reserved/openwrt_config.tar.gz';
const st = stat(path);
return {
status: t != '' ? t : 'idle',
path,
size: (st && st.type == 'file') ? st.size : 0
};
}
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 },
backup_status: { call: m_backup_status },
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 };