update 2026-09-24 16:56:26
No files matched your search
@@ -0,0 +1,38 @@
|
||||
#=====================================================================================================
|
||||
# This file is licensed under the terms of the GNU General Public
|
||||
# License version 2. This program is licensed "as is" without any
|
||||
# warranty of any kind, whether express or implied.
|
||||
#
|
||||
# This file is a part of the luci-app-amlogic plugin
|
||||
# https://github.com/ophub/luci-app-amlogic
|
||||
#
|
||||
# Description: Supports install/update/backup/restore/snapshot operations using OpenWrt on many boxes
|
||||
# Copyright (C) 2021- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021- https://github.com/ophub/luci-app-amlogic
|
||||
#
|
||||
# Command: curl -fsSL git.io/luci-app-amlogic | bash
|
||||
#=====================================================================================================
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-amlogic
|
||||
PKG_VERSION:=3.1.321
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_LICENSE:=GPL-2.0 License
|
||||
PKG_MAINTAINER:=ophub <https://github.com/ophub/luci-app-amlogic>
|
||||
|
||||
LUCI_TITLE:=LuCI support for custom TV boxes
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DEPENDS:= \
|
||||
@(aarch64||arm) +luci-base +rpcd +rpcd-mod-ucode \
|
||||
+bash +blkid +block-mount +curl +dosfstools +e2fsprogs \
|
||||
+fdisk +jq +losetup +lsblk +parted +perl +pv +uuidgen
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/amlogic
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="175" height="20" role="img" aria-label="OpenWrt: Compiler author"><title>OpenWrt: Compiler author</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="175" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="76" height="20" fill="#555"/><rect x="76" width="99" height="20" fill="#4c1"/><rect width="175" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViPC90aXRsZT48cGF0aCBkPSJNMTIgLjI5N2MtNi42MyAwLTEyIDUuMzczLTEyIDEyIDAgNS4zMDMgMy40MzggOS44IDguMjA1IDExLjM4NS42LjExMy44Mi0uMjU4LjgyLS41NzcgMC0uMjg1LS4wMS0xLjA0LS4wMTUtMi4wNC0zLjMzOC43MjQtNC4wNDItMS42MS00LjA0Mi0xLjYxQzQuNDIyIDE4LjA3IDMuNjMzIDE3LjcgMy42MzMgMTcuN2MtMS4wODctLjc0NC4wODQtLjcyOS4wODQtLjcyOSAxLjIwNS4wODQgMS44MzggMS4yMzYgMS44MzggMS4yMzYgMS4wNyAxLjgzNSAyLjgwOSAxLjMwNSAzLjQ5NS45OTguMTA4LS43NzYuNDE3LTEuMzA1Ljc2LTEuNjA1LTIuNjY1LS4zLTUuNDY2LTEuMzMyLTUuNDY2LTUuOTMgMC0xLjMxLjQ2NS0yLjM4IDEuMjM1LTMuMjItLjEzNS0uMzAzLS41NC0xLjUyMy4xMDUtMy4xNzYgMCAwIDEuMDA1LS4zMjIgMy4zIDEuMjMuOTYtLjI2NyAxLjk4LS4zOTkgMy0uNDA1IDEuMDIuMDA2IDIuMDQuMTM4IDMgLjQwNSAyLjI4LTEuNTUyIDMuMjg1LTEuMjMgMy4yODUtMS4yMy42NDUgMS42NTMuMjQgMi44NzMuMTIgMy4xNzYuNzY1Ljg0IDEuMjMgMS45MSAxLjIzIDMuMjIgMCA0LjYxLTIuODA1IDUuNjI1LTUuNDc1IDUuOTIuNDIuMzYuODEgMS4wOTYuODEgMi4yMiAwIDEuNjA2LS4wMTUgMi44OTYtLjAxNSAzLjI4NiAwIC4zMTUuMjEuNjkuODI1LjU3QzIwLjU2NSAyMi4wOTIgMjQgMTcuNTkyIDI0IDEyLjI5N2MwLTYuNjI3LTUuMzczLTEyLTEyLTEyIi8+PC9zdmc+"/><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">OpenWrt</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="490">OpenWrt</text><text aria-hidden="true" x="1245" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="890">Compiler author</text><text x="1245" y="140" transform="scale(.1)" fill="#fff" textLength="890">Compiler author</text></g></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="85" height="20" role="img" aria-label="JavaScript"><title>JavaScript</title><g shape-rendering="crispEdges"><rect width="0" height="20" fill="#555"/><rect x="0" width="85" height="20" fill="#323330"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><image x="5" y="3" width="14" height="14" href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjZjdkZjFlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+SmF2YVNjcmlwdDwvdGl0bGU+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6bTIyLjAzNCAxOC4yNzZjLS4xNzUtMS4wOTUtLjg4OC0yLjAxNS0zLjAwMy0yLjg3My0uNzM2LS4zNDUtMS41NTQtLjU4NS0xLjc5Ny0xLjE0LS4wOTEtLjMzLS4xMDUtLjUxLS4wNDYtLjcwNS4xNS0uNjQ2LjkxNS0uODQgMS41MTUtLjY2LjM5LjEyLjc1LjQyLjk3Ni45IDEuMDM0LS42NzYgMS4wMzQtLjY3NiAxLjc1NS0xLjEyNS0uMjctLjQyLS40MDQtLjYwMS0uNTg2LS43OC0uNjMtLjcwNS0xLjQ2OS0xLjA2NS0yLjgzNC0xLjAzNGwtLjcwNS4wODljLS42NzYuMTY1LTEuMzIuNTI1LTEuNzEgMS4wMDUtMS4xNCAxLjI5MS0uODExIDMuNTQxLjU2OSA0LjQ3MSAxLjM2NSAxLjAyIDMuMzYxIDEuMjQ0IDMuNjE2IDIuMjA1LjI0IDEuMTctLjg3IDEuNTQ1LTEuOTY2IDEuNDEtLjgxMS0uMTgtMS4yNi0uNTg2LTEuNzU1LTEuMzM2bC0xLjgzIDEuMDUxYy4yMS40OC40NS42ODkuODEgMS4xMDkgMS43NCAxLjc1NiA2LjA5IDEuNjY2IDYuODcxLTEuMDA0LjAyOS0uMDkuMjQtLjcwNS4wNzQtMS42NWwuMDQ2LjA2N3ptLTguOTgzLTcuMjQ1aC0yLjI0OGMwIDEuOTM4LS4wMDkgMy44NjQtLjAwOSA1LjgwNSAwIDEuMjMyLjA2MyAyLjM2My0uMTM4IDIuNzExLS4zMy42ODktMS4xOC42MDEtMS41NjYuNDgtLjM5Ni0uMTk2LS41OTctLjQ2Ni0uODMtLjg1NS0uMDYzLS4xMDUtLjExLS4xOTYtLjEyNy0uMTk2bC0xLjgyNSAxLjEyNWMuMzA1LjYzLjc1IDEuMTcyIDEuMzI0IDEuNTE3Ljg1NS41MSAyLjAwNC42NzUgMy4yMDcuNDA1Ljc4My0uMjI2IDEuNDU4LS42OTEgMS44MTEtMS40MTEuNTEtLjkzLjQwMi0yLjA3LjM5Ny0zLjM0Ni4wMTItMi4wNTQgMC00LjEwOSAwLTYuMTc5bC4wMDQtLjA1NnoiLz48L3N2Zz4="/><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="570">JavaScript</text></g></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="47" height="20" role="img" aria-label="Lua"><title>Lua</title><g shape-rendering="crispEdges"><rect width="0" height="20" fill="#555"/><rect x="0" width="47" height="20" fill="#2c2d72"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><image x="5" y="3" width="14" height="14" href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRpdGxlPkx1YTwvdGl0bGU+PHBhdGggZD0iTS4zOCAxMC4zNzdsLS4yNzItLjAzN2MtLjA0OC4zNDQtLjA4Mi42OTUtLjEwMSAxLjA0MWwuMjc1LjAxNmMuMDE4LS4zNC4wNTEtLjY4Mi4wOTgtMS4wMnpNNC4xMzYgMy4yODlsLS4xODQtLjIwNWMtLjI1OC4yMzItLjUwOS40OC0uNzQ2LjczNGwuMjAyLjE4OGMuMjMxLS4yNDguNDc2LS40OS43MjgtLjcxN3pNNS43NjkgMi4wNTlsLS4xNDYtLjIzNWMtLjI5Ni4xODYtLjU4Ni4zODUtLjg2My41OTRsLjE2Ni4yMTljLjI3LS4yMDMuNTU0LS4zOTkuODQzLS41Nzh6TTEuODI0IDE4LjM2OWMuMTg1LjI5Ny4zODQuNTg2LjU5My44NjNsLjIyLS4xNjRjLS4yMDUtLjI3MS0uMzk5LS41NTUtLjU4LS44NDRsLS4yMzMuMTQ1ek0xLjEyNyAxNi40MDJsLS4yNTUuMTA0Yy4xMjkuMzE4LjI3NC42MzUuNDMxLjk0M2wuMDA1LjAxLjI0NS0uMTI1LS4wMDUtLjAxYy0uMTUzLS4zMDEtLjI5NS0uNjExLS40MjEtLjkyMnpNLjI5OCA5LjMwOWwuMjY5LjA2M2MuMDc2LS4zMzIuMTY4LS42NjQuMjcyLS45ODZsLS4yNjEtLjA4N2MtLjEwOC4zMzItLjIwMi42NzItLjI4IDEuMDF6TS4yNzQgMTIuNDJsLS4yNzUuMDFjLjAxMi4zNDguMDQuNjk5LjA4MyAxLjA0M2wuMjczLS4wMzNjLS4wNDItLjMzNi0uMDY5LS42OC0uMDgxLTEuMDJ6TS4yNTYgMTQuNTA2Yy4wNzMuMzQuMTYyLjY4Mi4yNjQgMS4wMTRsLjI2My0uMDhjLS4xLS4zMjYtLjE4Ny0uNjU4LS4yNTgtLjk5bC0uMjY5LjA1NnpNMTEuNTczLjI3NUwxMS41NjMgMGMtLjM0OC4wMTItLjY5OS4wMzktMS4wNDQuMDgybC4wMzQuMjczYy4zMzgtLjA0MS42OC0uMDY4IDEuMDItLjA4ek0yMy4yMjEgOC41NjZjLjEuMzI2LjE4Ni42Ni4yNTYuOTkybC4yNy0uMDU5Yy0uMDcyLS4zNC0uMTYtLjY4Mi0uMjYyLTEuMDE0bC0uMjY0LjA4MXpNMTcuNjIxIDEuMzg5Yy0uMzA5LS4xNjQtLjYyNy0uMzE0LS45NDctLjQ0OWwtLjEwNy4yNTJjLjMxNC4xMzMuNjI1LjI4MS45MjYuNDM5bC4xMjgtLjI0MnpNMTUuNjkzLjU3MmMtLjMzMi0uMTA1LS42Ny0uMTk5LTEuMDEtLjI3N2wtLjA2My4yNjhjLjMzMi4wNzYuNjY0LjE2OC45ODguMjczbC4wODUtLjI2NHpNNi42NzQgMS41NDVjLjI5OC0uMTUuNjA2LS4yOTEuOTE2LS40MThMNy40ODYuODczYy0uMzE3LjEyNy0uNjMyLjI3Mi0uOTM3LjQyOGwtLjAxNS4wMDguMTI1LjI0NC4wMTUtLjAwOHpNMjMuNzI3IDExLjU4OGwuMjc1LS4wMWExMS43OTcgMTEuNzk3IDAgMCAwLS4wODItMS4wNDVsLS4yNzMuMDMzYy4wNDEuMzM4LjA2OC42ODIuMDggMS4wMjJ6TTEzLjY1NC4xMDVjLS4zNDYtLjA0Ny0uNjk2LS4wOC0xLjA0My0uMDk4bC0uMDE0LjI3M2MuMzM5LjAxOC42ODMuMDUxIDEuMDE5LjA5OGwuMDM4LS4yNzN6TTkuNTQ0LjUyN2wtLjA1OC0uMjdjLS4zNC4wNzItLjY4MS4xNi0xLjAxNC4yNjRsLjA4MS4yNjJjLjMyNS0uMDk5LjY1OS0uMTg1Ljk5MS0uMjU2ek0xLjkyMSA1LjQ2OWwuMjMxLjE1Yy4xODUtLjI4NS4zODQtLjU2Ni41OTItLjgzNGwtLjIxNy0uMTdjLS4yMTMuMjc2LS40MTcuNTYzLS42MDYuODU0ek0uOTQzIDcuMzE4bC4yNTMuMTA3Yy4xMzItLjMxMy4yOC0uNjI1LjQzOS0uOTI0bC0uMjQzLS4xMjhjLS4xNjMuMzA3LS4zMTQuNjI1LS40NDkuOTQ1ek0xOC4yMjMgMjEuOTQzbC4xNDUuMjM0Yy4yOTUtLjE4Ni41ODYtLjM4NS44NjMtLjU5NGwtLjE2NC0uMjE5Yy0uMjcyLjIwNC0uNTU3LjQtLjg0NC41Nzl6TTIxLjI0OCAxOS4yMTlsLjIxNy4xN2MuMjE1LS4yNzMuNDE4LS41NjEuNjA3LS44NTRsLS4yMy0uMTQ4Yy0uMTg2LjI4NS0uMzg1LjU2NC0uNTk0LjgzMnpNMTkuODU1IDIwLjcxNWwuMTg0LjIwM2MuMjU4LS4yMy41MS0uNDc5Ljc0Ni0uNzMybC0uMjAxLS4xODhjLS4yMy4yNDgtLjQ3Ny40ODgtLjcyOS43MTd6TTIyLjM1OSAxNy41MDRsLjI0NC4xMjljLjE2Mi0uMzA3LjMxNC0uNjI1LjQ0OS0uOTQ1bC0uMjU0LS4xMDdhMTEuMjcgMTEuMjcgMCAwIDEtLjQzOS45MjN6TTIzLjYxNyAxMy42MjlsLjI3My4wMzljLjA0OS0uMzQ2LjA4Mi0uNjk1LjEwMi0xLjA0M2wtLjI3NS0uMDE0Yy0uMDE4LjMzOC0uMDUxLjY4Mi0uMSAxLjAxOHpNMjMuMTU2IDE1LjYyMWwuMjY0LjA4NmMuMTA3LS4zMzIuMjAxLS42Ny4yNzktMS4wMWwtLjI2OC0uMDYzYy0uMDc3LjMzMy0uMTY5LjY2NS0uMjc1Ljk4N3pNMjIuNDUzIDYuNjcyYy4xNTQuMzAzLjI5Ny42MTcuNDI0LjkzMmwuMjU2LS4xMDRjLS4xMzEtLjMyMi0uMjc3LS42NDMtLjQzNi0uOTUzbC0uMjQ0LjEyNXpNOC4yOTYgMjMuNDE4Yy4zMzEuMTA3LjY3LjIwMSAxLjAwOS4yNzlsLjA2Mi0uMjY4Yy0uMzMxLS4wNzYtLjY2My0uMTY4LS45ODYtLjI3M2wtLjA4NS4yNjJ6TTEwLjMzNSAyMy44ODljLjM0NS4wNDkuNjk2LjA4MiAxLjA0My4xMDJsLjAxNC0uMjc1Yy0uMzM5LS4wMTgtLjY4Mi0uMDUxLTEuMDE5LS4wOThsLS4wMzguMjcxek0xNy4zMjYgMjIuNDQ5Yy0uMzAzLjE1NC0uNjEzLjI5Ny0uOTI2LjQyNGwuMTA0LjI1NmMuMzE4LS4xMzEuNjM5LS4yNzUuOTQ3LS40MzRsLjAwNC0uMDAyLS4xMjMtLjI0Ni0uMDA2LjAwMnpNNC42MTMgMjEuNDY3Yy4yNzQuMjEzLjU2Mi40MTguODU0LjYwNWwuMTQ5LS4yM2MtLjI4NS0uMTg0LS41NjUtLjM4NS0uODMzLS41OTJsLS4xNy4yMTd6TTEyLjQxNyAyMy43MjVsLjAwOS4yNzVjLjM0OC0uMDE0LjY5OS0uMDQxIDEuMDQ1LS4wODRsLS4wMzUtLjI3MWMtLjMzNi4wNDEtLjY4LjA2OC0xLjAxOS4wOHpNNi4zNyAyMi42MDRjLjMwNy4xNjIuNjI1LjMxNC45NDYuNDQ5bC4xMDctLjI1NGMtLjMxMy0uMTMzLS42MjQtLjI3OS0uOTI0LS40MzlsLS4xMjkuMjQ0ek0zLjA4MyAyMC4wNDFjLjIzMy4yNTguNDguNTEuNzM0Ljc0NmwuMTg4LS4yMDFjLS4yNDktLjIzLS40OS0uNDc3LS43MTctLjcyOWwtLjIwNS4xODR6TTE0LjQ0NSAyMy40NzVsLjA1OS4yN2MuMzQtLjA3NC42OC0uMTYyIDEuMDE0LS4yNjZsLS4wODItLjI2MmMtLjMyNS4wOTktLjY1OS4xODUtLjk5MS4yNTh6TTIxLjE4LjEyOUEyLjY4OSAyLjY4OSAwIDEgMCAyMS4xOCA1LjUwNyAyLjY4OSAyLjY4OSAwIDEgMCAyMS4xOC4xMjl6TTE1LjMyNCAxNS40NDdjMCAuNDcxLjMxNC42Ni44NTIuNjYuNjcgMCAxLjI5Ny0uMzk2IDEuMjk3LTEuMDE2di0uNjQ1Yy0uMjMuMTA3LS4zNzkuMTQxLTEuMTA3LjI0LS43MzUuMTA5LTEuMDQyLjMwNi0xLjA0Mi43NjF6TTEyIDIuODE4Yy01LjA3IDAtOS4xOCA0LjEwOS05LjE4IDkuMTggMCA1LjA2OCA0LjExIDkuMTggOS4xOCA5LjE4IDUuMDcgMCA5LjE4LTQuMTExIDkuMTgtOS4xOCAwLTUuMDctNC4xMS05LjE4LTkuMTgtOS4xOHptLTIuLine truncated
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="187" height="20" role="img" aria-label="OpenWrt: flippy's homepage"><title>OpenWrt: flippy's homepage</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="187" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="76" height="20" fill="#555"/><rect x="76" width="111" height="20" fill="blueviolet"/><rect width="187" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViPC90aXRsZT48cGF0aCBkPSJNMTIgLjI5N2MtNi42MyAwLTEyIDUuMzczLTEyIDEyIDAgNS4zMDMgMy40MzggOS44IDguMjA1IDExLjM4NS42LjExMy44Mi0uMjU4LjgyLS41NzcgMC0uMjg1LS4wMS0xLjA0LS4wMTUtMi4wNC0zLjMzOC43MjQtNC4wNDItMS42MS00LjA0Mi0xLjYxQzQuNDIyIDE4LjA3IDMuNjMzIDE3LjcgMy42MzMgMTcuN2MtMS4wODctLjc0NC4wODQtLjcyOS4wODQtLjcyOSAxLjIwNS4wODQgMS44MzggMS4yMzYgMS44MzggMS4yMzYgMS4wNyAxLjgzNSAyLjgwOSAxLjMwNSAzLjQ5NS45OTguMTA4LS43NzYuNDE3LTEuMzA1Ljc2LTEuNjA1LTIuNjY1LS4zLTUuNDY2LTEuMzMyLTUuNDY2LTUuOTMgMC0xLjMxLjQ2NS0yLjM4IDEuMjM1LTMuMjItLjEzNS0uMzAzLS41NC0xLjUyMy4xMDUtMy4xNzYgMCAwIDEuMDA1LS4zMjIgMy4zIDEuMjMuOTYtLjI2NyAxLjk4LS4zOTkgMy0uNDA1IDEuMDIuMDA2IDIuMDQuMTM4IDMgLjQwNSAyLjI4LTEuNTUyIDMuMjg1LTEuMjMgMy4yODUtMS4yMy42NDUgMS42NTMuMjQgMi44NzMuMTIgMy4xNzYuNzY1Ljg0IDEuMjMgMS45MSAxLjIzIDMuMjIgMCA0LjYxLTIuODA1IDUuNjI1LTUuNDc1IDUuOTIuNDIuMzYuODEgMS4wOTYuODEgMi4yMiAwIDEuNjA2LS4wMTUgMi44OTYtLjAxNSAzLjI4NiAwIC4zMTUuMjEuNjkuODI1LjU3QzIwLjU2NSAyMi4wOTIgMjQgMTcuNTkyIDI0IDEyLjI5N2MwLTYuNjI3LTUuMzczLTEyLTEyLTEyIi8+PC9zdmc+"/><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">OpenWrt</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="490">OpenWrt</text><text aria-hidden="true" x="1305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="1010">flippy's homepage</text><text x="1305" y="140" transform="scale(.1)" fill="#fff" textLength="1010">flippy's homepage</text></g></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="161" height="20" role="img" aria-label="Plugin: luci-app-amlogic"><title>Plugin: luci-app-amlogic</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="161" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="60" height="20" fill="#555"/><rect x="60" width="101" height="20" fill="#007ec6"/><rect width="161" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViPC90aXRsZT48cGF0aCBkPSJNMTIgLjI5N2MtNi42MyAwLTEyIDUuMzczLTEyIDEyIDAgNS4zMDMgMy40MzggOS44IDguMjA1IDExLjM4NS42LjExMy44Mi0uMjU4LjgyLS41NzcgMC0uMjg1LS4wMS0xLjA0LS4wMTUtMi4wNC0zLjMzOC43MjQtNC4wNDItMS42MS00LjA0Mi0xLjYxQzQuNDIyIDE4LjA3IDMuNjMzIDE3LjcgMy42MzMgMTcuN2MtMS4wODctLjc0NC4wODQtLjcyOS4wODQtLjcyOSAxLjIwNS4wODQgMS44MzggMS4yMzYgMS44MzggMS4yMzYgMS4wNyAxLjgzNSAyLjgwOSAxLjMwNSAzLjQ5NS45OTguMTA4LS43NzYuNDE3LTEuMzA1Ljc2LTEuNjA1LTIuNjY1LS4zLTUuNDY2LTEuMzMyLTUuNDY2LTUuOTMgMC0xLjMxLjQ2NS0yLjM4IDEuMjM1LTMuMjItLjEzNS0uMzAzLS41NC0xLjUyMy4xMDUtMy4xNzYgMCAwIDEuMDA1LS4zMjIgMy4zIDEuMjMuOTYtLjI2NyAxLjk4LS4zOTkgMy0uNDA1IDEuMDIuMDA2IDIuMDQuMTM4IDMgLjQwNSAyLjI4LTEuNTUyIDMuMjg1LTEuMjMgMy4yODUtMS4yMy42NDUgMS42NTMuMjQgMi44NzMuMTIgMy4xNzYuNzY1Ljg0IDEuMjMgMS45MSAxLjIzIDMuMjIgMCA0LjYxLTIuODA1IDUuNjI1LTUuNDc1IDUuOTIuNDIuMzYuODEgMS4wOTYuODEgMi4yMiAwIDEuNjA2LS4wMTUgMi44OTYtLjAxNSAzLjI4NiAwIC4zMTUuMjEuNjkuODI1LjU3QzIwLjU2NSAyMi4wOTIgMjQgMTcuNTkyIDI0IDEyLjI5N2MwLTYuNjI3LTUuMzczLTEyLTEyLTEyIi8+PC9zdmc+"/><text aria-hidden="true" x="395" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">Plugin</text><text x="395" y="140" transform="scale(.1)" fill="#fff" textLength="330">Plugin</text><text aria-hidden="true" x="1095" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="910">luci-app-amlogic</text><text x="1095" y="140" transform="scale(.1)" fill="#fff" textLength="910">luci-app-amlogic</text></g></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,84 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Theme-aware styles for luci-app-amlogic JS views.
|
||||
*/
|
||||
|
||||
/* --- Status text ------------------------------------ */
|
||||
.amlogic-status-ok { color: #1a7f37; }
|
||||
.amlogic-status-err { color: #c0392b; }
|
||||
.amlogic-status-info { color: #1f6feb; }
|
||||
|
||||
/* --- Spacious row table -----------------------------------------------
|
||||
* Used by views that lay out a single button + status text per row
|
||||
* (check / backup / upload). border-collapse:collapse avoids the visible
|
||||
* blank band that border-spacing would otherwise add around the table
|
||||
* edges; row breathing room comes purely from cell padding.
|
||||
* ---------------------------------------------------------------------- */
|
||||
.amlogic-row-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.amlogic-row-table > tbody > tr > td,
|
||||
.amlogic-row-table > tr > td {
|
||||
padding: 10px 12px;
|
||||
vertical-align: middle;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* --- Snapshot grid (cards) --------------------------------------------- */
|
||||
.amlogic-snap-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
.amlogic-snap-list:after,
|
||||
.amlogic-snap-list .amlogic-snap-item:not(:nth-child(3n)) {
|
||||
content: "";
|
||||
}
|
||||
.amlogic-snap-item {
|
||||
box-sizing: border-box;
|
||||
width: 150px;
|
||||
height: 130px;
|
||||
margin: 8px;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: 1px solid rgba(127, 127, 127, 0.35);
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
}
|
||||
.amlogic-snap-item:hover {
|
||||
background: rgba(127, 127, 127, 0.15);
|
||||
}
|
||||
.amlogic-snap-line {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
/* --- Info-page logo / author tiles ------------------------------------- */
|
||||
.amlogic-info-logo {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* --- Dark mode overrides (system / browser preference) -----------------
|
||||
* Reads prefers-color-scheme from the system / browser. When the user has
|
||||
* picked a dark theme, switch to brighter colors to keep readable contrast.
|
||||
* LuCI itself only adjusts the background, so this stylesheet fills in the
|
||||
* status text and snapshot border colors that the upstream theme leaves
|
||||
* untouched.
|
||||
* ---------------------------------------------------------------------- */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.amlogic-status-ok { color: #4ade80; }
|
||||
.amlogic-status-err { color: #f87171; }
|
||||
.amlogic-status-info { color: #60a5fa; }
|
||||
.amlogic-snap-item {
|
||||
border-color: rgba(200, 200, 200, 0.25);
|
||||
}
|
||||
.amlogic-snap-item:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// CPU Freq Settings page
|
||||
//
|
||||
// Purpose: configure scaling governor and min/max frequency per CPU policy
|
||||
// (cluster). After save, the backend script fixcpufreq.pl is invoked so the
|
||||
// new values take effect immediately. The backing RPC is exposed by
|
||||
// /usr/share/rpcd/ucode/luci.amlogic.
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require rpc';
|
||||
'require uci';
|
||||
|
||||
// Query CPU cluster info (each policy: core type, available governors, freqs).
|
||||
const callPolicies = rpc.declare({ object: 'luci.amlogic', method: 'cpu_policies',
|
||||
expect: { policies: [] } });
|
||||
// Tell backend to apply the new governor / frequency limits right after save.
|
||||
const callReloadCpu = rpc.declare({ object: 'luci.amlogic', method: 'reload_cpu' });
|
||||
|
||||
return view.extend({
|
||||
// Load in parallel: CPU policy info + UCI config (we will read/write it).
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
callPolicies(),
|
||||
uci.load('amlogic')
|
||||
]);
|
||||
},
|
||||
|
||||
// Render a tabbed form section per CPU policy (cluster), with options to
|
||||
// set the governor and min/max frequency. The form is bound to the amlogic.armcpu UCI section.
|
||||
render: function (data) {
|
||||
const policies = data[0] || [];
|
||||
|
||||
// Auto-create the armcpu section when missing, so that on a fresh
|
||||
// install/upgrade the user can save without first creating the section
|
||||
// manually.
|
||||
if (!uci.get('amlogic', 'armcpu')) {
|
||||
uci.add('amlogic', 'settings', 'armcpu');
|
||||
}
|
||||
|
||||
// Build CBI Map bound to the amlogic.armcpu NamedSection.
|
||||
const m = new form.Map('amlogic', _('CPU Freq Settings'),
|
||||
_('Set CPU Scaling Governor to Max Performance or Balance Mode'));
|
||||
const s = m.section(form.NamedSection, 'armcpu', 'settings');
|
||||
s.anonymous = true;
|
||||
|
||||
// Render an independent tab + form options per CPU cluster (big/little).
|
||||
policies.forEach(function (p) {
|
||||
const tabId = 'tab' + p.id;
|
||||
s.tab(tabId, p.name);
|
||||
|
||||
// Microarchitecture (read-only display, inline so it aligns with the label).
|
||||
const ct = s.taboption(tabId, form.DummyValue, 'core_type' + p.id,
|
||||
_('Microarchitectures:'));
|
||||
ct.cfgvalue = function () { return p.core_type; };
|
||||
ct.renderWidget = function (section_id) {
|
||||
return E('span', { style: 'line-height:2em; display:inline-block; vertical-align:middle' },
|
||||
this.cfgvalue(section_id) || '');
|
||||
};
|
||||
|
||||
// CPU scaling governor: list of governors exposed by the kernel
|
||||
// (performance / schedutil / etc).
|
||||
const gov = s.taboption(tabId, form.ListValue, 'governor' + p.id,
|
||||
_('CPU Scaling Governor:'));
|
||||
(p.governors || []).forEach(function (e) { gov.value(e, e.toUpperCase()); });
|
||||
gov.default = 'schedutil';
|
||||
gov.rmempty = false;
|
||||
|
||||
// Minimum running frequency (kHz).
|
||||
const minF = s.taboption(tabId, form.ListValue, 'minfreq' + p.id, _('Min Freq:'));
|
||||
(p.freqs || []).forEach(function (e) { minF.value(e); });
|
||||
minF.default = '500000';
|
||||
minF.rmempty = false;
|
||||
|
||||
// Maximum running frequency (kHz).
|
||||
const maxF = s.taboption(tabId, form.ListValue, 'maxfreq' + p.id, _('Max Freq:'));
|
||||
(p.freqs || []).forEach(function (e) { maxF.value(e); });
|
||||
maxF.default = '1512000';
|
||||
maxF.rmempty = false;
|
||||
});
|
||||
|
||||
// Reload CPU settings after the LuCI apply mechanism commits UCI to disk.
|
||||
// The standard apply flow (apply_rollback → ucitrack → init.d restart)
|
||||
// reads UCI before the new values are fully written, so we re-apply here.
|
||||
document.addEventListener('uci-applied', function () {
|
||||
callReloadCpu();
|
||||
});
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Backup Firmware Config + Snapshot management + (optional) KVM switch
|
||||
//
|
||||
// Purpose: pack current /etc config into openwrt_config.tar.gz and download it;
|
||||
// manage system snapshots (list / create / restore / delete); optionally show
|
||||
// KVM dual-partition switch when the platform reports has_kvm.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (backup_create, snapshot_*, platform_info, kvm_switch).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require fs';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// Generate one openwrt_config.tar.gz on the server and return its path so the
|
||||
// frontend can stream it back via fs.read_direct.
|
||||
const callBackup = rpc.declare({ object: 'luci.amlogic', method: 'backup_create' });
|
||||
// List existing snapshot names (e.g. etc-000 / etc-001 / etc-202401...).
|
||||
const callSnapList = rpc.declare({ object: 'luci.amlogic', method: 'snapshot_list',
|
||||
expect: { names: [] } });
|
||||
// Create a new snapshot (the backend chooses its name).
|
||||
const callSnapCreate = rpc.declare({ object: 'luci.amlogic', method: 'snapshot_create' });
|
||||
// Delete a snapshot by short name (without the etc- prefix).
|
||||
const callSnapDel = rpc.declare({ object: 'luci.amlogic', method: 'snapshot_delete',
|
||||
params: ['name'] });
|
||||
// Restore the given snapshot and reboot.
|
||||
const callSnapRest = rpc.declare({ object: 'luci.amlogic', method: 'snapshot_restore',
|
||||
params: ['name'] });
|
||||
// Read platform info (this page only consumes has_kvm to decide whether to
|
||||
// show the KVM switch row).
|
||||
const callPlatform = rpc.declare({ object: 'luci.amlogic', method: 'platform_info' });
|
||||
// Switch active KVM partition.
|
||||
const callKvmSwitch = rpc.declare({ object: 'luci.amlogic', method: 'kvm_switch' });
|
||||
|
||||
// The view object for the Backup & Snapshot page.
|
||||
return view.extend({
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
// Load in parallel: platform info + current snapshot list.
|
||||
load: function () {
|
||||
amlogicShared.ensureCss();
|
||||
return Promise.all([callPlatform(), callSnapList()]);
|
||||
},
|
||||
|
||||
// Render the page: backup config section + snapshot management section + (optional) KVM switch section.
|
||||
render: function (data) {
|
||||
const platform = data[0] || {};
|
||||
const snapNames = data[1] || [];
|
||||
const view = this;
|
||||
|
||||
const editListBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Open List'),
|
||||
// Navigate to the backuplist page to edit the backup file list.
|
||||
click: function () {
|
||||
location.href = L.url('admin/system/amlogic/backuplist');
|
||||
}
|
||||
});
|
||||
|
||||
// Download status hint; updated to success color after generation completes.
|
||||
const downloadStatus = E('span', { class: 'amlogic-status-err' });
|
||||
const downloadBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Download Backup'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
const btn = ev.currentTarget;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Generating...');
|
||||
// Step 1: ask the server to pack /etc into openwrt_config.tar.gz.
|
||||
return callBackup().then(function (r) {
|
||||
if (!r || !r.ok) {
|
||||
downloadStatus.textContent = _("Couldn't open file:") + ' ' + (r ? r.path : '');
|
||||
return;
|
||||
}
|
||||
// Switch to success color before streaming the blob
|
||||
downloadStatus.className = 'amlogic-status-ok';
|
||||
downloadStatus.textContent = _('The file Will download automatically.') + ' ' + r.path;
|
||||
// Fetch the file blob via fs.read_direct and trigger browser download.
|
||||
return fs.read_direct(r.path, 'blob').then(function (blob) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'openwrt_config.tar.gz';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
setTimeout(function () { URL.revokeObjectURL(url); }, 10000);
|
||||
});
|
||||
}).catch(function (e) {
|
||||
downloadStatus.textContent = _('Create upload file error.') +
|
||||
' ' + (e && e.message ? e.message : '');
|
||||
}).finally(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Download Backup');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// Navigate to the upload page where the user can upload a backup file to restore.
|
||||
const restoreBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Upload Backup'),
|
||||
click: function () {
|
||||
location.href = L.url('admin/system/amlogic/upload');
|
||||
}
|
||||
});
|
||||
|
||||
// Snapshot grid; styles adapt to the current theme via amlogic-snap-* classes.
|
||||
const snapDiv = E('div', { class: 'amlogic-snap-list' });
|
||||
|
||||
// Render snapshot cards; etc-000/001 are read-only (no Delete button).
|
||||
// Backend expects the short name without the "etc-" prefix.
|
||||
function renderSnapshots(names) {
|
||||
snapDiv.innerHTML = '';
|
||||
if (!names || !names.length) {
|
||||
snapDiv.appendChild(E('span', { class: 'amlogic-status-err' },
|
||||
_('Currently OpenWrt does not support the snapshot function.') +
|
||||
_('Please use this plugin to reinstall or upgrade OpenWrt to enable the snapshot function.')));
|
||||
return;
|
||||
}
|
||||
names.forEach(function (n) {
|
||||
let title;
|
||||
if (n === 'etc-000') title = _('Initialize Snapshot');
|
||||
else if (n === 'etc-001') title = _('Update Snapshot');
|
||||
else title = n;
|
||||
const restoreSnapBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-apply',
|
||||
value: _('Restore Snap'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
const short = n.replace(/^etc-/, '');
|
||||
if (!confirm(_('You selected a snapshot:') + ' [ ' + n + ' ] , ' +
|
||||
_('Confirm recovery and restart OpenWrt?')))
|
||||
return;
|
||||
// Capture btn before entering async chain; ev.currentTarget becomes null after await.
|
||||
const btn = ev.currentTarget;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Restoring...');
|
||||
return callSnapRest(short).then(function (r) {
|
||||
if (r && r.code == 0) {
|
||||
btn.value = _('Successfully Restored');
|
||||
ui.addNotification(null,
|
||||
E('p', { class: 'amlogic-status-err' },
|
||||
_('Snapshot restored. The system will reboot now.')));
|
||||
} else {
|
||||
btn.value = _('Restore Failed');
|
||||
}
|
||||
}).catch(function () { btn.value = _('Restore Failed'); });
|
||||
})
|
||||
});
|
||||
const item = E('div', {
|
||||
class: 'amlogic-snap-item',
|
||||
id: 'snapshots_div_' + n
|
||||
}, [
|
||||
E('div', { class: 'amlogic-snap-line' }, title),
|
||||
E('div', { class: 'amlogic-snap-line' }, restoreSnapBtn)
|
||||
]);
|
||||
if (n !== 'etc-000' && n !== 'etc-001') {
|
||||
const delBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-remove',
|
||||
value: _('Delete Snap'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
const short = n.replace(/^etc-/, '');
|
||||
if (!confirm(_('You selected a snapshot:') + ' [ ' + n + ' ] , ' +
|
||||
_('Confirm delete?')))
|
||||
return;
|
||||
// Capture btn before entering async chain; ev.currentTarget becomes null after await.
|
||||
const btn = ev.currentTarget;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Deleting...');
|
||||
return callSnapDel(short).then(function (r) {
|
||||
if (r && r.code == 0) {
|
||||
btn.value = _('Successfully Deleted');
|
||||
item.style.display = 'none';
|
||||
} else {
|
||||
btn.value = _('Delete Failed');
|
||||
}
|
||||
}).catch(function () { btn.value = _('Delete Failed'); });
|
||||
})
|
||||
});
|
||||
item.appendChild(E('div', { class: 'amlogic-snap-line' }, delBtn));
|
||||
}
|
||||
snapDiv.appendChild(item);
|
||||
});
|
||||
}
|
||||
renderSnapshots(snapNames);
|
||||
|
||||
// Create snapshot button above the grid.
|
||||
const createSnapBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Create Snapshot'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
const btn = ev.currentTarget;
|
||||
btn.disabled = true;
|
||||
return callSnapCreate().then(function () {
|
||||
return callSnapList();
|
||||
}).then(function (r) {
|
||||
renderSnapshots(Array.isArray(r) ? r : (r && r.names) || []);
|
||||
}).finally(function () { btn.disabled = false; });
|
||||
})
|
||||
});
|
||||
|
||||
// If the platform supports KVM dual partitions, show a button to trigger partition switch.
|
||||
const sections = [
|
||||
E('h2', _('Backup Firmware Config')),
|
||||
E('p', _('Backup OpenWrt config (openwrt_config.tar.gz). Use this file to restore the config in [Manually Upload Update].')),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('table', { class: 'amlogic-row-table' }, [
|
||||
E('tr', [
|
||||
E('td', { width: '20%', align: 'right' }, _('Edit List:')),
|
||||
E('td', { width: '80%', align: 'left' }, editListBtn)
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { width: '20%', align: 'right' }, _('Backup Config:')),
|
||||
E('td', { width: '80%', align: 'left' }, [downloadBtn, ' ', downloadStatus])
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { width: '20%', align: 'right' }, _('Restore Backup:')),
|
||||
E('td', { width: '80%', align: 'left' }, restoreBtn)
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('h2', _('Snapshot Management')),
|
||||
E('p', _('Create a snapshot of the current system configuration, or restore to a snapshot.')),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('div', { style: 'margin-bottom:10px' }, createSnapBtn),
|
||||
snapDiv
|
||||
])
|
||||
];
|
||||
|
||||
// If the platform supports KVM dual partitions, show a button to trigger partition switch.
|
||||
if (platform.has_kvm) {
|
||||
const kvmBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Switch System'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
if (!confirm(_('Are you sure you want to switch systems?'))) return;
|
||||
ev.currentTarget.disabled = true;
|
||||
ui.showModal(_('System is switching...'), [
|
||||
E('p', { class: 'spinning' }, _('Waiting for system switching...'))
|
||||
]);
|
||||
return callKvmSwitch().then(function () {
|
||||
if (ui.awaitReconnect) {
|
||||
ui.awaitReconnect();
|
||||
} else {
|
||||
// Fallback for older LuCI builds without awaitReconnect
|
||||
setTimeout(function () { location.reload(); }, 5000);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
sections.push(E('h2', _('KVM dual system switching')));
|
||||
sections.push(E('p', _('You can freely switch between KVM dual partitions, using OpenWrt systems in different partitions.')));
|
||||
sections.push(E('div', { class: 'cbi-section' }, [kvmBtn]));
|
||||
}
|
||||
|
||||
return E('div', {}, sections);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Backup file list editor
|
||||
//
|
||||
// Purpose: edit /etc/amlogic_backup_list.conf which controls which files/dirs
|
||||
// the openwrt-backup script archives. If the file is missing or empty, the
|
||||
// backend seeds it from the default BACKUP_LIST in /usr/sbin/openwrt-backup.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (save_backup_list, prime_backup_list).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require fs';
|
||||
|
||||
// Persist the user-edited content back to /etc/amlogic_backup_list.conf.
|
||||
const callSave = rpc.declare({ object: 'luci.amlogic', method: 'save_backup_list',
|
||||
params: ['content'] });
|
||||
// Seed the list file from defaults embedded in openwrt-backup if missing or empty.
|
||||
const callPrime = rpc.declare({ object: 'luci.amlogic', method: 'prime_backup_list' });
|
||||
|
||||
// This page uses its own Save button; hide LuCI's default Save/Apply/Reset.
|
||||
return view.extend({
|
||||
// This page uses its own Save button; hide LuCI's default Save/Apply/Reset.
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
// Load the existing backup list content to pre-fill the editor. If the file is
|
||||
load: function () {
|
||||
// Run prime first (idempotent), then read the file content.
|
||||
return callPrime().then(function () {
|
||||
return fs.read('/etc/amlogic_backup_list.conf').catch(function () { return ''; });
|
||||
});
|
||||
},
|
||||
|
||||
// Render a full-width textarea pre-filled with the existing content, and a Save button
|
||||
// that persists the changes via RPC. Also include a Back button to return to the main Backup page.
|
||||
render: function (text) {
|
||||
// Full-width multi-line editor pre-filled with the existing list.
|
||||
const ta = E('textarea', {
|
||||
rows: '30',
|
||||
style: 'width:100%; font-family:monospace'
|
||||
}, text || '');
|
||||
|
||||
// Status hint area (saved successfully / failed).
|
||||
const status = E('span', { style: 'margin-left:1em' });
|
||||
const saveBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Save'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
const btn = ev.currentTarget;
|
||||
btn.disabled = true;
|
||||
return callSave(ta.value).then(function (r) {
|
||||
if (r && (r.ok || r.code == 0)) {
|
||||
status.textContent = _('Successfully saved.');
|
||||
// Navigate back after a short delay so the success message is visible.
|
||||
setTimeout(function () {
|
||||
location.href = L.url('admin/system/amlogic/backup');
|
||||
}, 700);
|
||||
} else {
|
||||
status.textContent = _('Save Failed');
|
||||
}
|
||||
}).catch(function (e) {
|
||||
status.textContent = _('Save Failed') + ': ' + (e && e.message ? e.message : String(e));
|
||||
}).finally(function () { btn.disabled = false; });
|
||||
})
|
||||
});
|
||||
// Return to the Backup main page (without saving).
|
||||
const backBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reset',
|
||||
value: _('Back'),
|
||||
click: function () { location.href = L.url('admin/system/amlogic/backup'); }
|
||||
});
|
||||
|
||||
return E('div', {}, [
|
||||
E('h2', _('Edit list of backup files')),
|
||||
E('p', _('In this list, you can manually add the file or directory list of files that need to be backed up. Please confirm that the format and content of the file are correct.')),
|
||||
E('div', { class: 'cbi-section' }, [ta]),
|
||||
E('div', { class: 'cbi-page-actions' }, [saveBtn, ' ', backBtn, status])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,471 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Online Download Update + Rescue Kernel
|
||||
//
|
||||
// Purpose: check / download / install plugin, kernel, and firmware updates online.
|
||||
// Log tail is polled at 1 Hz; the last log line drives a state machine
|
||||
// (idle → checking → button → installing → done). Terminal log lines contain
|
||||
// raw HTML <input> button strings which are parsed and rendered as DOM buttons.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (state, check_*, start_*, read_log_tail).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require dom';
|
||||
'require poll';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// ── RPCs ─────────────────────────────────────────────────────────────────────
|
||||
// Query system state (firmware / plugin / kernel versions).
|
||||
const callState = rpc.declare({ object: 'luci.amlogic', method: 'state' });
|
||||
// Run plugin / kernel / firmware version check and write progress to the log.
|
||||
const callCheckPlugin = rpc.declare({ object: 'luci.amlogic', method: 'check_plugin', params: ['options'] });
|
||||
const callCheckKernel = rpc.declare({ object: 'luci.amlogic', method: 'check_kernel', params: ['options'] });
|
||||
const callCheckFirmware = rpc.declare({ object: 'luci.amlogic', method: 'check_firmware', params: ['options'] });
|
||||
const callStartPlugin = rpc.declare({ object: 'luci.amlogic', method: 'start_plugin' });
|
||||
const callStartKernel = rpc.declare({ object: 'luci.amlogic', method: 'start_kernel' });
|
||||
const callStartUpdate = rpc.declare({ object: 'luci.amlogic', method: 'start_update', params: ['amlogic_update_sel'] });
|
||||
const callStartRescue = rpc.declare({ object: 'luci.amlogic', method: 'start_rescue' });
|
||||
// Read the last line (up to 4096 bytes) of the named log file.
|
||||
const callLogTail = rpc.declare({ object: 'luci.amlogic', method: 'read_log_tail', params: ['name'], expect: { line: '' } });
|
||||
|
||||
// ── Log-line HTML button parser ──────────────────────────────────────────────
|
||||
// Parse an <input> button string from the log; return null for plain-text lines.
|
||||
// Returned object: { type, param, label } where type identifies the action.
|
||||
function parseButtonLine(line) {
|
||||
if (!line || line.indexOf('<input') === -1) return null;
|
||||
|
||||
// Extract value="..."
|
||||
var valueMatch = line.match(/value="([^"]*)"/);
|
||||
var onclickMatch = line.match(/onclick="([^"]*)"/);
|
||||
if (!valueMatch || !onclickMatch) return null;
|
||||
|
||||
var value = valueMatch[1];
|
||||
var onclick = onclickMatch[1];
|
||||
|
||||
// Text after the self-closing tag
|
||||
var labelMatch = line.match(/\/>(.*)$/);
|
||||
var label = labelMatch ? labelMatch[1].trim() : '';
|
||||
|
||||
// Plugin download: b_check_plugin(this, 'download_VERSION')
|
||||
if (value === 'Download' && onclick.indexOf('b_check_plugin') !== -1) {
|
||||
var m = onclick.match(/b_check_plugin\s*\(\s*this\s*,\s*'([^']*)'\s*\)/);
|
||||
return { type: 'plugin-download', param: m ? m[1] : null, label: label };
|
||||
}
|
||||
|
||||
// Firmware download: b_check_firmware(this, 'PARAM')
|
||||
if (value === 'Download' && onclick.indexOf('b_check_firmware') !== -1) {
|
||||
var m = onclick.match(/b_check_firmware\s*\(\s*this\s*,\s*'([^']*)'\s*\)/);
|
||||
return { type: 'firmware-download', param: m ? m[1] : null, label: label };
|
||||
}
|
||||
|
||||
// Kernel download: b_check_kernel(this, 'PARAM')
|
||||
if (value === 'Download' && onclick.indexOf('b_check_kernel') !== -1) {
|
||||
var m = onclick.match(/b_check_kernel\s*\(\s*this\s*,\s*'([^']*)'\s*\)/);
|
||||
return { type: 'kernel-download', param: m ? m[1] : null, label: label };
|
||||
}
|
||||
|
||||
// Firmware install: amlogic_update(this, 'PARAM')
|
||||
if (value === 'Update' && onclick.indexOf('amlogic_update') !== -1) {
|
||||
var m = onclick.match(/amlogic_update\s*\(\s*this\s*,\s*'([^']*)'\s*\)/);
|
||||
return { type: 'firmware-update', param: m ? m[1] : null, label: label };
|
||||
}
|
||||
|
||||
// Kernel install: amlogic_kernel(this)
|
||||
if (value === 'Update' && onclick.indexOf('amlogic_kernel') !== -1) {
|
||||
return { type: 'kernel-update', param: null, label: label };
|
||||
}
|
||||
|
||||
// Plugin install: amlogic_plugin(this)
|
||||
if (value === 'Update' && onclick.indexOf('amlogic_plugin') !== -1) {
|
||||
return { type: 'plugin-update', param: null, label: label };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Per-action state machine ──────────────────────────────────────────────────
|
||||
// States: 'idle' | 'checking' | 'button' | 'installing' | 'done'
|
||||
|
||||
// Action object factory: { name, statusEl, state, subBtn }
|
||||
function makeAction(name, statusEl) {
|
||||
return { name: name, statusEl: statusEl, state: 'idle', subBtn: null };
|
||||
}
|
||||
|
||||
// Update the status span with a text string (blue info style).
|
||||
function showInfo(el, text) {
|
||||
dom.content(el, E('span', { class: 'amlogic-status-info' }, text));
|
||||
}
|
||||
|
||||
// Update the status span with a success string (green).
|
||||
function showOk(el, text) {
|
||||
dom.content(el, E('span', { class: 'amlogic-status-ok' }, text));
|
||||
}
|
||||
|
||||
// Update the status span with an error string (red).
|
||||
function showErr(el, text) {
|
||||
dom.content(el, E('span', { class: 'amlogic-status-err' }, text));
|
||||
}
|
||||
|
||||
// Replace statusEl contents with a real DOM button + optional label text.
|
||||
// onClick is called when the user presses it.
|
||||
function showSubButton(el, btnLabel, labelText, onClick) {
|
||||
var btn = E('input', {
|
||||
type: 'button',
|
||||
class: 'cbi-button cbi-button-reload',
|
||||
value: btnLabel,
|
||||
click: onClick
|
||||
});
|
||||
var nodes = [btn];
|
||||
if (labelText) nodes.push(E('span', { class: 'amlogic-status-info' }, ' ' + labelText));
|
||||
dom.content(el, nodes);
|
||||
return btn;
|
||||
}
|
||||
|
||||
// ── Poll handler (called at 1 Hz) ────────────────────────────────────────────
|
||||
// Inspect the last non-empty log line and advance the action state machine.
|
||||
function handlePoll(action) {
|
||||
if (action.state === 'done') return;
|
||||
|
||||
return callLogTail(action.name).then(function (raw) {
|
||||
// Only inspect the last non-empty line to avoid false-positive
|
||||
// 'Failed'/'error' matches from intermediate opkg/apk output.
|
||||
var lines = (raw || '').split('\n');
|
||||
var line = '';
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
var l = lines[i].trim();
|
||||
if (l) { line = l; break; }
|
||||
}
|
||||
|
||||
if (action.state === 'idle') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.state === 'button') {
|
||||
// Waiting for user to click sub-button; don't re-render on every poll.
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.state === 'installing') {
|
||||
// Show progress lines; detect terminal keywords.
|
||||
if (!line) return;
|
||||
// Skip if line is the same HTML button (stale log from check phase).
|
||||
if (line.indexOf('<input') !== -1) return;
|
||||
if (line.indexOf('Successfully updated') !== -1 ||
|
||||
line.indexOf('Successful Update') !== -1 ||
|
||||
line.indexOf('Successful') !== -1) {
|
||||
showOk(action.statusEl, line);
|
||||
action.state = 'done';
|
||||
} else if (line.indexOf('Failed') !== -1 || line.indexOf('failed') !== -1 ||
|
||||
line.indexOf('Error') !== -1 || line.indexOf('error') !== -1) {
|
||||
showErr(action.statusEl, line);
|
||||
action.state = 'done';
|
||||
} else {
|
||||
// Show current progress line (may change rapidly).
|
||||
showInfo(action.statusEl, line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// state === 'checking': look for HTML button line or plain progress text.
|
||||
var parsed = parseButtonLine(line);
|
||||
if (parsed) {
|
||||
action.state = 'button';
|
||||
action._parsed = parsed;
|
||||
renderParsedButton(action);
|
||||
} else if (line) {
|
||||
showInfo(action.statusEl, line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Render the sub-button parsed from the log line and wire up the second-phase RPC.
|
||||
function renderParsedButton(action) {
|
||||
var parsed = action._parsed;
|
||||
var el = action.statusEl;
|
||||
|
||||
switch (parsed.type) {
|
||||
|
||||
case 'plugin-download':
|
||||
showSubButton(el, _('Download'), parsed.label, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ev.currentTarget.value = _('Downloading...');
|
||||
action.state = 'checking';
|
||||
callCheckPlugin(parsed.param).then(function () {});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'firmware-download':
|
||||
showSubButton(el, _('Download'), parsed.label, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ev.currentTarget.value = _('Downloading...');
|
||||
action.state = 'checking';
|
||||
callCheckFirmware(parsed.param).then(function () {});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'kernel-download':
|
||||
showSubButton(el, _('Download'), parsed.label, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ev.currentTarget.value = _('Downloading...');
|
||||
action.state = 'checking';
|
||||
callCheckKernel(parsed.param).then(function () {});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'firmware-update':
|
||||
showSubButton(el, _('Update'), parsed.label, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ev.currentTarget.value = _('Updating...');
|
||||
// Transition to 'installing'; terminal result comes via log polling.
|
||||
action.state = 'installing';
|
||||
dom.content(el, E('span', { class: 'amlogic-status-info' }, _('Starting update...')));
|
||||
callStartUpdate(parsed.param).catch(function () {
|
||||
// Only show error if the trigger call itself fails (rare).
|
||||
showErr(el, _('Failed to start update'));
|
||||
action.state = 'done';
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'kernel-update':
|
||||
showSubButton(el, _('Update'), parsed.label, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ev.currentTarget.value = _('Updating...');
|
||||
action.state = 'installing';
|
||||
dom.content(el, E('span', { class: 'amlogic-status-info' }, _('Starting kernel update...')));
|
||||
callStartKernel().catch(function () {
|
||||
showErr(el, _('Failed to start kernel update'));
|
||||
action.state = 'done';
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'plugin-update':
|
||||
showSubButton(el, _('Update'), parsed.label, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ev.currentTarget.value = _('Updating...');
|
||||
action.state = 'installing';
|
||||
dom.content(el, E('span', { class: 'amlogic-status-info' }, _('Starting plugin update...')));
|
||||
callStartPlugin().catch(function () {
|
||||
showErr(el, _('Failed to start plugin update'));
|
||||
action.state = 'done';
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
showInfo(el, parsed.label || '');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main view ────────────────────────────────────────────────────────────────
|
||||
return view.extend({
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
render: function () {
|
||||
amlogicShared.ensureCss();
|
||||
|
||||
// Version display spans (left persistent, filled by state RPC)
|
||||
var verFirmware = E('span', _('Collecting data...'));
|
||||
var verPlugin = E('span', _('Collecting data...'));
|
||||
var verKernel = E('span', _('Collecting data...'));
|
||||
var verRescue = E('span', _('Collecting data...'));
|
||||
|
||||
// Per-action status spans (right side, updated by poll)
|
||||
var statusFw = E('span');
|
||||
var statusPl = E('span');
|
||||
var statusKn = E('span');
|
||||
var statusRs = E('span');
|
||||
|
||||
// State machines
|
||||
var actPlugin = makeAction('plugin', statusPl);
|
||||
var actKernel = makeAction('kernel', statusKn);
|
||||
var actFirmware = makeAction('firmware', statusFw);
|
||||
var actRescue = makeAction('rescue', statusRs);
|
||||
|
||||
// ── Action buttons (left column) ──────────────────────────────────
|
||||
|
||||
// Helper: reset an action back to idle so it can be re-run after 'done'.
|
||||
function resetAction(action, btn, idleLabel) {
|
||||
action.state = 'idle';
|
||||
action.subBtn = null;
|
||||
dom.content(action.statusEl, '');
|
||||
btn.disabled = false;
|
||||
btn.value = idleLabel;
|
||||
}
|
||||
|
||||
// Each button triggers the first-phase RPC to start the check/download/install process,
|
||||
// then the poll handler picks up log lines to advance the state machine and render sub-buttons as needed.
|
||||
var btnPlugin = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Only update Amlogic Service'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
var btn = ev.currentTarget;
|
||||
// Allow re-run after completion by resetting 'done' → 'idle'
|
||||
if (actPlugin.state === 'done')
|
||||
resetAction(actPlugin, btn, _('Only update Amlogic Service'));
|
||||
if (actPlugin.state !== 'idle') return;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Checking...');
|
||||
actPlugin.state = 'checking';
|
||||
dom.content(actPlugin.statusEl, '');
|
||||
return callCheckPlugin('check').then(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Only update Amlogic Service');
|
||||
}).catch(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Only update Amlogic Service');
|
||||
showErr(actPlugin.statusEl, _('Check Failed'));
|
||||
actPlugin.state = 'idle';
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// The kernel and firmware buttons have similar logic, just different RPCs and labels.
|
||||
var btnKernel = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Update system kernel only'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
var btn = ev.currentTarget;
|
||||
if (actKernel.state === 'done')
|
||||
resetAction(actKernel, btn, _('Update system kernel only'));
|
||||
if (actKernel.state !== 'idle') return;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Checking...');
|
||||
actKernel.state = 'checking';
|
||||
dom.content(actKernel.statusEl, '');
|
||||
return callCheckKernel('check').then(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Update system kernel only');
|
||||
}).catch(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Update system kernel only');
|
||||
showErr(actKernel.statusEl, _('Check Failed'));
|
||||
actKernel.state = 'idle';
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// Firmware update may include both kernel and plugin updates, so it has a more generic label.
|
||||
var btnFirmware = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Complete system update'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
var btn = ev.currentTarget;
|
||||
if (actFirmware.state === 'done')
|
||||
resetAction(actFirmware, btn, _('Complete system update'));
|
||||
if (actFirmware.state !== 'idle') return;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Checking...');
|
||||
actFirmware.state = 'checking';
|
||||
dom.content(actFirmware.statusEl, '');
|
||||
return callCheckFirmware('check').then(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Complete system update');
|
||||
}).catch(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Complete system update');
|
||||
showErr(actFirmware.statusEl, _('Check Failed'));
|
||||
actFirmware.state = 'idle';
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// Rescue button: triggers the rescue RPC which starts mutual recovery; the log will show progress and terminal status.
|
||||
var btnRescue = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Rescue the original system kernel'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
var btn = ev.currentTarget;
|
||||
if (actRescue.state === 'done')
|
||||
resetAction(actRescue, btn, _('Rescue the original system kernel'));
|
||||
if (actRescue.state !== 'idle') return;
|
||||
btn.disabled = true;
|
||||
btn.value = _('Rescuing...');
|
||||
// Set installing state before the call so the poller picks up log lines.
|
||||
actRescue.state = 'installing';
|
||||
showInfo(actRescue.statusEl, _('Starting rescue...'));
|
||||
return callStartRescue().then(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Rescue the original system kernel');
|
||||
}).catch(function () {
|
||||
btn.disabled = false;
|
||||
btn.value = _('Rescue the original system kernel');
|
||||
showErr(actRescue.statusEl, _('Failed to start rescue'));
|
||||
actRescue.state = 'idle';
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// ── Layout ────────────────────────────────────────────────────────
|
||||
|
||||
var body = E('div', {}, [
|
||||
E('h2', _('Check Update')),
|
||||
E('p', _('Provide OpenWrt Firmware, Kernel and Plugin online check, download and update service.')),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('p', { style: 'text-align:center' }, [
|
||||
_('Update plugins first, then update the kernel or firmware. More options can be configured in [Plugin Settings].')
|
||||
]),
|
||||
E('table', { class: 'amlogic-row-table' }, [
|
||||
E('tr', [
|
||||
E('td', { width: '35%', align: 'right' }, btnPlugin),
|
||||
E('td', { width: '65%', align: 'left' }, [verPlugin, ' ', statusPl])
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { width: '35%', align: 'right' }, btnKernel),
|
||||
E('td', { width: '65%', align: 'left' }, [verKernel, ' ', statusKn])
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { width: '35%', align: 'right' }, btnFirmware),
|
||||
E('td', { width: '65%', align: 'left' }, [verFirmware, ' ', statusFw])
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('h2', _('Rescue Kernel')),
|
||||
E('p', _('When a kernel update fails and causes the OpenWrt system to be unbootable, the kernel can be restored by mutual recovery from eMMC/NVMe/sdX.')),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('table', { class: 'amlogic-row-table' }, [
|
||||
E('tr', [
|
||||
E('td', { width: '35%', align: 'right' }, btnRescue),
|
||||
E('td', { width: '65%', align: 'left' }, [verRescue, ' ', statusRs])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
// ── Populate version spans ────────────────────────────────────────
|
||||
|
||||
callState().then(function (s) {
|
||||
dom.content(verFirmware, s && s.current_firmware_version
|
||||
? E('span', { class: 'amlogic-status-ok' }, _('Current Version') + ' [ ' + s.current_firmware_version + ' ] ')
|
||||
: E('span', { class: 'amlogic-status-err' }, _('Invalid value.')));
|
||||
dom.content(verPlugin, s && s.current_plugin_version
|
||||
? E('span', { class: 'amlogic-status-ok' }, _('Current Version') + ' [ ' + s.current_plugin_version + ' ] ')
|
||||
: E('span', { class: 'amlogic-status-err' }, _('Invalid value.')));
|
||||
dom.content(verKernel, s && s.current_kernel_version
|
||||
? E('span', { class: 'amlogic-status-ok' }, _('Current Version') + ' [ ' + s.current_kernel_version + ' ] ')
|
||||
: E('span', { class: 'amlogic-status-err' }, _('Invalid value.')));
|
||||
|
||||
// Rescue row: show same kernel version directly via the verRescue span
|
||||
dom.content(verRescue, s && s.current_kernel_version
|
||||
? E('span', { class: 'amlogic-status-ok' }, _('Current Version') + ' [ ' + s.current_kernel_version + ' ] ')
|
||||
: E('span', { class: 'amlogic-status-err' }, _('Invalid value.')));
|
||||
});
|
||||
|
||||
// ── 1 Hz poll ─────────────────────────────────────────────────────
|
||||
|
||||
poll.add(function () {
|
||||
return Promise.all([
|
||||
handlePoll(actPlugin),
|
||||
handlePoll(actKernel),
|
||||
handlePoll(actFirmware),
|
||||
handlePoll(actRescue)
|
||||
]);
|
||||
}, 1);
|
||||
|
||||
return body;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Plugin Settings
|
||||
//
|
||||
// Purpose: configure the `config` section in /etc/config/amlogic via form.Map,
|
||||
// covering firmware/kernel GitHub repos, tags, version branch, plugin branch,
|
||||
// keep-config flag, bootloader write flag, and shared partition fs type.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (platform_info, state).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require rpc';
|
||||
'require uci';
|
||||
|
||||
// Get platform info; only used to display the device PLATFORM tag.
|
||||
const callPlatform = rpc.declare({ object: 'luci.amlogic', method: 'platform_info' });
|
||||
// Query runtime state; kernel_release is used to derive default kernel tag and branch.
|
||||
const callState = rpc.declare({ object: 'luci.amlogic', method: 'state' });
|
||||
|
||||
// This page uses its own Save button; hide LuCI's default Save/Apply/Reset.
|
||||
return view.extend({
|
||||
// Load platform info + state in parallel, along with the UCI config (we will read/write it).
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
callPlatform(),
|
||||
callState(),
|
||||
uci.load('amlogic')
|
||||
]);
|
||||
},
|
||||
|
||||
// Render form options bound to the amlogic.config NamedSection, and pre-fill some defaults based on platform/state.
|
||||
render: function (data) {
|
||||
const platform = data[0] || {};
|
||||
const state = data[1] || {};
|
||||
|
||||
// Auto-create the section if missing
|
||||
if (!uci.get('amlogic', 'config')) {
|
||||
uci.add('amlogic', 'amlogic', 'config');
|
||||
}
|
||||
|
||||
// Build a form.Map bound to the amlogic.config NamedSection, with options for various plugin settings.
|
||||
const m = new form.Map('amlogic', _('Plugin Settings'),
|
||||
_('You can customize the github.com download repository of OpenWrt files and kernels in [Online Download Update].') +
|
||||
'<br />' + _('Tip: The same files as the current OpenWrt system\'s BOARD (such as rock5b) and kernel (such as 5.10) will be downloaded.'));
|
||||
const o = m.section(form.NamedSection, 'config', 'amlogic');
|
||||
o.anonymous = true;
|
||||
|
||||
// 1. Display device platform
|
||||
const dev = o.option(form.DummyValue, 'mydevice', _('Current Device:'),
|
||||
_('Display the PLATFORM classification of the device.'));
|
||||
dev.cfgvalue = function () { return 'PLATFORM: ' + (platform.platform || 'Unknown'); };
|
||||
|
||||
// 2. OpenWrt download repository
|
||||
const repo = o.option(form.Value, 'amlogic_firmware_repo',
|
||||
_('OpenWrt download repository:'),
|
||||
_('Set the OpenWrt files download repository on github.com in [Online Download Update].'));
|
||||
repo.default = 'https://github.com/breakingbadboy/OpenWrt';
|
||||
repo.rmempty = false;
|
||||
|
||||
// 3. Tags keyword
|
||||
const tag = o.option(form.Value, 'amlogic_firmware_tag',
|
||||
_('OpenWrt download tags keyword:'),
|
||||
_('Set the OpenWrt files download tags keyword for github.com in [Online Download Update].'));
|
||||
tag.default = 'ARMv8';
|
||||
tag.rmempty = false;
|
||||
|
||||
// 4. File suffix
|
||||
const suffix = o.option(form.ListValue, 'amlogic_firmware_suffix',
|
||||
_('OpenWrt files suffix:'),
|
||||
_('Set the OpenWrt files download suffix for github.com in [Online Download Update].'));
|
||||
['.7z', '.zip', '.img.gz', '.img.xz'].forEach(function (s) { suffix.value(s, _(s)); });
|
||||
suffix.default = '.img.gz';
|
||||
suffix.rmempty = false;
|
||||
|
||||
// 5. Kernel download repository
|
||||
const kpath = o.option(form.ListValue, 'amlogic_kernel_path',
|
||||
_('Kernel download repository:'),
|
||||
_('Set the kernel files download repository on github.com in [Online Download Update].'));
|
||||
kpath.value('https://github.com/breakingbadboy/OpenWrt');
|
||||
kpath.value('https://github.com/ophub/kernel');
|
||||
kpath.default = 'https://github.com/breakingbadboy/OpenWrt';
|
||||
kpath.rmempty = false;
|
||||
|
||||
// 6. Kernel tags; available tags depend on the selected kernel repo.
|
||||
// Default is auto-derived from kernel_release suffixes (-rk3588/-rk35xx).
|
||||
const currentKpath = uci.get('amlogic', 'config', 'amlogic_kernel_path') ||
|
||||
'https://github.com/breakingbadboy/OpenWrt';
|
||||
const knownTags = {
|
||||
kernel_rk3588: 'kernel_rk3588 [Rockchip RK3588 Kernel]',
|
||||
kernel_rk35xx: 'kernel_rk35xx [Rockchip RK35xx Kernel]',
|
||||
kernel_stable: 'kernel_stable [Mainline Stable Kernel]'
|
||||
};
|
||||
if (currentKpath.indexOf('ophub/kernel') >= 0) {
|
||||
knownTags.kernel_flippy = 'kernel_flippy [Mainline Stable Kernel by Flippy]';
|
||||
knownTags.kernel_beta = 'kernel_beta [Beta Kernel]';
|
||||
}
|
||||
// Determine default tag from saved config or kernel_release uname string.
|
||||
let kernelTagDefault = uci.get('amlogic', 'config', 'amlogic_kernel_tags') || '';
|
||||
if (!kernelTagDefault) {
|
||||
const u = state.kernel_release || '';
|
||||
if (u.indexOf('-rk3588') >= 0) kernelTagDefault = 'kernel_rk3588';
|
||||
else if (u.indexOf('-rk35xx') >= 0) kernelTagDefault = 'kernel_rk35xx';
|
||||
else if (u.indexOf('-beta') >= 0) kernelTagDefault = 'kernel_beta';
|
||||
else kernelTagDefault = 'kernel_stable';
|
||||
}
|
||||
const ktags = o.option(form.ListValue, 'amlogic_kernel_tags',
|
||||
_('Kernel download tags:'),
|
||||
_('Set the kernel files download tags on github.com in [Online Download Update].'));
|
||||
Object.keys(knownTags).forEach(function (k) { ktags.value(k, _(knownTags[k])); });
|
||||
if (!knownTags[kernelTagDefault]) ktags.value(kernelTagDefault, kernelTagDefault);
|
||||
ktags.default = kernelTagDefault;
|
||||
ktags.rmempty = false;
|
||||
|
||||
// 7. Kernel branch
|
||||
const kbranch = o.option(form.ListValue, 'amlogic_kernel_branch',
|
||||
_('Set version branch:'),
|
||||
_('Set the version branch of the OpenWrt files and kernel selected in [Online Download Update].'));
|
||||
['5.10', '5.15', '6.1', '6.6', '6.12', '6.18'].forEach(function (b) {
|
||||
kbranch.value(b, _(b));
|
||||
});
|
||||
const m2 = (state.kernel_release || '').match(/^(\d+\.\d+)/);
|
||||
kbranch.default = m2 ? m2[1] : '5.10';
|
||||
kbranch.rmempty = false;
|
||||
|
||||
// 8. Plugin branch
|
||||
const pbranch = o.option(form.ListValue, 'amlogic_plugin_branch',
|
||||
_('Set plugin branch:'),
|
||||
state.has_luci_js
|
||||
? _('Set the branch of the luci-app-amlogic plugin used in [Only update Amlogic Service]. Select main for JavaScript version or lua for Lua version.')
|
||||
: _('Set the branch of the luci-app-amlogic plugin used in [Only update Amlogic Service]. This system does not have JS LuCI, only the Lua branch is available.'));
|
||||
if (state.has_luci_js)
|
||||
pbranch.value('main', _('main [JavaScript version]'));
|
||||
pbranch.value('lua', _('lua [Lua version]'));
|
||||
pbranch.default = state.has_luci_js ? 'main' : 'lua';
|
||||
pbranch.rmempty = false;
|
||||
|
||||
// 9. Keep config
|
||||
const fcfg = o.option(form.Flag, 'amlogic_firmware_config',
|
||||
_('Keep config update:'),
|
||||
_('Set whether to keep the current config during [Online Download Update] and [Manually Upload Update].'));
|
||||
fcfg.default = '1';
|
||||
fcfg.rmempty = false;
|
||||
|
||||
// 9. Write bootloader
|
||||
const wb = o.option(form.Flag, 'amlogic_write_bootloader',
|
||||
_('Auto write bootloader:'),
|
||||
_('[Recommended choice] Set whether to auto write bootloader during install and update OpenWrt.'));
|
||||
wb.default = '0';
|
||||
wb.rmempty = false;
|
||||
|
||||
// 10. Shared partition fs type
|
||||
const fst = o.option(form.ListValue, 'amlogic_shared_fstype',
|
||||
_('Set the file system type:'),
|
||||
_('[Default ext4] Set the file system type of the shared partition (/mnt/mmcblk*p4) when install OpenWrt.'));
|
||||
['ext4', 'f2fs', 'btrfs', 'xfs'].forEach(function (s) { fst.value(s, _(s)); });
|
||||
fst.default = 'ext4';
|
||||
fst.rmempty = false;
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Plugin landing / info page
|
||||
//
|
||||
// Purpose: render the plugin logo, badge links, feature summary, and supported-box list.
|
||||
// Calls sync_menu on load to update sidebar menu flags from runtime platform detection.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (author, sync_menu, state).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// Extract OPENWRT_AUTHOR from /usr/sbin/openwrt-update-amlogic and return the author's GitHub repo URL.
|
||||
const callAuthor = rpc.declare({
|
||||
object: 'luci.amlogic',
|
||||
method: 'author',
|
||||
expect: { openwrt_author: 'Invalid value.' }
|
||||
});
|
||||
|
||||
// Sync menu_install / menu_armcpu UCI flags from runtime platform detection.
|
||||
const callSyncMenu = rpc.declare({ object: 'luci.amlogic', method: 'sync_menu' });
|
||||
|
||||
// Get runtime state to determine plugin_branch for the language badge.
|
||||
const callState = rpc.declare({ object: 'luci.amlogic', method: 'state' });
|
||||
|
||||
// Static list of supported boxes (display only, not used in any logic).
|
||||
const SUPPORTED_BOXES = [
|
||||
_('Amlogic s922x --- [ Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000 ]'),
|
||||
_('Amlogic s905x3 -- [ X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air ]'),
|
||||
_('Amlogic s905x2 -- [ X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2 ]'),
|
||||
_('Amlogic s912 ---- [ H96-Pro-Plus, Octopus-Planet, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro ]'),
|
||||
_('Amlogic s905x --- [ HG680P, B860H, TBee, T95, TX9, XiaoMI-3S, X96 ]'),
|
||||
_('Amlogic s905w --- [ X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K ]'),
|
||||
_('Amlogic s905d --- [ Phicomm-N1, MECOOL-KI-Pro, SML-5442TW ]'),
|
||||
_('Amlogic s905l --- [ UNT402A, M201-S ]'),
|
||||
_('Amlogic s905l2 -- [ MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E ]'),
|
||||
_('Amlogic s905l3 -- [ CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310 ]'),
|
||||
_('Amlogic s905l3a - [ E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H ]'),
|
||||
_('Amlogic s905l3b - [ CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A ]'),
|
||||
_('Amlogic s905 ---- [ Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5 ]'),
|
||||
_('Allwinner H6 ---- [ V-Plus Cloud ]'),
|
||||
_('Rockchip -------- [ BeikeYun, L1-Pro, FastRhino R66S/R68S, Radxa 5B/E25 ]'),
|
||||
_('Used in KVM ----- [ Can be used in KVM virtual machine of Armbian system. ]')
|
||||
];
|
||||
|
||||
// Open an external link in a new tab; fall back to the current window if the
|
||||
// browser blocks the popup.
|
||||
function openExternal(url) {
|
||||
const w = window.open(url, '_blank', 'noopener,noreferrer');
|
||||
if (!w) window.location.href = url;
|
||||
}
|
||||
|
||||
// This is a read-only info page, so we disable the top Save / Apply / Reset buttons by setting the handlers to null.
|
||||
return view.extend({
|
||||
// Read-only info page: disable the top Save / Apply / Reset buttons.
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
// On load, call sync_menu to update sidebar menu flags from runtime platform detection. If the sidebar nav is out of
|
||||
// sync with the server-side menu flags, reload once to pick up the new menu (guarded by #menu-synced hash).
|
||||
load: function () {
|
||||
// Call sync_menu; reload once when the sidebar nav disagrees with the
|
||||
// server-side menu flags in either direction (guarded by #menu-synced hash).
|
||||
const alreadyReloaded = window.location.hash === '#menu-synced';
|
||||
const syncMenuPromise = callSyncMenu().then(function (res) {
|
||||
if (alreadyReloaded || !res) return;
|
||||
// Check if the sidebar navigation already contains the install link.
|
||||
const installLink = document.querySelector('a[href*="amlogic/install"]');
|
||||
// Menu became visible on the server but is absent in the browser nav.
|
||||
const needShow = (res.show_install === 'yes' && !installLink);
|
||||
// Menu became hidden on the server but is still present in the browser nav.
|
||||
const needHide = (res.show_install === 'no' && installLink);
|
||||
if (needShow || needHide) {
|
||||
// Server-side index cache updated; reload to pick up new nav.
|
||||
window.location.replace(window.location.pathname + '#menu-synced');
|
||||
window.location.reload();
|
||||
}
|
||||
}).catch(function () {});
|
||||
return Promise.all([callAuthor(), syncMenuPromise]).then(function (r) { return r[0]; });
|
||||
},
|
||||
|
||||
// Render the plugin info: logo, badge links, feature summary, and supported-box list.
|
||||
render: function (authorUrl) {
|
||||
// Inject the theme stylesheet on first entry so amlogic-* classes work.
|
||||
amlogicShared.ensureCss();
|
||||
const res = L.resource('amlogic');
|
||||
|
||||
// Language badge image — default to javascript.svg (JS version),
|
||||
// will be confirmed/corrected after the state RPC resolves.
|
||||
var langsImg = E('img', {
|
||||
id: 'Langs',
|
||||
src: res + '/javascript.svg', alt: 'javascript',
|
||||
width: '86', height: '20',
|
||||
style: 'margin:0 5px; vertical-align:middle; border-radius:3px'
|
||||
});
|
||||
|
||||
// Fetch plugin_branch from state and update the language badge.
|
||||
callState().then(function (s) {
|
||||
var isLua = s && s.plugin_branch === 'lua';
|
||||
langsImg.src = res + (isLua ? '/lua.svg' : '/javascript.svg');
|
||||
langsImg.alt = isLua ? 'lua' : 'javascript';
|
||||
langsImg.setAttribute('width', isLua ? '46' : '86');
|
||||
}).catch(function () {});
|
||||
|
||||
// Flatten the supported-boxes list to [text, <br>, text, <br>, ...].
|
||||
const boxRows = [];
|
||||
for (let i = 0; i < SUPPORTED_BOXES.length; i++)
|
||||
boxRows.push(SUPPORTED_BOXES[i], E('br'));
|
||||
|
||||
return E('div', { class: 'cbi-section' }, [
|
||||
E('h2', { style: 'border-bottom:none' }, _('Amlogic Service')),
|
||||
E('p', { style: 'border:none; margin-bottom:0' }, _('Supports management of Amlogic s9xxx, Allwinner (V-Plus Cloud), and Rockchip (BeikeYun, Chainedbox L1 Pro) boxes.')),
|
||||
E('table', { class: 'amlogic-row-table' }, [
|
||||
E('tr', [
|
||||
E('td', { colspan: '2', style: 'text-align:center' }, [
|
||||
E('img', { src: res + '/logo.png', alt: 'Logo', width: '135' })
|
||||
])
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { colspan: '2', style: 'text-align:center' }, [
|
||||
E('img', {
|
||||
src: res + '/packit.svg', alt: 'Packit',
|
||||
width: '168', height: '20',
|
||||
style: 'cursor:pointer; margin:0 5px; vertical-align:middle; border-radius:3px',
|
||||
click: function () { openExternal('https://github.com/unifreq/openwrt_packit'); }
|
||||
}),
|
||||
E('img', {
|
||||
src: res + '/author.svg', alt: 'Author',
|
||||
width: '168', height: '20',
|
||||
style: 'cursor:pointer; margin:0 5px; vertical-align:middle; border-radius:3px',
|
||||
click: function () {
|
||||
callAuthor().then(function (repo) {
|
||||
let url = String(repo || '').trim();
|
||||
if (!url) return;
|
||||
if (url.indexOf('https') === -1)
|
||||
url = 'https://github.com/' + url;
|
||||
openExternal(url);
|
||||
});
|
||||
}
|
||||
}),
|
||||
E('img', {
|
||||
src: res + '/plugin.svg', alt: 'luci-app-amlogic',
|
||||
width: '160', height: '20',
|
||||
style: 'cursor:pointer; margin:0 5px; vertical-align:middle; border-radius:3px',
|
||||
click: function () { openExternal('https://github.com/ophub/luci-app-amlogic'); }
|
||||
}),
|
||||
langsImg
|
||||
])
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { width: '20%', align: 'right' }, _('Supported functions:')),
|
||||
E('td', { width: '80%', align: 'left' },
|
||||
_('Provide services such as install to EMMC, Update Firmware and Kernel, Backup and Recovery Config, Snapshot management, etc.'))
|
||||
]),
|
||||
E('tr', [
|
||||
E('td', { width: '20%', align: 'right', style: 'vertical-align:top; padding-top:4px' }, _('Supported Boxes:')),
|
||||
E('td', { width: '80%', align: 'left', style: 'line-height:1.8' }, boxRows)
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,156 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Install OpenWrt to EMMC
|
||||
//
|
||||
// Purpose: let the user select a device model (or enter dtb/soc/uboot manually),
|
||||
// then invoke start_install and poll the log tail until success or failure.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (model_database, start_install, read_log_tail).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require dom';
|
||||
'require poll';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// Read the model database (parsed by the backend from /usr/share/amlogic/model_database.txt).
|
||||
const callDB = rpc.declare({ object: 'luci.amlogic', method: 'model_database',
|
||||
expect: { entries: [] } });
|
||||
// Start install; parameter is the composed string "id@dtb:soc:uboot".
|
||||
const callStartInst = rpc.declare({ object: 'luci.amlogic', method: 'start_install',
|
||||
params: ['amlogic_install_sel'] });
|
||||
// Read the last line of the named log (here we use the 'install' log).
|
||||
const callLogTail = rpc.declare({ object: 'luci.amlogic', method: 'read_log_tail',
|
||||
params: ['name'], expect: { line: '' } });
|
||||
|
||||
// This page uses its own Install button; hide LuCI's default Save/Apply/Reset.
|
||||
return view.extend({
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
load: function () {
|
||||
return callDB();
|
||||
},
|
||||
|
||||
// Render the plugin info: logo, badge links, feature summary, and supported-box list.
|
||||
render: function (entries) {
|
||||
amlogicShared.ensureCss();
|
||||
// Model dropdown: 0 = placeholder, 99 = manual entry, others from database.
|
||||
const sel = E('select', { style: 'width:auto', name: 'amlogic_soc', id: 'amlogic_soc' });
|
||||
sel.appendChild(E('option', { value: '0' }, _('Select List')));
|
||||
(entries || []).forEach(function (e) {
|
||||
sel.appendChild(E('option', { value: e.id },
|
||||
'[ ' + e.id + ' ] ' + e.name));
|
||||
});
|
||||
sel.appendChild(E('option', { value: '99' }, _('Enter the dtb file name')));
|
||||
|
||||
// The three manual fields are only required when the user picks 99.
|
||||
const dtbInput = E('input', { type: 'text', class: 'cbi-input-text',
|
||||
style: 'width:235px', id: 'amlogic_dtb' });
|
||||
const socInput = E('input', { type: 'text', class: 'cbi-input-text',
|
||||
style: 'width:235px', id: 'amlogic_socname' });
|
||||
const ubootInput = E('input', { type: 'text', class: 'cbi-input-text',
|
||||
style: 'width:235px', id: 'amlogic_uboot' });
|
||||
|
||||
const trDtb = E('tr', { style: 'display:none' }, [
|
||||
E('td', { width: '30%', align: 'right' }, _('Enter the dtb file name:')),
|
||||
E('td', { width: '70%', align: 'left' }, dtbInput)
|
||||
]);
|
||||
const trSoc = E('tr', { style: 'display:none' }, [
|
||||
E('td', { width: '30%', align: 'right' }, _('Enter the soc name:')),
|
||||
E('td', { width: '70%', align: 'left' }, socInput)
|
||||
]);
|
||||
const trUboot = E('tr', { style: 'display:none' }, [
|
||||
E('td', { width: '30%', align: 'right' }, _('Enter the uboot_overload name:')),
|
||||
E('td', { width: '70%', align: 'left' }, ubootInput)
|
||||
]);
|
||||
|
||||
sel.addEventListener('change', function () {
|
||||
// On selection change, dynamically show/hide the three manual rows.
|
||||
const show = (sel.value === '99') ? '' : 'none';
|
||||
trDtb.style.display = show;
|
||||
trSoc.style.display = show;
|
||||
trUboot.style.display = show;
|
||||
});
|
||||
|
||||
// Status / log line shown next to the install button while it runs.
|
||||
// 'installing' tracks whether the background install is in progress.
|
||||
const logSpan = E('span');
|
||||
var installing = false;
|
||||
var installDone = false;
|
||||
const btn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Install'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
if (installing) return;
|
||||
const text = sel.options[sel.selectedIndex].text;
|
||||
// Confirm dialog; install proceeds only after user confirms.
|
||||
if (!confirm(_('You have chosen:') + ' ' + text + ', ' + _('Start install?')))
|
||||
return;
|
||||
// dtb falls back to auto_dtb when the manual field is blank.
|
||||
const dtbVal = dtbInput.value || 'auto_dtb';
|
||||
const socVal = socInput.value || '';
|
||||
const ubVal = ubootInput.value || '';
|
||||
// Compose the "id@dtb:soc:uboot" string the backend expects.
|
||||
const composed = sel.value + '@' + dtbVal + ':' + socVal + ':' + ubVal;
|
||||
const target = ev.currentTarget;
|
||||
target.disabled = true;
|
||||
target.value = _('Installing...');
|
||||
installing = true;
|
||||
installDone = false;
|
||||
// Fire the background install — result comes via log polling.
|
||||
return callStartInst(composed).catch(function () {
|
||||
target.value = _('Install Failed');
|
||||
target.disabled = false;
|
||||
installing = false;
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// Poll the install log once per second; detect success/failure keywords.
|
||||
poll.add(function () {
|
||||
return callLogTail('install').then(function (line) {
|
||||
if (!line || line === '\n') {
|
||||
dom.content(logSpan, '');
|
||||
return;
|
||||
}
|
||||
dom.content(logSpan, E('span', { class: 'amlogic-status-info' }, ' ' + line));
|
||||
if (installing && !installDone) {
|
||||
if (line.indexOf('Successfully') !== -1 || line.indexOf('successful') !== -1) {
|
||||
installDone = true;
|
||||
installing = false;
|
||||
btn.value = _('Successful Install');
|
||||
btn.disabled = false;
|
||||
dom.content(logSpan, E('span', { class: 'amlogic-status-ok' }, ' ' + line));
|
||||
} else if (line.indexOf('Failed') !== -1 || line.indexOf('failed') !== -1 ||
|
||||
line.indexOf('Error') !== -1 || line.indexOf('error') !== -1) {
|
||||
installDone = true;
|
||||
installing = false;
|
||||
btn.value = _('Install Failed');
|
||||
btn.disabled = false;
|
||||
dom.content(logSpan, E('span', { class: 'amlogic-status-err' }, ' ' + line));
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 1);
|
||||
|
||||
return E('div', {}, [
|
||||
E('h2', _('Install OpenWrt')),
|
||||
E('p', _('Install OpenWrt to EMMC, Please select the device model, Or enter the dtb file name.')),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('table', { class: 'amlogic-row-table' }, [
|
||||
E('tr', [
|
||||
E('td', { width: '35%', align: 'right' }, _('Select the device model:')),
|
||||
E('td', { width: '65%', align: 'left' }, sel)
|
||||
]),
|
||||
trDtb, trSoc, trUboot,
|
||||
E('tr', [
|
||||
E('td', { width: '30%', align: 'right' }, _('Install OpenWrt:')),
|
||||
E('td', { width: '70%', align: 'left' }, [btn, ' ', logSpan])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Service log viewer
|
||||
//
|
||||
// Purpose: read the full amlogic service log into a read-only textarea and
|
||||
// refresh it every 2 seconds; provide controls to pause/resume polling, clear
|
||||
// the log, and download the current contents as a plain-text file.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (read_log_full, del_log).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require poll';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// Read the full log content (entire text).
|
||||
const callLogFull = rpc.declare({ object: 'luci.amlogic', method: 'read_log_full',
|
||||
params: ['name'], expect: { content: '' } });
|
||||
// Clear the service main log.
|
||||
const callDelLog = rpc.declare({ object: 'luci.amlogic', method: 'del_log' });
|
||||
|
||||
// This page uses its own Start/Stop Refresh buttons and does not need the default Save/Apply/Reset buttons,
|
||||
// so we disable them by setting the handlers to null.
|
||||
return view.extend({
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
render: function () {
|
||||
amlogicShared.ensureCss();
|
||||
// Read-only, no-wrap, monospace log display area.
|
||||
const ta = E('textarea', {
|
||||
rows: '20', readonly: 'readonly', wrap: 'off',
|
||||
style: 'width:100%; font-family:monospace; white-space:pre'
|
||||
});
|
||||
// Status hint: Running / Stopped.
|
||||
const status = E('span', { class: 'amlogic-status-ok', style: 'margin-left:1em' });
|
||||
// Polling is enabled by default when the page opens.
|
||||
let polling = true;
|
||||
|
||||
// Refresh the full log and scroll to the bottom.
|
||||
function refresh() {
|
||||
return callLogFull('main').then(function (txt) {
|
||||
ta.value = txt || '';
|
||||
ta.scrollTop = ta.scrollHeight;
|
||||
});
|
||||
}
|
||||
const view = this;
|
||||
|
||||
const stopBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reset',
|
||||
value: _('Stop Refresh'),
|
||||
click: function () { polling = false; status.textContent = _('Stopped'); }
|
||||
});
|
||||
const startBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-apply',
|
||||
value: _('Start Refresh'),
|
||||
click: function () { polling = true; status.textContent = _('Running'); refresh(); }
|
||||
});
|
||||
const cleanBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-remove',
|
||||
value: _('Clean Log'),
|
||||
click: ui.createHandlerFn(view, function () {
|
||||
return callDelLog().then(refresh);
|
||||
})
|
||||
});
|
||||
const dlBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-save',
|
||||
value: _('Download Log'),
|
||||
click: function () {
|
||||
// Wrap textarea content in a Blob and trigger a browser download via a temporary <a>.
|
||||
const blob = new Blob([ta.value || ''], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'amlogic_service.log';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
});
|
||||
|
||||
// Polling: only refresh while polling is true; runs once every 2s.
|
||||
poll.add(function () { if (polling) return refresh(); }, 2);
|
||||
refresh();
|
||||
status.textContent = _('Running');
|
||||
|
||||
return E('div', {}, [
|
||||
E('h2', _('Service Log')),
|
||||
E('p', _('Display the log of plug-in service operation.')),
|
||||
E('div', { class: 'cbi-section' }, [ta]),
|
||||
E('div', { class: 'cbi-page-actions' },
|
||||
[stopBtn, ' ', startBtn, ' ', cleanBtn, ' ', dlBtn, status])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Safe Power Off page
|
||||
//
|
||||
// Purpose: call the backend poweroff RPC then run a 5-second on-page countdown;
|
||||
// switch status to "Powered off" and show a power-off icon when done.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (poweroff).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// Trigger the backend poweroff RPC.
|
||||
const callPoweroff = rpc.declare({ object: 'luci.amlogic', method: 'poweroff' });
|
||||
|
||||
// This page uses its own PowerOff button and does not need the default Save/Apply/Reset buttons,
|
||||
// so we disable them by setting the handlers to null.
|
||||
return view.extend({
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
render: function () {
|
||||
amlogicShared.ensureCss();
|
||||
// Status text; amlogic-status-err color adapts to dark mode automatically.
|
||||
const status = E('span', { class: 'amlogic-status-err', style: 'margin-left:1em' });
|
||||
// Power-off icon, hidden by default; shown only after the countdown ends.
|
||||
const icon = E('img', {
|
||||
src: L.resource('amlogic/poweroff.png'),
|
||||
width: '32', height: '32',
|
||||
style: 'width:32px; height:32px; max-width:32px; display:none; margin-left:1em; vertical-align:middle'
|
||||
});
|
||||
|
||||
// PowerOff button click handler: confirm twice, then call RPC and start countdown on success.
|
||||
const btn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-remove',
|
||||
value: _('Perform PowerOff'),
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
// Confirm twice to avoid accidental clicks.
|
||||
if (!confirm(_('Are you sure you want to power off the device?')))
|
||||
return;
|
||||
ev.currentTarget.disabled = true;
|
||||
return callPoweroff().then(function () {
|
||||
// Show a 5-second countdown; replace with "Powered off" and icon when done.
|
||||
status.textContent = _('Powering off, please wait...');
|
||||
let n = 5;
|
||||
const t = setInterval(function () {
|
||||
if (--n <= 0) {
|
||||
clearInterval(t);
|
||||
status.textContent = _('Powered off.');
|
||||
icon.style.display = 'inline-block';
|
||||
} else {
|
||||
status.textContent = _('Powering off, please wait...') + ' ' + n;
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
return E('div', {}, [
|
||||
E('h2', _('Power Off')),
|
||||
E('p', _('Click the button below to safely power off the device.')),
|
||||
E('div', { class: 'cbi-section' }, [btn, status, icon])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Shared helper module for all luci-app-amlogic JS views.
|
||||
//
|
||||
// Purpose: inject the theme-aware stylesheet amlogic.css into <head> on first
|
||||
// page load so every view can use .amlogic-status-* / .amlogic-snap-* classes
|
||||
// and prefers-color-scheme dark mode overrides without duplicating inline styles.
|
||||
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
|
||||
return baseclass.extend({
|
||||
// Inject amlogic.css once; guarded by element id to prevent duplicates.
|
||||
ensureCss: function () {
|
||||
if (document.getElementById('amlogic-shared-css')) return;
|
||||
const link = document.createElement('link');
|
||||
link.id = 'amlogic-shared-css';
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = L.resource('view/amlogic/amlogic.css');
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,298 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Manually Upload Update
|
||||
//
|
||||
// Purpose: upload local files (firmware / kernel / ipk / config backup) to the server,
|
||||
// list them, and trigger install / restore / update actions. Chunked upload uses
|
||||
// a custom ubus RPC instead of cgi-upload to avoid session ACL issues.
|
||||
// Backend RPC: /usr/share/rpcd/ucode/luci.amlogic (upload_path, list_uploads, upload_chunk,
|
||||
// delete_upload, install_upload, start_update, start_kernel, read_log_tail, state).
|
||||
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require dom';
|
||||
'require poll';
|
||||
'require fs';
|
||||
'require view.amlogic.shared as amlogicShared';
|
||||
|
||||
// Query the upload directory the backend allows (default /tmp/upload/, the backend ensures it exists).
|
||||
const callUploadPath = rpc.declare({ object: 'luci.amlogic', method: 'upload_path', expect: { path: '/tmp/upload/' } });
|
||||
// List files in the upload dir plus flags about installable firmware/kernel/cfg.
|
||||
const callList = rpc.declare({ object: 'luci.amlogic', method: 'list_uploads' });
|
||||
// Delete a named file in the upload dir.
|
||||
const callDelete = rpc.declare({ object: 'luci.amlogic', method: 'delete_upload', params: ['name'] });
|
||||
// Install one uploaded file (ipk) or restore a config backup; the backend dispatches by suffix.
|
||||
const callInstall = rpc.declare({ object: 'luci.amlogic', method: 'install_upload', params: ['name'] });
|
||||
// Chunked upload via our own RPC (sidesteps cgi-upload's session ACL flow).
|
||||
const callUploadChunk = rpc.declare({ object: 'luci.amlogic', method: 'upload_chunk', params: ['name', 'data', 'append'] });
|
||||
// Run the full firmware update flow with parameters auto@updated@/tmp.
|
||||
const callStartUpd = rpc.declare({ object: 'luci.amlogic', method: 'start_update', params: ['amlogic_update_sel'] });
|
||||
// Run the kernel-only replace flow.
|
||||
const callStartKnl = rpc.declare({ object: 'luci.amlogic', method: 'start_kernel' });
|
||||
// Tail a named log to surface progress during install/update.
|
||||
const callLogTail = rpc.declare({ object: 'luci.amlogic', method: 'read_log_tail', params: ['name'], expect: { line: '' } });
|
||||
// Read system state (this page only uses current_firmware_version).
|
||||
const callState = rpc.declare({ object: 'luci.amlogic', method: 'state' });
|
||||
|
||||
// This page uses its own Upload/Install buttons and does not need the default Save/Apply/Reset buttons,
|
||||
// so we disable them by setting the handlers to null.
|
||||
return view.extend({
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
|
||||
// On load, ensure the CSS is injected and load the upload path from the backend.
|
||||
load: function () {
|
||||
amlogicShared.ensureCss();
|
||||
return callUploadPath();
|
||||
},
|
||||
|
||||
// Render the upload page: file input + upload button, table of existing uploads with Remove + Install/Restore buttons,
|
||||
// and firmware/kernel update buttons if applicable. Also start polling the install/update log tails.
|
||||
render: function (path) {
|
||||
const view = this;
|
||||
|
||||
const tableContainer = E('div');
|
||||
const descSpan = E('span', { class: 'amlogic-status-ok', style: 'font-weight:bold' });
|
||||
const fwBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Update OpenWrt firmware'), style: 'display:none',
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
return doButton(ev.currentTarget,
|
||||
_('Updating...'), _('Update Failed'),
|
||||
function () { return callStartUpd('auto@updated@/tmp'); });
|
||||
})
|
||||
});
|
||||
const knBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-reload',
|
||||
value: _('Replace OpenWrt Kernel'), style: 'display:none; margin-left:5px',
|
||||
click: ui.createHandlerFn(this, function (ev) {
|
||||
return doButton(ev.currentTarget,
|
||||
_('Updating...'), _('Update Failed'),
|
||||
function () { return callStartKnl(); });
|
||||
})
|
||||
});
|
||||
const fwLog = E('span');
|
||||
const knLog = E('span');
|
||||
const fwVer = E('span', _('Collecting data...'));
|
||||
|
||||
function doButton(btn, busyText, fail, fn) {
|
||||
// Shared button state machine: disabled → RPC → busyText or fail label.
|
||||
// On success (code==0) the button stays in busyText state (device rebooting);
|
||||
// on failure, re-enable so the user can retry.
|
||||
btn.disabled = true; btn.value = busyText;
|
||||
return Promise.resolve(fn()).then(function (r) {
|
||||
if (!r || r.code !== 0) {
|
||||
btn.value = fail;
|
||||
btn.disabled = false;
|
||||
}
|
||||
// code == 0 → keep busyText + disabled (update in progress)
|
||||
}).catch(function () {
|
||||
btn.value = fail;
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function refreshList() {
|
||||
// Refresh the file table; show/hide firmware/kernel update buttons
|
||||
// based on has_firmware / has_kernel flags from the backend.
|
||||
return callList().then(function (info) {
|
||||
dom.content(tableContainer, buildTable(info.items || [], info.path));
|
||||
let parts = [];
|
||||
if (info.has_config)
|
||||
parts.push(_('There are config file in the upload directory, and you can restore the config. '));
|
||||
if (info.has_kernel)
|
||||
parts.push(_('There are kernel files in the upload directory, and you can replace the kernel.'));
|
||||
if (info.has_firmware)
|
||||
parts.push(_('There are openwrt firmware file in the upload directory, and you can update the openwrt.'));
|
||||
descSpan.textContent = parts.length ? ' Tip: ' + parts.join('') : '';
|
||||
fwBtn.style.display = info.has_firmware ? '' : 'none';
|
||||
knBtn.style.display = info.has_kernel ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Helper to build the file table with Remove + Install/Restore buttons per row.
|
||||
function buildTable(items, dir) {
|
||||
// File table: expose Remove + Install (ipk) / Restore (config backup) per row.
|
||||
const tbl = E('table', { class: 'table cbi-section-table' }, [
|
||||
E('tr', { class: 'tr cbi-section-table-titles' }, [
|
||||
E('th', { class: 'th' }, _('File name')),
|
||||
E('th', { class: 'th' }, _('Modify time')),
|
||||
E('th', { class: 'th' }, _('Attributes')),
|
||||
E('th', { class: 'th' }, _('Size')),
|
||||
E('th', { class: 'th' }, _('Remove')),
|
||||
E('th', { class: 'th' }, _('Install'))
|
||||
])
|
||||
]);
|
||||
items.forEach(function (it) {
|
||||
const removeBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-remove',
|
||||
value: _('Remove'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
return callDelete(it.name).then(refreshList);
|
||||
})
|
||||
});
|
||||
let installCell;
|
||||
if (it.ipk) {
|
||||
// ipk: "Install" calls install_upload to install one package.
|
||||
installCell = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-apply',
|
||||
value: _('Install'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
return callInstall(it.name).then(function (r) {
|
||||
if (r && r.code !== 0 && r.output)
|
||||
ui.addNotification(null,
|
||||
E('p', { class: 'amlogic-status-err' }, [
|
||||
r.output, E('br'),
|
||||
E('b', _('Please refresh the page to see the changes.'))
|
||||
]));
|
||||
else if (r && r.code === 0)
|
||||
ui.addNotification(null,
|
||||
E('p', { class: 'amlogic-status-ok' },
|
||||
_('Package installed successfully. Please refresh the page to see the changes.')));
|
||||
return refreshList();
|
||||
});
|
||||
})
|
||||
});
|
||||
} else if (it.cfg) {
|
||||
// Config backup (openwrt-config.tar.gz): "Restore" makes the
|
||||
// backend roll the config back and reboot.
|
||||
installCell = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-apply',
|
||||
value: _('Restore'),
|
||||
click: ui.createHandlerFn(view, function (ev) {
|
||||
ev.currentTarget.disabled = true;
|
||||
ui.addNotification(null,
|
||||
E('p', { class: 'amlogic-status-ok' },
|
||||
_('Tip: The config is being restored, and it will automatically restart after completion.')));
|
||||
return callInstall(it.name);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
installCell = E('span', '-');
|
||||
}
|
||||
tbl.appendChild(E('tr', { class: 'tr cbi-rowstyle-1' }, [
|
||||
E('td', { class: 'td' }, it.name),
|
||||
E('td', { class: 'td' }, it.mtime),
|
||||
E('td', { class: 'td' }, it.modestr),
|
||||
E('td', { class: 'td' }, it.size),
|
||||
E('td', { class: 'td' }, removeBtn),
|
||||
E('td', { class: 'td' }, installCell)
|
||||
]));
|
||||
});
|
||||
if (!items.length)
|
||||
tbl.appendChild(E('tr', [E('td', { class: 'td', colspan: '6' }, _('No specify upload file.'))]));
|
||||
return tbl;
|
||||
}
|
||||
|
||||
// Chunked file upload via luci.amlogic.upload_chunk RPC (avoids cgi-upload session ACL).
|
||||
// 32 KB raw per chunk → ~44 KB base64, safely under the ubus per-request size limit.
|
||||
const CHUNK_SIZE = 32 * 1024;
|
||||
const uploadStatus = E('span');
|
||||
const uploadFileName = E('span', { style: 'margin-right:1em' });
|
||||
|
||||
// Encode a Uint8Array as base64 in 32 KB slices to avoid call-stack limits.
|
||||
function bytesToB64(buf) {
|
||||
let s = '';
|
||||
for (let i = 0; i < buf.length; i += 0x8000)
|
||||
s += String.fromCharCode.apply(null, buf.subarray(i, i + 0x8000));
|
||||
return btoa(s);
|
||||
}
|
||||
|
||||
// Upload a file in chunks; on success, refresh the file list to show the new upload.
|
||||
function uploadFile(file, target, basename) {
|
||||
const total = file.size;
|
||||
let offset = 0;
|
||||
let first = true;
|
||||
function step() {
|
||||
if (offset >= total) {
|
||||
uploadStatus.textContent = _('File saved to') + ' ' + target;
|
||||
return refreshList();
|
||||
}
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
return slice.arrayBuffer().then(function (buf) {
|
||||
const b64 = bytesToB64(new Uint8Array(buf));
|
||||
const append = !first;
|
||||
first = false;
|
||||
return callUploadChunk(basename, b64, append).then(function (r) {
|
||||
if (!r || !r.ok)
|
||||
throw new Error((r && r.error) || _('upload chunk failed'));
|
||||
offset += CHUNK_SIZE;
|
||||
const pct = Math.min(100, (offset / total) * 100);
|
||||
uploadStatus.textContent = _('Uploading file…') +
|
||||
' ' + pct.toFixed(1) + '%';
|
||||
return step();
|
||||
});
|
||||
});
|
||||
}
|
||||
return step();
|
||||
}
|
||||
|
||||
// Hidden file input to trigger the file picker dialog; on file selection, start the chunked upload.
|
||||
const hiddenFileInput = E('input', {
|
||||
type: 'file', style: 'display:none',
|
||||
change: ui.createHandlerFn(this, function (ev) {
|
||||
const file = ev.target.files && ev.target.files[0];
|
||||
if (!file) return;
|
||||
const basename = file.name.replace(/^.*[\\\/]/, '');
|
||||
const target = path.replace(/\/+$/, '') + '/' + basename;
|
||||
uploadFileName.textContent = basename;
|
||||
uploadStatus.textContent = _('Uploading file…') + ' 0%';
|
||||
return uploadFile(file, target, basename).catch(function (e) {
|
||||
uploadStatus.textContent = _('Create upload file error.') +
|
||||
' ' + (e && e.message ? e.message : e);
|
||||
}).finally(function () {
|
||||
hiddenFileInput.value = '';
|
||||
});
|
||||
})
|
||||
});
|
||||
const uploadBtn = E('input', {
|
||||
type: 'button', class: 'cbi-button cbi-button-apply',
|
||||
value: _('Upload file...'),
|
||||
click: function () { hiddenFileInput.click(); }
|
||||
});
|
||||
|
||||
// Polling: pull firmware and kernel log tails once per second.
|
||||
poll.add(function () {
|
||||
return Promise.all([
|
||||
callLogTail('firmware').then(function (l) {
|
||||
dom.content(fwLog, l && l !== '\n'
|
||||
? E('span', { class: 'amlogic-status-info' }, ' ' + l) : '');
|
||||
}),
|
||||
callLogTail('kernel').then(function (l) {
|
||||
dom.content(knLog, l && l !== '\n'
|
||||
? E('span', { class: 'amlogic-status-info' }, ' ' + l) : '');
|
||||
})
|
||||
]);
|
||||
}, 1);
|
||||
callState().then(function (s) {
|
||||
dom.content(fwVer, s.current_firmware_version
|
||||
? E('span', { class: 'amlogic-status-ok' }, _('Current Version') + ' [ ' + s.current_firmware_version + ' ] ')
|
||||
: E('span', { class: 'amlogic-status-err' }, _('Invalid value.')));
|
||||
});
|
||||
refreshList();
|
||||
|
||||
return E('div', {}, [
|
||||
E('h2', _('Upload')),
|
||||
E('p', _('Update plugins first, then update the kernel or firmware.')),
|
||||
E('p', _('After uploading [Firmware], [Kernel], [IPK] or [Backup Config], the operation buttons will be displayed.')),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('label', { style: 'display:inline-block;width:150px' },
|
||||
_('Choose local file:')),
|
||||
uploadBtn, ' ', uploadFileName, hiddenFileInput,
|
||||
E('br'), uploadStatus
|
||||
]),
|
||||
E('h3', _('Upload file list')),
|
||||
E('div', { class: 'cbi-section' }, [descSpan, tableContainer]),
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
E('p', { style: 'text-align:center' },
|
||||
_('After uploading firmware (.img/.img.gz/.img.xz/.7z suffix) or kernel files (3 kernel files), the update button will be displayed.')),
|
||||
// Single centered cell: when fwBtn / knBtn are hidden the row would otherwise show an empty left column.
|
||||
E('div', { style: 'text-align:center' },
|
||||
[fwBtn, knBtn, ' ', fwVer, ' ', fwLog, knLog])
|
||||
])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,654 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: https://github.com/ophub/luci-app-amlogic\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru_RU\n"
|
||||
"X-Generator: Poedit 2.3.1\n"
|
||||
|
||||
msgid "Choose local file:"
|
||||
msgstr "Выберите локальный файл:"
|
||||
|
||||
msgid "Couldn't open file:"
|
||||
msgstr "Не удалось открыть файл:"
|
||||
|
||||
msgid "The file Will download automatically."
|
||||
msgstr "Файл будет загружен автоматически."
|
||||
|
||||
msgid "Create upload file error."
|
||||
msgstr "Ошибка создания загружаемого файла."
|
||||
|
||||
msgid "Download"
|
||||
msgstr "Скачать"
|
||||
|
||||
msgid "Download file"
|
||||
msgstr "Скачать файл"
|
||||
|
||||
msgid "File name"
|
||||
msgstr "Имя файла"
|
||||
|
||||
msgid "File saved to"
|
||||
msgstr "Файл сохранён в"
|
||||
|
||||
msgid "FileTransfer"
|
||||
msgstr "Передача файлов"
|
||||
|
||||
msgid "Install"
|
||||
msgstr "Установить"
|
||||
|
||||
msgid "Attributes"
|
||||
msgstr "Атрибуты"
|
||||
|
||||
msgid "Modify time"
|
||||
msgstr "Время изменения"
|
||||
|
||||
msgid "No specify upload file."
|
||||
msgstr "Не указан файл для загрузки."
|
||||
|
||||
msgid "Path on Route:"
|
||||
msgstr "Путь на маршрутизаторе:"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Удалить"
|
||||
|
||||
msgid "Size"
|
||||
msgstr "Размер"
|
||||
|
||||
msgid "Upload"
|
||||
msgstr "Загрузить"
|
||||
|
||||
msgid "Upload file list"
|
||||
msgstr "Список загруженных файлов"
|
||||
|
||||
msgid "There are config file in the upload directory, and you can restore the config. "
|
||||
msgstr "В каталоге загрузки есть файл конфигурации — вы можете восстановить настройки."
|
||||
|
||||
msgid "There are kernel files in the upload directory, and you can replace the kernel."
|
||||
msgstr "В каталоге загрузки находятся файлы ядра — вы можете заменить ядро."
|
||||
|
||||
msgid "There are openwrt firmware file in the upload directory, and you can update the openwrt."
|
||||
msgstr "В каталоге загрузки есть файл прошивки OpenWrt — вы можете обновить OpenWrt."
|
||||
|
||||
msgid "Update plugins first, then update the kernel or firmware."
|
||||
msgstr "Сначала обновите плагины, затем ядро или прошивку."
|
||||
|
||||
msgid "After uploading [Firmware], [Kernel], [IPK] or [Backup Config], the operation buttons will be displayed."
|
||||
msgstr "После загрузки [прошивки], [ядра], [IPK-пакета] или [резервной конфигурации] появятся соответствующие кнопки управления."
|
||||
|
||||
msgid "After uploading firmware (.img/.img.gz/.img.xz/.7z suffix) or kernel files (3 kernel files), the update button will be displayed."
|
||||
msgstr "После загрузки файлов прошивки (с расширениями .img/.img.gz/.img.xz/.7z) или файлов ядра (3 файла), появится кнопка обновления."
|
||||
|
||||
msgid "Amlogic Service"
|
||||
msgstr "Сервис Amlogic"
|
||||
|
||||
msgid "Restore Config / Replace OpenWrt Kernel"
|
||||
msgstr "Восстановить конфигурацию / Заменить ядро OpenWrt"
|
||||
|
||||
msgid "Manually Upload Update"
|
||||
msgstr "Обновление вручную"
|
||||
|
||||
msgid "Install OpenWrt"
|
||||
msgstr "Установить OpenWrt"
|
||||
|
||||
msgid "Select the device model:"
|
||||
msgstr "Выберите модель устройства:"
|
||||
|
||||
msgid "Select List"
|
||||
msgstr "Выбрать из списка"
|
||||
|
||||
msgid "Enter the dtb file name"
|
||||
msgstr "Введите имя файла DTB"
|
||||
|
||||
msgid "Enter the soc name:"
|
||||
msgstr "Введите имя SoC:"
|
||||
|
||||
msgid "Enter the uboot_overload name:"
|
||||
msgstr "Введите имя uboot_overload:"
|
||||
|
||||
msgid "Invalid value."
|
||||
msgstr "Недопустимое значение."
|
||||
|
||||
msgid "Install OpenWrt:"
|
||||
msgstr "Установка OpenWrt:"
|
||||
|
||||
msgid "Install OpenWrt to EMMC, Please select the device model, Or enter the dtb file name."
|
||||
msgstr "Установка OpenWrt на eMMC. Пожалуйста, выберите модель устройства или введите имя файла DTB."
|
||||
|
||||
msgid "Tip: Writing is in progress, and it will automatically restart after completion."
|
||||
msgstr "Подсказка: идёт запись — после завершения устройство автоматически перезагрузится."
|
||||
|
||||
msgid "You have chosen:"
|
||||
msgstr "Вы выбрали:"
|
||||
|
||||
msgid "Start install?"
|
||||
msgstr "Начать установку?"
|
||||
|
||||
msgid "Installing..."
|
||||
msgstr "Установка..."
|
||||
|
||||
msgid "Install Failed"
|
||||
msgstr "Установка не удалась"
|
||||
|
||||
msgid "Successful Install"
|
||||
msgstr "Установка завершена успешно"
|
||||
|
||||
msgid "Update"
|
||||
msgstr "Обновить"
|
||||
|
||||
msgid "Updating..."
|
||||
msgstr "Обновление..."
|
||||
|
||||
msgid "Update Failed"
|
||||
msgstr "Обновление не удалось"
|
||||
|
||||
msgid "Successful Update"
|
||||
msgstr "Обновление завершено успешно"
|
||||
|
||||
msgid "Update OpenWrt firmware"
|
||||
msgstr "Обновить прошивку OpenWrt"
|
||||
|
||||
msgid "kernel"
|
||||
msgstr "ядро"
|
||||
|
||||
msgid "Replace OpenWrt Kernel"
|
||||
msgstr "Заменить ядро OpenWrt"
|
||||
|
||||
msgid "Tip: The kernel is being replaced, and it will automatically restart after completion."
|
||||
msgstr "Подсказка: идёт замена ядра — после завершения устройство автоматически перезагрузится."
|
||||
|
||||
msgid "Supports management of Amlogic s9xxx, Allwinner (V-Plus Cloud), and Rockchip (BeikeYun, Chainedbox L1 Pro) boxes."
|
||||
msgstr "Поддерживает управление устройствами на базе Amlogic s9xxx, Allwinner (V-Plus Cloud) и Rockchip (BeikeYun, Chainedbox L1 Pro)."
|
||||
|
||||
msgid "Supported functions:"
|
||||
msgstr "Поддерживаемые функции:"
|
||||
|
||||
msgid "Provide services such as install to EMMC, Update Firmware and Kernel, Backup and Recovery Config, Snapshot management, etc."
|
||||
msgstr "Позволяет устанавливать OpenWrt на eMMC, обновлять прошивку и ядро, выполнять резервное копирование и восстановление конфигурации, а также управлять снапшотами (мгновенными снимками системы)."
|
||||
|
||||
msgid "Supported Boxes:"
|
||||
msgstr "Поддерживаемые устройства:"
|
||||
|
||||
msgid "Amlogic s922x --- [ Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000 ]"
|
||||
msgstr "Amlogic s922x — [Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000]"
|
||||
|
||||
msgid "Amlogic s905x3 -- [ X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air ]"
|
||||
msgstr "Amlogic s905x3 — [X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air]"
|
||||
|
||||
msgid "Amlogic s905x2 -- [ X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2 ]"
|
||||
msgstr "Amlogic s905x2 — [X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2]"
|
||||
|
||||
msgid "Amlogic s912 ---- [ H96-Pro-Plus, Octopus-Planet, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro ]"
|
||||
msgstr "Amlogic s912 — [H96-Pro-Plus, Octopus-Planet, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro]"
|
||||
|
||||
msgid "Amlogic s905x --- [ HG680P, B860H, TBee, T95, TX9, XiaoMI-3S, X96 ]"
|
||||
msgstr "Amlogic s905x — [HG680P, B860H, TBee, T95, TX9, XiaoMI-3S, X96]"
|
||||
|
||||
msgid "Amlogic s905w --- [ X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K ]"
|
||||
msgstr "Amlogic s905w — [X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K]"
|
||||
|
||||
msgid "Amlogic s905d --- [ Phicomm-N1, MECOOL-KI-Pro, SML-5442TW ]"
|
||||
msgstr "Amlogic s905d — [Phicomm-N1, MECOOL-KI-Pro, SML-5442TW]"
|
||||
|
||||
msgid "Amlogic s905l --- [ UNT402A, M201-S ]"
|
||||
msgstr "Amlogic s905l — [UNT402A, M201-S]"
|
||||
|
||||
msgid "Amlogic s905l2 -- [ MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E ]"
|
||||
msgstr "Amlogic s905l2 — [MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E]"
|
||||
|
||||
msgid "Amlogic s905l3 -- [ CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310 ]"
|
||||
msgstr "Amlogic s905l3 — [CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310]"
|
||||
|
||||
msgid "Amlogic s905l3a - [ E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H ]"
|
||||
msgstr "Amlogic s905l3a — [E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H]"
|
||||
|
||||
msgid "Amlogic s905l3b - [ CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A ]"
|
||||
msgstr "Amlogic s905l3b — [CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A]"
|
||||
|
||||
msgid "Amlogic s905 ---- [ Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5 ]"
|
||||
msgstr "Amlogic s905 — [Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5]"
|
||||
|
||||
msgid "Allwinner H6 ---- [ V-Plus Cloud ]"
|
||||
msgstr "Allwinner H6 — [V-Plus Cloud]"
|
||||
|
||||
msgid "Rockchip -------- [ BeikeYun, L1-Pro, FastRhino R66S/R68S, Radxa 5B/E25 ]"
|
||||
msgstr "Rockchip — [BeikeYun, L1-Pro, FastRhino R66S/R68S, Radxa 5B/E25]"
|
||||
|
||||
msgid "Used in KVM ----- [ Can be used in KVM virtual machine of Armbian system. ]"
|
||||
msgstr "Используется в KVM — [можно использовать в виртуальной машине KVM на базе Armbian]."
|
||||
|
||||
msgid "KVM dual system switching"
|
||||
msgstr "Переключение между двумя системами в KVM"
|
||||
|
||||
msgid "You can freely switch between KVM dual partitions, using OpenWrt systems in different partitions."
|
||||
msgstr "Вы можете свободно переключаться между двумя разделами KVM, используя OpenWrt из разных разделов."
|
||||
|
||||
msgid "Switch System"
|
||||
msgstr "Переключить систему"
|
||||
|
||||
msgid "System is switching..."
|
||||
msgstr "Переключение системы..."
|
||||
|
||||
msgid "Waiting for system switching..."
|
||||
msgstr "Ожидание завершения переключения..."
|
||||
|
||||
msgid "System switchover succeeded, restarting..."
|
||||
msgstr "Переключение успешно, перезагрузка..."
|
||||
|
||||
msgid "Are you sure you want to switch systems?"
|
||||
msgstr "Вы уверены, что хотите переключить систему?"
|
||||
|
||||
msgid "Install Ipk"
|
||||
msgstr "Установить IPK"
|
||||
|
||||
msgid "Plugin Settings"
|
||||
msgstr "Настройки плагина"
|
||||
|
||||
msgid "Backup Firmware Config"
|
||||
msgstr "Резервное копирование конфигурации"
|
||||
|
||||
msgid "Backup Config:"
|
||||
msgstr "Резервная копия конфигурации:"
|
||||
|
||||
msgid "Download Backup"
|
||||
msgstr "Скачать резервную копию"
|
||||
|
||||
msgid "Backup OpenWrt config (openwrt_config.tar.gz). Use this file to restore the config in [Manually Upload Update]."
|
||||
msgstr "Создать резервную копию конфигурации OpenWrt (openwrt_config.tar.gz). Используйте этот файл для восстановления в разделе [Обновление вручную]."
|
||||
|
||||
msgid "Edit List:"
|
||||
msgstr "Редактировать список:"
|
||||
|
||||
msgid "Open List"
|
||||
msgstr "Открыть список"
|
||||
|
||||
msgid "Backup Configuration - Custom List"
|
||||
msgstr "Резервная копия — пользовательский список"
|
||||
|
||||
msgid "Write one configuration item per line, and directories should end with a /."
|
||||
msgstr "По одному элементу на строку. Имена каталогов должны оканчиваться на «/»."
|
||||
|
||||
msgid "Restore Backup:"
|
||||
msgstr "Восстановить из резервной копии:"
|
||||
|
||||
msgid "Upload Backup"
|
||||
msgstr "Загрузить резервную копию"
|
||||
|
||||
msgid "Restore"
|
||||
msgstr "Восстановить"
|
||||
|
||||
msgid "Restore Config"
|
||||
msgstr "Восстановить конфигурацию"
|
||||
|
||||
msgid "Restore configuration"
|
||||
msgstr "Восстановление конфигурации"
|
||||
|
||||
msgid "Config File"
|
||||
msgstr "Файл конфигурации"
|
||||
|
||||
msgid "Tip: The config is being restored, and it will automatically restart after completion."
|
||||
msgstr "Подсказка: идёт восстановление конфигурации — после завершения устройство автоматически перезагрузится."
|
||||
|
||||
msgid "Snapshot Management"
|
||||
msgstr "Управление снапшотами"
|
||||
|
||||
msgid "Create Snapshot"
|
||||
msgstr "Создать снапшот"
|
||||
|
||||
msgid "Creating..."
|
||||
msgstr "Создание..."
|
||||
|
||||
msgid "Created successfully"
|
||||
msgstr "Создано успешно"
|
||||
|
||||
msgid "Creation failed"
|
||||
msgstr "Ошибка создания"
|
||||
|
||||
msgid "Initialize Snapshot"
|
||||
msgstr "Инициализировать снапшот"
|
||||
|
||||
msgid "Update Snapshot"
|
||||
msgstr "Обновить снапшот"
|
||||
|
||||
msgid "Restore Snap"
|
||||
msgstr "Восстановить снапшот"
|
||||
|
||||
msgid "Restoring..."
|
||||
msgstr "Восстановление..."
|
||||
|
||||
msgid "Restore Failed"
|
||||
msgstr "Восстановление не удалось"
|
||||
|
||||
msgid "Successfully Restored"
|
||||
msgstr "Восстановлено успешно"
|
||||
|
||||
msgid "Delete Snap"
|
||||
msgstr "Удалить снапшот"
|
||||
|
||||
msgid "You selected a snapshot:"
|
||||
msgstr "Вы выбрали снапшот:"
|
||||
|
||||
msgid "Confirm delete?"
|
||||
msgstr "Подтвердить удаление?"
|
||||
|
||||
msgid "Confirm recovery and restart OpenWrt?"
|
||||
msgstr "Подтвердить восстановление и перезагрузку OpenWrt?"
|
||||
|
||||
msgid "Delete Failed"
|
||||
msgstr "Удаление не удалось"
|
||||
|
||||
msgid "Successfully Deleted"
|
||||
msgstr "Удалено успешно"
|
||||
|
||||
msgid "Create a snapshot of the current system configuration, or restore to a snapshot."
|
||||
msgstr "Создать снапшот текущей конфигурации системы или восстановиться из снапшота."
|
||||
|
||||
msgid "Currently OpenWrt does not support the snapshot function."
|
||||
msgstr "В текущей версии OpenWrt функция снапшотов не поддерживается."
|
||||
|
||||
msgid "Please use this plugin to reinstall or upgrade OpenWrt to enable the snapshot function."
|
||||
msgstr "Пожалуйста, используйте этот плагин для переустановки или обновления OpenWrt, чтобы включить поддержку снапшотов."
|
||||
|
||||
msgid "Deleting..."
|
||||
msgstr "Удаление..."
|
||||
|
||||
msgid "Online Download Update"
|
||||
msgstr "Обновление через интернет"
|
||||
|
||||
msgid "Config Source"
|
||||
msgstr "Источник конфигурации"
|
||||
|
||||
msgid "You can customize the github.com download repository of OpenWrt files and kernels in [Online Download Update]."
|
||||
msgstr "Вы можете настроить репозиторий на github.com для загрузки файлов OpenWrt и ядер в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "Tip: The same files as the current OpenWrt system's BOARD (such as rock5b) and kernel (such as 5.10) will be downloaded."
|
||||
msgstr "Подсказка: будут загружены файлы, соответствующие текущей платформе (например, rock5b) и версии ядра (например, 5.10)."
|
||||
|
||||
msgid "OpenWrt download repository:"
|
||||
msgstr "Репозиторий OpenWrt:"
|
||||
|
||||
msgid "Set the OpenWrt files download repository on github.com in [Online Download Update]."
|
||||
msgstr "Укажите репозиторий на github.com для загрузки файлов OpenWrt в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "OpenWrt download tags keyword:"
|
||||
msgstr "Ключевое слово тега OpenWrt:"
|
||||
|
||||
msgid "Set the OpenWrt files download tags keyword for github.com in [Online Download Update]."
|
||||
msgstr "Укажите ключевое слово тега для загрузки файлов OpenWrt на github.com в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "OpenWrt files suffix:"
|
||||
msgstr "Расширение файлов OpenWrt:"
|
||||
|
||||
msgid "Set the OpenWrt files download suffix for github.com in [Online Download Update]."
|
||||
msgstr "Укажите расширение файлов OpenWrt, загружаемых с github.com, в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "Kernel download repository:"
|
||||
msgstr "Репозиторий ядра:"
|
||||
|
||||
msgid "Set the kernel files download repository on github.com in [Online Download Update]."
|
||||
msgstr "Укажите репозиторий на github.com для загрузки файлов ядра в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "Kernel download tags:"
|
||||
msgstr "Теги ядра:"
|
||||
|
||||
msgid "Set the kernel files download tags on github.com in [Online Download Update]."
|
||||
msgstr "Укажите теги для загрузки файлов ядра на github.com в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "kernel_rk3588 [Rockchip RK3588 Kernel]"
|
||||
msgstr "kernel_rk3588 [Ядро для Rockchip RK3588]"
|
||||
|
||||
msgid "kernel_rk35xx [Rockchip RK35xx Kernel]"
|
||||
msgstr "kernel_rk35xx [Ядро для Rockchip RK35xx]"
|
||||
|
||||
msgid "kernel_stable [Mainline Stable Kernel]"
|
||||
msgstr "kernel_stable [Стабильное ядро из mainline]"
|
||||
|
||||
msgid "kernel_flippy [Mainline Stable Kernel by Flippy]"
|
||||
msgstr "kernel_flippy [Стабильное ядро от Flippy]"
|
||||
|
||||
msgid "kernel_beta [Beta Kernel]"
|
||||
msgstr "kernel_beta [Бета-версия ядра]"
|
||||
|
||||
msgid "Set version branch:"
|
||||
msgstr "Ветка версии:"
|
||||
|
||||
msgid "Set the version branch of the OpenWrt files and kernel selected in [Online Download Update]."
|
||||
msgstr "Укажите ветку версии для файлов OpenWrt и ядра в разделе [Обновление через интернет]."
|
||||
|
||||
msgid "Set plugin branch:"
|
||||
msgstr "Ветка плагина:"
|
||||
|
||||
msgid "Set the branch of the luci-app-amlogic plugin used in [Only update Amlogic Service]. Select main for JavaScript version or lua for Lua version."
|
||||
msgstr "Укажите ветку плагина luci-app-amlogic для раздела [Только обновить сервис Amlogic]. Выберите main для JavaScript версии или lua для Lua версии."
|
||||
|
||||
msgid "main [JavaScript version]"
|
||||
msgstr "main [JavaScript версия]"
|
||||
|
||||
msgid "lua [Lua version]"
|
||||
msgstr "lua [Lua версия]"
|
||||
|
||||
msgid "Keep config update:"
|
||||
msgstr "Сохранять конфигурацию при обновлении:"
|
||||
|
||||
msgid "Set whether to keep the current config during [Online Download Update] and [Manually Upload Update]."
|
||||
msgstr "Выберите, сохранять ли текущую конфигурацию при обновлении через интернет или вручную."
|
||||
|
||||
msgid "Auto write bootloader:"
|
||||
msgstr "Автоматически записывать bootloader:"
|
||||
|
||||
msgid "[Recommended choice] Set whether to auto write bootloader during install and update OpenWrt."
|
||||
msgstr "[Рекомендуется] Указывает, записывать ли автоматически bootloader при установке или обновлении OpenWrt."
|
||||
|
||||
msgid "Set the file system type:"
|
||||
msgstr "Тип файловой системы:"
|
||||
|
||||
msgid "[Default ext4] Set the file system type of the shared partition (/mnt/mmcblk*p4) when install OpenWrt."
|
||||
msgstr "[По умолчанию ext4] Укажите тип файловой системы для общего раздела (/mnt/mmcblk*p4) при установке OpenWrt."
|
||||
|
||||
msgid "Save Config:"
|
||||
msgstr "Сохранить конфигурацию:"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "Сохранить"
|
||||
|
||||
msgid "Check All Components Update"
|
||||
msgstr "Проверка обновлений"
|
||||
|
||||
msgid "Provide OpenWrt Firmware, Kernel and Plugin online check, download and update service."
|
||||
msgstr "Предоставляет онлайн-проверку, загрузку и обновление прошивки OpenWrt, ядра и плагинов."
|
||||
|
||||
msgid "Only update Amlogic Service"
|
||||
msgstr "Обновить только сервис Amlogic"
|
||||
|
||||
msgid "Update system kernel only"
|
||||
msgstr "Обновить только ядро системы"
|
||||
|
||||
msgid "Complete system update"
|
||||
msgstr "Полное обновление системы"
|
||||
|
||||
msgid "Check Update"
|
||||
msgstr "Проверить обновления"
|
||||
|
||||
msgid "Checking..."
|
||||
msgstr "Проверка..."
|
||||
|
||||
msgid "Current Version"
|
||||
msgstr "Текущая версия"
|
||||
|
||||
msgid "Latest Version"
|
||||
msgstr "Последняя версия"
|
||||
|
||||
msgid "Rescue Kernel"
|
||||
msgstr "Аварийное восстановление ядра"
|
||||
|
||||
msgid "When a kernel update fails and causes the OpenWrt system to be unbootable, the kernel can be restored by mutual recovery from eMMC/NVMe/sdX."
|
||||
msgstr "Если обновление ядра привело к невозможности загрузки OpenWrt, его можно восстановить с другого носителя (eMMC/NVMe/sdX)."
|
||||
|
||||
msgid "Rescue the original system kernel"
|
||||
msgstr "Восстановить исходное ядро системы"
|
||||
|
||||
msgid "Rescuing..."
|
||||
msgstr "Восстановление..."
|
||||
|
||||
msgid "Current Device:"
|
||||
msgstr "Текущее устройство:"
|
||||
|
||||
msgid "Display the PLATFORM classification of the device."
|
||||
msgstr "Отображает класс PLATFORM устройства."
|
||||
|
||||
msgid "Update plugins first, then update the kernel or firmware. More options can be configured in [Plugin Settings]."
|
||||
msgstr "Сначала обновите плагины, затем ядро или прошивку. Дополнительные настройки доступны в [Настройках плагина]."
|
||||
|
||||
msgid "Collecting data..."
|
||||
msgstr "Сбор данных..."
|
||||
|
||||
msgid "Server Logs"
|
||||
msgstr "Журнал операций"
|
||||
|
||||
msgid "Display the execution log of the current operation."
|
||||
msgstr "Отображает журнал выполнения текущей операции."
|
||||
|
||||
msgid "Stop Refresh Log"
|
||||
msgstr "Остановить обновление журнала"
|
||||
|
||||
msgid "Start Refresh Log"
|
||||
msgstr "Начать обновление журнала"
|
||||
|
||||
msgid "Clean Log"
|
||||
msgstr "Очистить журнал"
|
||||
|
||||
msgid "Download Log"
|
||||
msgstr "Скачать журнал"
|
||||
|
||||
msgid "CPU Settings"
|
||||
msgstr "Настройки CPU"
|
||||
|
||||
msgid "CPU Freq"
|
||||
msgstr "Частота CPU"
|
||||
|
||||
msgid "CPU Freq Settings"
|
||||
msgstr "Настройки частоты CPU"
|
||||
|
||||
msgid "Set CPU Scaling Governor to Max Performance or Balance Mode"
|
||||
msgstr "Выберите режим управления частотой CPU: максимальная производительность или баланс."
|
||||
|
||||
msgid "CPU Scaling Governor:"
|
||||
msgstr "Режим управления частотой:"
|
||||
|
||||
msgid "ondemand"
|
||||
msgstr "Ondemand — автоматический баланс"
|
||||
|
||||
msgid "performance"
|
||||
msgstr "Performance — максимальная производительность"
|
||||
|
||||
msgid "schedutil"
|
||||
msgstr "Schedutil — умный планировщик"
|
||||
|
||||
msgid "CPU Freq from 48000 to 716000 (Khz)"
|
||||
msgstr "Диапазон частоты CPU: от 48000 до 716000 КГц"
|
||||
|
||||
msgid "Min Freq:"
|
||||
msgstr "Мин. частота:"
|
||||
|
||||
msgid "Max Freq:"
|
||||
msgstr "Макс. частота:"
|
||||
|
||||
msgid "CPU Switching Threshold:"
|
||||
msgstr "Порог переключения частоты CPU:"
|
||||
|
||||
msgid "Kernel make a decision on whether it should increase the frequency (%)"
|
||||
msgstr "При загрузке CPU выше указанного процента (%), ядро увеличит частоту."
|
||||
|
||||
msgid "CPU Switching Sampling rate:"
|
||||
msgstr "Период проверки частоты CPU:"
|
||||
|
||||
msgid "The sampling rate determines how frequently the governor checks to tune the CPU (ms)"
|
||||
msgstr "Частота проверки загрузки CPU (мс). Примечание: слишком частое переключение может вызвать дрожание задержек в сети."
|
||||
|
||||
msgid "Microarchitectures:"
|
||||
msgstr "Микроархитектура:"
|
||||
|
||||
msgid "Loading"
|
||||
msgstr "Загрузка"
|
||||
|
||||
msgid "PowerOff"
|
||||
msgstr "Выключить"
|
||||
|
||||
msgid "Shut down your router device."
|
||||
msgstr "Выключить устройство маршрутизатора."
|
||||
|
||||
msgid "Perform PowerOff"
|
||||
msgstr "Выполнить выключение"
|
||||
|
||||
msgid "Are you sure you want to shut down?"
|
||||
msgstr "Вы уверены, что хотите выключить устройство?"
|
||||
|
||||
msgid "Device is shutting down..."
|
||||
msgstr "Устройство выключается..."
|
||||
|
||||
msgid "Waiting for the device to shut down..."
|
||||
msgstr "Ожидание завершения выключения..."
|
||||
|
||||
msgid "The device has been turned off"
|
||||
msgstr "Устройство выключено"
|
||||
|
||||
msgid "Are you sure you want to power off the device?"
|
||||
msgstr "Вы уверены, что хотите выключить устройство?"
|
||||
|
||||
msgid "Back"
|
||||
msgstr "Назад"
|
||||
|
||||
msgid "Click the button below to safely power off the device."
|
||||
msgstr "Нажмите кнопку ниже, чтобы безопасно выключить устройство."
|
||||
|
||||
msgid "Display the log of plug-in service operation."
|
||||
msgstr "Показать журнал работы службы плагина."
|
||||
|
||||
msgid "Edit list of backup files"
|
||||
msgstr "Изменить список файлов резервной копии"
|
||||
|
||||
msgid "Enter the dtb file name:"
|
||||
msgstr "Введите имя файла dtb:"
|
||||
|
||||
msgid "In this list, you can manually add the file or directory list of files that need to be backed up. Please confirm that the format and content of the file are correct."
|
||||
msgstr "В этом списке можно вручную добавить файлы или каталоги, которые нужно резервировать. Пожалуйста, проверьте корректность формата и содержимого."
|
||||
|
||||
msgid "Please refresh the page to see the changes."
|
||||
msgstr "Пожалуйста, обновите страницу, чтобы увидеть изменения."
|
||||
|
||||
msgid "Power Off"
|
||||
msgstr "Выключить"
|
||||
|
||||
msgid "Powered off."
|
||||
msgstr "Выключено."
|
||||
|
||||
msgid "Powering off, please wait..."
|
||||
msgstr "Выключение, пожалуйста, подождите..."
|
||||
|
||||
msgid "Running"
|
||||
msgstr "Работает"
|
||||
|
||||
msgid "Save Failed"
|
||||
msgstr "Ошибка сохранения"
|
||||
|
||||
msgid "Service Log"
|
||||
msgstr "Журнал службы"
|
||||
|
||||
msgid "Start Refresh"
|
||||
msgstr "Начать обновление"
|
||||
|
||||
msgid "Stop Refresh"
|
||||
msgstr "Остановить обновление"
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr "Остановлено"
|
||||
|
||||
msgid "Successfully saved."
|
||||
msgstr "Сохранено успешно."
|
||||
|
||||
msgid "Upload file..."
|
||||
msgstr "Загрузить файл..."
|
||||
@@ -0,0 +1,654 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: https://github.com/ophub/luci-app-amlogic \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: zh_CN\n"
|
||||
"X-Generator: Poedit 2.3.1\n"
|
||||
|
||||
msgid "Choose local file:"
|
||||
msgstr "选择本地文件:"
|
||||
|
||||
msgid "Couldn't open file:"
|
||||
msgstr "无法打开文件:"
|
||||
|
||||
msgid "The file Will download automatically."
|
||||
msgstr "文件将自动下载"
|
||||
|
||||
msgid "Create upload file error."
|
||||
msgstr "创建上传文件失败。"
|
||||
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
msgid "Download file"
|
||||
msgstr "下载文件"
|
||||
|
||||
msgid "File name"
|
||||
msgstr "文件名"
|
||||
|
||||
msgid "File saved to"
|
||||
msgstr "文件保存到"
|
||||
|
||||
msgid "FileTransfer"
|
||||
msgstr "文件传输"
|
||||
|
||||
msgid "Install"
|
||||
msgstr "安装"
|
||||
|
||||
msgid "Attributes"
|
||||
msgstr "属性"
|
||||
|
||||
msgid "Modify time"
|
||||
msgstr "修改时间"
|
||||
|
||||
msgid "No specify upload file."
|
||||
msgstr "未指定上传文件。"
|
||||
|
||||
msgid "Path on Route:"
|
||||
msgstr "路由根目录:"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "移除"
|
||||
|
||||
msgid "Size"
|
||||
msgstr "大小"
|
||||
|
||||
msgid "Upload"
|
||||
msgstr "上传"
|
||||
|
||||
msgid "Upload file list"
|
||||
msgstr "上传文件列表"
|
||||
|
||||
msgid "There are config file in the upload directory, and you can restore the config. "
|
||||
msgstr "上传文件列表中有配置文件,你可以恢复配置。"
|
||||
|
||||
msgid "There are kernel files in the upload directory, and you can replace the kernel."
|
||||
msgstr "上传文件列表中有内核文件,你可以升级内核。"
|
||||
|
||||
msgid "There are openwrt firmware file in the upload directory, and you can update the openwrt."
|
||||
msgstr "上传文件列表中有 OpenWrt 固件文件,你可以升级 OpenWrt。"
|
||||
|
||||
msgid "Update plugins first, then update the kernel or firmware."
|
||||
msgstr "首先更新插件,再更新内核或固件。"
|
||||
|
||||
msgid "After uploading [Firmware], [Kernel], [IPK] or [Backup Config], the operation buttons will be displayed."
|
||||
msgstr "当上传 [固件文件],[内核文件],[IPK文件],[配置文件] 后,将自动显示相关操作按钮。"
|
||||
|
||||
msgid "After uploading firmware (.img/.img.gz/.img.xz/.7z suffix) or kernel files (3 kernel files), the update button will be displayed."
|
||||
msgstr "当上传文件列表中有 OpenWrt 固件(.img/.img.gz/.img.xz/.7z 后缀)或内核文件(3个内核文件)时,将自动显示相关操作按钮。"
|
||||
|
||||
msgid "Amlogic Service"
|
||||
msgstr "晶晨宝盒"
|
||||
|
||||
msgid "Restore Config / Replace OpenWrt Kernel"
|
||||
msgstr "恢复配置 / 升级 OpenWrt 内核"
|
||||
|
||||
msgid "Manually Upload Update"
|
||||
msgstr "手动上传更新"
|
||||
|
||||
msgid "Install OpenWrt"
|
||||
msgstr "安装 OpenWrt"
|
||||
|
||||
msgid "Select the device model:"
|
||||
msgstr "选择设备型号:"
|
||||
|
||||
msgid "Select List"
|
||||
msgstr "选择列表"
|
||||
|
||||
msgid "Enter the dtb file name"
|
||||
msgstr "输入自定义 dtb 文件名"
|
||||
|
||||
msgid "Enter the soc name:"
|
||||
msgstr "输入自定义 soc 名称:"
|
||||
|
||||
msgid "Enter the uboot_overload name:"
|
||||
msgstr "输入自定义 uboot_overload 名称:"
|
||||
|
||||
msgid "Invalid value."
|
||||
msgstr "无效值"
|
||||
|
||||
msgid "Install OpenWrt:"
|
||||
msgstr "安装 OpenWrt:"
|
||||
|
||||
msgid "Install OpenWrt to EMMC, Please select the device model, Or enter the dtb file name."
|
||||
msgstr "将 OpenWrt 安装到 EMMC,请选择设备型号,或输入 dtb 文件名。"
|
||||
|
||||
msgid "Tip: Writing is in progress, and it will automatically restart after completion."
|
||||
msgstr "提示:正在写入,完成后将自动重启。"
|
||||
|
||||
msgid "You have chosen:"
|
||||
msgstr "你选择了:"
|
||||
|
||||
msgid "Start install?"
|
||||
msgstr "开始安装吗?"
|
||||
|
||||
msgid "Installing..."
|
||||
msgstr "正在安装..."
|
||||
|
||||
msgid "Install Failed"
|
||||
msgstr "安装失败"
|
||||
|
||||
msgid "Successful Install"
|
||||
msgstr "安装成功"
|
||||
|
||||
msgid "Update"
|
||||
msgstr "升级"
|
||||
|
||||
msgid "Updating..."
|
||||
msgstr "正在更新..."
|
||||
|
||||
msgid "Update Failed"
|
||||
msgstr "更新失败"
|
||||
|
||||
msgid "Successful Update"
|
||||
msgstr "更新成功"
|
||||
|
||||
msgid "Update OpenWrt firmware"
|
||||
msgstr "升级 OpenWrt 固件"
|
||||
|
||||
msgid "kernel"
|
||||
msgstr "内核"
|
||||
|
||||
msgid "Replace OpenWrt Kernel"
|
||||
msgstr "更换 OpenWrt 内核"
|
||||
|
||||
msgid "Tip: The kernel is being replaced, and it will automatically restart after completion."
|
||||
msgstr "提示:正在替换内核,完成后将自动重启。"
|
||||
|
||||
msgid "Supports management of Amlogic s9xxx, Allwinner (V-Plus Cloud), and Rockchip (BeikeYun, Chainedbox L1 Pro) boxes."
|
||||
msgstr "支持对晶晨 s9xxx 系列(斐讯N1、HK1等),全志(微加云),以及瑞芯微(贝壳云、我家云)的盒子进行在线管理。"
|
||||
|
||||
msgid "Supported functions:"
|
||||
msgstr "支持的功能:"
|
||||
|
||||
msgid "Provide services such as install to EMMC, Update Firmware and Kernel, Backup and Recovery Config, Snapshot management, etc."
|
||||
msgstr "支持将 OpenWrt 系统写入 EMMC、升级固件与内核,备份与恢复自定义配置,快照管理等功能。"
|
||||
|
||||
msgid "Supported Boxes:"
|
||||
msgstr "支持的盒子:"
|
||||
|
||||
msgid "Amlogic s922x --- [ Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000 ]"
|
||||
msgstr "晶晨 s922x --- [ Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000 ]"
|
||||
|
||||
msgid "Amlogic s905x3 -- [ X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air ]"
|
||||
msgstr "晶晨 s905x3 -- [ X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air ]"
|
||||
|
||||
msgid "Amlogic s905x2 -- [ X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2 ]"
|
||||
msgstr "晶晨 s905x2 -- [ X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2 ]"
|
||||
|
||||
msgid "Amlogic s912 ---- [ H96-Pro-Plus, Octopus-Planet, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro ]"
|
||||
msgstr "晶晨 s912 ---- [ H96-Pro-Plus, 章鱼星球, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro ]"
|
||||
|
||||
msgid "Amlogic s905x --- [ HG680P, B860H, TBee, T95, TX9, XiaoMI-3S, X96 ]"
|
||||
msgstr "晶晨 s905x --- [ 烽火HG680, 中兴B860H, TBee, T95, TX9, XiaoMI-3S, X96 ]"
|
||||
|
||||
msgid "Amlogic s905w --- [ X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K ]"
|
||||
msgstr "晶晨 s905w --- [ X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K ]"
|
||||
|
||||
msgid "Amlogic s905d --- [ Phicomm-N1, MECOOL-KI-Pro, SML-5442TW ]"
|
||||
msgstr "晶晨 s905d --- [ 斐讯-N1, MECOOL-KI-Pro, SML-5442TW ]"
|
||||
|
||||
msgid "Amlogic s905l --- [ UNT402A, M201-S ]"
|
||||
msgstr "晶晨 s905l --- [ UNT402A, M201-S ]"
|
||||
|
||||
msgid "Amlogic s905l2 -- [ MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E ]"
|
||||
msgstr "晶晨 s905l2 -- [ MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E ]"
|
||||
|
||||
msgid "Amlogic s905l3 -- [ CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310 ]"
|
||||
msgstr "晶晨 s905l3 -- [ CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310 ]"
|
||||
|
||||
msgid "Amlogic s905l3a - [ E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H ]"
|
||||
msgstr "晶晨 s905l3a - [ E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H ]"
|
||||
|
||||
msgid "Amlogic s905l3b - [ CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A ]"
|
||||
msgstr "晶晨 s905l3b - [ CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A ]"
|
||||
|
||||
msgid "Amlogic s905 ---- [ Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5 ]"
|
||||
msgstr "晶晨 s905 ---- [ Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5 ]"
|
||||
|
||||
msgid "Allwinner H6 ---- [ V-Plus Cloud ]"
|
||||
msgstr "全志 H6 ------ [ 微加云 ]"
|
||||
|
||||
msgid "Rockchip -------- [ BeikeYun, L1-Pro, FastRhino R66S/R68S, Radxa 5B/E25 ]"
|
||||
msgstr "瑞芯微 ------- [ 贝壳云, 我家云, 电犀牛R66S/R68S, 瑞莎5B/E25 ]"
|
||||
|
||||
msgid "Used in KVM ----- [ Can be used in KVM virtual machine of Armbian system. ]"
|
||||
msgstr "KVM 中使用 --- [ 可以在 Armbian 系统的 KVM 虚拟机中使用。 ]"
|
||||
|
||||
msgid "KVM dual system switching"
|
||||
msgstr "KVM 双系统切换"
|
||||
|
||||
msgid "You can freely switch between KVM dual partitions, using OpenWrt systems in different partitions."
|
||||
msgstr "您可以在 KVM 双分区之间自由切换,使用不同分区中的 OpenWrt 系统。"
|
||||
|
||||
msgid "Switch System"
|
||||
msgstr "切换系统"
|
||||
|
||||
msgid "System is switching..."
|
||||
msgstr "系统切换中..."
|
||||
|
||||
msgid "Waiting for system switching..."
|
||||
msgstr "等待系统完成切换..."
|
||||
|
||||
msgid "System switchover succeeded, restarting..."
|
||||
msgstr "系统切换成功,正在重启..."
|
||||
|
||||
msgid "Are you sure you want to switch systems?"
|
||||
msgstr "你确定要切换系统吗?"
|
||||
|
||||
msgid "Install Ipk"
|
||||
msgstr "安装Ipk"
|
||||
|
||||
msgid "Plugin Settings"
|
||||
msgstr "插件设置"
|
||||
|
||||
msgid "Backup Firmware Config"
|
||||
msgstr "备份固件配置"
|
||||
|
||||
msgid "Backup Config:"
|
||||
msgstr "备份配置:"
|
||||
|
||||
msgid "Download Backup"
|
||||
msgstr "下载备份"
|
||||
|
||||
msgid "Backup OpenWrt config (openwrt_config.tar.gz). Use this file to restore the config in [Manually Upload Update]."
|
||||
msgstr "备份当前 OpenWrt 的相关配置信息(openwrt_config.tar.gz)。使用此文件可在 [手动上传更新] 中恢复配置。"
|
||||
|
||||
msgid "Edit List:"
|
||||
msgstr "编辑列表:"
|
||||
|
||||
msgid "Open List"
|
||||
msgstr "打开列表"
|
||||
|
||||
msgid "Backup Configuration - Custom List"
|
||||
msgstr "备份配置 - 自定义列表"
|
||||
|
||||
msgid "Write one configuration item per line, and directories should end with a /."
|
||||
msgstr "每行写一个配置项,目录需要以/结尾。"
|
||||
|
||||
msgid "Restore Backup:"
|
||||
msgstr "恢复备份:"
|
||||
|
||||
msgid "Upload Backup"
|
||||
msgstr "上传备份"
|
||||
|
||||
msgid "Restore"
|
||||
msgstr "恢复"
|
||||
|
||||
msgid "Restore Config"
|
||||
msgstr "恢复配置"
|
||||
|
||||
msgid "Restore configuration"
|
||||
msgstr "还原配置"
|
||||
|
||||
msgid "Config File"
|
||||
msgstr "配置文件"
|
||||
|
||||
msgid "Tip: The config is being restored, and it will automatically restart after completion."
|
||||
msgstr "提示:正在还原配置,完成后将自动重启。"
|
||||
|
||||
msgid "Snapshot Management"
|
||||
msgstr "快照管理"
|
||||
|
||||
msgid "Create Snapshot"
|
||||
msgstr "创建快照"
|
||||
|
||||
msgid "Creating..."
|
||||
msgstr "正在创建"
|
||||
|
||||
msgid "Created successfully"
|
||||
msgstr "创建成功"
|
||||
|
||||
msgid "Creation failed"
|
||||
msgstr "创建失败"
|
||||
|
||||
msgid "Initialize Snapshot"
|
||||
msgstr "初始化快照"
|
||||
|
||||
msgid "Update Snapshot"
|
||||
msgstr "更新点快照"
|
||||
|
||||
msgid "Restore Snap"
|
||||
msgstr "还原快照"
|
||||
|
||||
msgid "Restoring..."
|
||||
msgstr "正在还原"
|
||||
|
||||
msgid "Restore Failed"
|
||||
msgstr "还原失败"
|
||||
|
||||
msgid "Successfully Restored"
|
||||
msgstr "还原成功"
|
||||
|
||||
msgid "Delete Snap"
|
||||
msgstr "删除快照"
|
||||
|
||||
msgid "You selected a snapshot:"
|
||||
msgstr "你选择了快照:"
|
||||
|
||||
msgid "Confirm delete?"
|
||||
msgstr "确定删除吗?"
|
||||
|
||||
msgid "Confirm recovery and restart OpenWrt?"
|
||||
msgstr "确定恢复并重启 OpenWrt 吗?"
|
||||
|
||||
msgid "Delete Failed"
|
||||
msgstr "删除失败"
|
||||
|
||||
msgid "Successfully Deleted"
|
||||
msgstr "删除成功"
|
||||
|
||||
msgid "Create a snapshot of the current system configuration, or restore to a snapshot."
|
||||
msgstr "创建当前系统配置的快照,或还原到某个快照。"
|
||||
|
||||
msgid "Currently OpenWrt does not support the snapshot function."
|
||||
msgstr "当前 OpenWrt 不支持快照功能。"
|
||||
|
||||
msgid "Please use this plugin to reinstall or upgrade OpenWrt to enable the snapshot function."
|
||||
msgstr "请使用本插件重新安装或升级 OpenWrt 开启快照功能。"
|
||||
|
||||
msgid "Deleting..."
|
||||
msgstr "正在删除..."
|
||||
|
||||
msgid "Online Download Update"
|
||||
msgstr "在线下载更新"
|
||||
|
||||
msgid "Config Source"
|
||||
msgstr "配置来源"
|
||||
|
||||
msgid "You can customize the github.com download repository of OpenWrt files and kernels in [Online Download Update]."
|
||||
msgstr "您可以自定义 [在线下载和更新] 中 OpenWrt 固件和内核的 github.com 下载仓库。"
|
||||
|
||||
msgid "Tip: The same files as the current OpenWrt system's BOARD (such as rock5b) and kernel (such as 5.10) will be downloaded."
|
||||
msgstr "提示:将下载与当前 OpenWrt 系统的 BOARD(如:rock5b)和内核(如:5.10)相同的文件。"
|
||||
|
||||
msgid "OpenWrt download repository:"
|
||||
msgstr "OpenWrt 文件的下载仓库:"
|
||||
|
||||
msgid "Set the OpenWrt files download repository on github.com in [Online Download Update]."
|
||||
msgstr "设置 [在线下载更新] 中 github.com 的 OpenWrt 文件的下载仓库。"
|
||||
|
||||
msgid "OpenWrt download tags keyword:"
|
||||
msgstr "OpenWrt 下载标签关键字:"
|
||||
|
||||
msgid "Set the OpenWrt files download tags keyword for github.com in [Online Download Update]."
|
||||
msgstr "设置 [在线下载更新] 中 github.com 的 OpenWrt 文件下载标签关键字。"
|
||||
|
||||
msgid "OpenWrt files suffix:"
|
||||
msgstr "OpenWrt 文件的后缀:"
|
||||
|
||||
msgid "Set the OpenWrt files download suffix for github.com in [Online Download Update]."
|
||||
msgstr "设置 [在线下载更新] 中 github.com 的 OpenWrt 文件的后缀。"
|
||||
|
||||
msgid "Kernel download repository:"
|
||||
msgstr "内核的下载仓库:"
|
||||
|
||||
msgid "Set the kernel files download repository on github.com in [Online Download Update]."
|
||||
msgstr "设置 [在线下载更新] 中 github.com 的内核文件下载仓库。"
|
||||
|
||||
msgid "Kernel download tags:"
|
||||
msgstr "内核的下载标签:"
|
||||
|
||||
msgid "Set the kernel files download tags on github.com in [Online Download Update]."
|
||||
msgstr "设置 [在线下载更新] 中 github.com 的内核文件下载标签。"
|
||||
|
||||
msgid "kernel_rk3588 [Rockchip RK3588 Kernel]"
|
||||
msgstr "kernel_rk3588 [Rockchip RK3588 内核]"
|
||||
|
||||
msgid "kernel_rk35xx [Rockchip RK35xx Kernel]"
|
||||
msgstr "kernel_rk35xx [Rockchip RK35xx 内核]"
|
||||
|
||||
msgid "kernel_stable [Mainline Stable Kernel]"
|
||||
msgstr "kernel_stable [主线稳定内核]"
|
||||
|
||||
msgid "kernel_flippy [Mainline Stable Kernel by Flippy]"
|
||||
msgstr "kernel_flippy [Flippy 的主线稳定内核]"
|
||||
|
||||
msgid "kernel_beta [Beta Kernel]"
|
||||
msgstr "kernel_beta [测试版内核]"
|
||||
|
||||
msgid "Set version branch:"
|
||||
msgstr "设置版本分支:"
|
||||
|
||||
msgid "Set the version branch of the OpenWrt files and kernel selected in [Online Download Update]."
|
||||
msgstr "设置 [在线下载更新] 时 OpenWrt 文件与内核所选用的版本分支。"
|
||||
|
||||
msgid "Set plugin branch:"
|
||||
msgstr "设置插件分支:"
|
||||
|
||||
msgid "Set the branch of the luci-app-amlogic plugin used in [Only update Amlogic Service]. Select main for JavaScript version or lua for Lua version."
|
||||
msgstr "设置 [仅更新晶晨宝盒] 时使用的插件分支。选择 main 使用 JavaScript 版本,选择 lua 使用 Lua 版本。"
|
||||
|
||||
msgid "main [JavaScript version]"
|
||||
msgstr "main [JavaScript 版本]"
|
||||
|
||||
msgid "lua [Lua version]"
|
||||
msgstr "lua [Lua 版本]"
|
||||
|
||||
msgid "Keep config update:"
|
||||
msgstr "保留配置更新:"
|
||||
|
||||
msgid "Set whether to keep the current config during [Online Download Update] and [Manually Upload Update]."
|
||||
msgstr "设置是否在 [在线下载更新] 和 [手动上传更新] 时保留当前的配置。"
|
||||
|
||||
msgid "Auto write bootloader:"
|
||||
msgstr "自动写入 bootloader:"
|
||||
|
||||
msgid "[Recommended choice] Set whether to auto write bootloader during install and update OpenWrt."
|
||||
msgstr "[推荐选择] 设置在安装和更新 OpenWrt 固件时是否自动写入 bootloader 文件。"
|
||||
|
||||
msgid "Set the file system type:"
|
||||
msgstr "设置文件系统类型:"
|
||||
|
||||
msgid "[Default ext4] Set the file system type of the shared partition (/mnt/mmcblk*p4) when install OpenWrt."
|
||||
msgstr "[默认 ext4] 设置安装 OpenWrt 时共享分区 (/mnt/mmcblk*p4) 的文件系统类型。"
|
||||
|
||||
msgid "Save Config:"
|
||||
msgstr "保存配置:"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
msgid "Check All Components Update"
|
||||
msgstr "插件和内核检查更新"
|
||||
|
||||
msgid "Provide OpenWrt Firmware, Kernel and Plugin online check, download and update service."
|
||||
msgstr "提供 OpenWrt 固件,内核和插件在线检查,下载和更新服务。"
|
||||
|
||||
msgid "Only update Amlogic Service"
|
||||
msgstr "仅更新晶晨宝盒"
|
||||
|
||||
msgid "Update system kernel only"
|
||||
msgstr "仅更新系统内核"
|
||||
|
||||
msgid "Complete system update"
|
||||
msgstr "完整更新全系统"
|
||||
|
||||
msgid "Check Update"
|
||||
msgstr "检查更新"
|
||||
|
||||
msgid "Checking..."
|
||||
msgstr "正在检查更新..."
|
||||
|
||||
msgid "Current Version"
|
||||
msgstr "当前版本"
|
||||
|
||||
msgid "Latest Version"
|
||||
msgstr "最新版本"
|
||||
|
||||
msgid "Rescue Kernel"
|
||||
msgstr "救援内核"
|
||||
|
||||
msgid "When a kernel update fails and causes the OpenWrt system to be unbootable, the kernel can be restored by mutual recovery from eMMC/NVMe/sdX."
|
||||
msgstr "当内核更新失败造成 OpenWrt 系统无法启动时,可以从 eMMC/NVME/sdX 相互恢复内核。"
|
||||
|
||||
msgid "Rescue the original system kernel"
|
||||
msgstr "救援原系统内核"
|
||||
|
||||
msgid "Rescuing..."
|
||||
msgstr "正在救援..."
|
||||
|
||||
msgid "Current Device:"
|
||||
msgstr "当前设备:"
|
||||
|
||||
msgid "Display the PLATFORM classification of the device."
|
||||
msgstr "显示设备的 PLATFORM 分类。"
|
||||
|
||||
msgid "Update plugins first, then update the kernel or firmware. More options can be configured in [Plugin Settings]."
|
||||
msgstr "首先更新插件,再更新内核或固件。更多选项可以在插件设置中配置。"
|
||||
|
||||
msgid "Collecting data..."
|
||||
msgstr "正在收集数据…"
|
||||
|
||||
msgid "Server Logs"
|
||||
msgstr "操作日志"
|
||||
|
||||
msgid "Display the execution log of the current operation."
|
||||
msgstr "显示当前操作的执行日志。"
|
||||
|
||||
msgid "Stop Refresh Log"
|
||||
msgstr "停止刷新"
|
||||
|
||||
msgid "Start Refresh Log"
|
||||
msgstr "开始刷新"
|
||||
|
||||
msgid "Clean Log"
|
||||
msgstr "清理日志"
|
||||
|
||||
msgid "Download Log"
|
||||
msgstr "下载日志"
|
||||
|
||||
msgid "CPU Settings"
|
||||
msgstr "CPU 设置"
|
||||
|
||||
msgid "CPU Freq"
|
||||
msgstr "CPU 性能优化调节"
|
||||
|
||||
msgid "CPU Freq Settings"
|
||||
msgstr "CPU 性能优化调节设置"
|
||||
|
||||
msgid "Set CPU Scaling Governor to Max Performance or Balance Mode"
|
||||
msgstr "设置路由器的 CPU 性能模式(高性能/均衡省电)"
|
||||
|
||||
msgid "CPU Scaling Governor:"
|
||||
msgstr "CPU 调速器:"
|
||||
|
||||
msgid "ondemand"
|
||||
msgstr "Ondemand 自动平衡模式"
|
||||
|
||||
msgid "performance"
|
||||
msgstr "Performance 高性能模式"
|
||||
|
||||
msgid "schedutil"
|
||||
msgstr "Schedutil 敏捷调度器模式"
|
||||
|
||||
msgid "CPU Freq from 48000 to 716000 (Khz)"
|
||||
msgstr "CPU 频率范围为 48000 到 716000 (Khz)"
|
||||
|
||||
msgid "Min Freq:"
|
||||
msgstr "最小频率:"
|
||||
|
||||
msgid "Max Freq:"
|
||||
msgstr "最大频率:"
|
||||
|
||||
msgid "CPU Switching Threshold:"
|
||||
msgstr "CPU 切换频率触发阈值:"
|
||||
|
||||
msgid "Kernel make a decision on whether it should increase the frequency (%)"
|
||||
msgstr "当 CPU 占用率超过 (%) 的情况下触发内核切换频率"
|
||||
|
||||
msgid "CPU Switching Sampling rate:"
|
||||
msgstr "CPU 切换周期:"
|
||||
|
||||
msgid "The sampling rate determines how frequently the governor checks to tune the CPU (ms)"
|
||||
msgstr "CPU 检查切换的周期 (ms) 。注意:过于频繁的切换频率会引起网络延迟抖动"
|
||||
|
||||
msgid "Microarchitectures:"
|
||||
msgstr "微架构:"
|
||||
|
||||
msgid "Loading"
|
||||
msgstr "载入中"
|
||||
|
||||
msgid "PowerOff"
|
||||
msgstr "关机"
|
||||
|
||||
msgid "Shut down your router device."
|
||||
msgstr "关闭你的路由器设备。"
|
||||
|
||||
msgid "Perform PowerOff"
|
||||
msgstr "执行关机"
|
||||
|
||||
msgid "Are you sure you want to shut down?"
|
||||
msgstr "你确定要关机吗?"
|
||||
|
||||
msgid "Device is shutting down..."
|
||||
msgstr "设备正在关机..."
|
||||
|
||||
msgid "Waiting for the device to shut down..."
|
||||
msgstr "等待设备关机..."
|
||||
|
||||
msgid "The device has been turned off"
|
||||
msgstr "设备已经关机"
|
||||
|
||||
msgid "Are you sure you want to power off the device?"
|
||||
msgstr "确定要关闭设备吗?"
|
||||
|
||||
msgid "Back"
|
||||
msgstr "返回"
|
||||
|
||||
msgid "Click the button below to safely power off the device."
|
||||
msgstr "点击下方的按钮可以安全地关闭设备。"
|
||||
|
||||
msgid "Display the log of plug-in service operation."
|
||||
msgstr "显示插件服务运行日志。"
|
||||
|
||||
msgid "Edit list of backup files"
|
||||
msgstr "编辑备份文件列表"
|
||||
|
||||
msgid "Enter the dtb file name:"
|
||||
msgstr "输入 dtb 文件名:"
|
||||
|
||||
msgid "In this list, you can manually add the file or directory list of files that need to be backed up. Please confirm that the format and content of the file are correct."
|
||||
msgstr "在此列表中,您可以手动添加需要备份的文件或目录。请确认文件的格式和内容正确。"
|
||||
|
||||
msgid "Please refresh the page to see the changes."
|
||||
msgstr "请刷新页面以查看更改。"
|
||||
|
||||
msgid "Power Off"
|
||||
msgstr "关闭设备"
|
||||
|
||||
msgid "Powered off."
|
||||
msgstr "已关机。"
|
||||
|
||||
msgid "Powering off, please wait..."
|
||||
msgstr "正在关机,请稍候……"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "Save Failed"
|
||||
msgstr "保存失败"
|
||||
|
||||
msgid "Service Log"
|
||||
msgstr "服务日志"
|
||||
|
||||
msgid "Start Refresh"
|
||||
msgstr "开始刷新"
|
||||
|
||||
msgid "Stop Refresh"
|
||||
msgstr "停止刷新"
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr "已停止"
|
||||
|
||||
msgid "Successfully saved."
|
||||
msgstr "保存成功。"
|
||||
|
||||
msgid "Upload file..."
|
||||
msgstr "上传文件……"
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
config amlogic 'config'
|
||||
option enable '1'
|
||||
option amlogic_firmware_repo 'https://github.com/breakingbadboy/OpenWrt'
|
||||
option amlogic_firmware_tag 'ARMv8'
|
||||
option amlogic_firmware_suffix '.img.gz'
|
||||
option amlogic_kernel_path 'https://github.com/ophub/kernel'
|
||||
option amlogic_kernel_tags ''
|
||||
option amlogic_kernel_branch ''
|
||||
option amlogic_plugin_branch 'main'
|
||||
option amlogic_firmware_config '1'
|
||||
option amlogic_write_bootloader '0'
|
||||
option amlogic_shared_fstype 'ext4'
|
||||
|
||||
config settings 'armcpu'
|
||||
option governor0 'schedutil'
|
||||
option minfreq0 '500000'
|
||||
option maxfreq0 '1512000'
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
START=60
|
||||
|
||||
NAME=amlogic
|
||||
|
||||
uci_get_by_type() {
|
||||
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
|
||||
echo ${ret:=$3}
|
||||
}
|
||||
|
||||
uci_set_by_type() {
|
||||
uci set $NAME.@$1[0].$2=$3 2>/dev/null
|
||||
uci commit $NAME
|
||||
}
|
||||
|
||||
start() {
|
||||
[ -x "/usr/sbin/fixcpufreq.pl" ] && /usr/sbin/fixcpufreq.pl && sync
|
||||
local cpu_policys=$(ls /sys/devices/system/cpu/cpufreq 2>/dev/null | grep -E 'policy[0-9]{1,3}' | xargs)
|
||||
if [ "${cpu_policys}" = "" ]; then
|
||||
cpu_policys="policy0"
|
||||
fi
|
||||
|
||||
config_load $NAME
|
||||
for policy_name in ${cpu_policys}; do
|
||||
local policy_id="${policy_name//policy/}"
|
||||
|
||||
# Get an optional value list for the current device
|
||||
local governor_list="$(cat /sys/devices/system/cpu/cpufreq/${policy_name}/scaling_available_frequencies 2>/dev/null | xargs)"
|
||||
local second_place_order="$(echo ${governor_list} | awk '{print $1}')"
|
||||
local second_place_reverse="$(echo ${governor_list} | awk '{print $NF}')"
|
||||
|
||||
# Get the default value in the Config file
|
||||
local governor=$(uci_get_by_type settings governor${policy_id} schedutil)
|
||||
local minfreq=$(uci_get_by_type settings minfreq${policy_id} ${second_place_order})
|
||||
local maxfreq=$(uci_get_by_type settings maxfreq${policy_id} ${second_place_reverse})
|
||||
|
||||
# Update result to the corresponding file
|
||||
echo $governor >/sys/devices/system/cpu/cpufreq/${policy_name}/scaling_governor
|
||||
echo $minfreq >/sys/devices/system/cpu/cpufreq/${policy_name}/scaling_min_freq
|
||||
echo $maxfreq >/sys/devices/system/cpu/cpufreq/${policy_name}/scaling_max_freq
|
||||
|
||||
# If the governor is ondemand, configure its specific parameters.
|
||||
if [ "$governor" = "ondemand" ]; then
|
||||
local ondemand_dir="/sys/devices/system/cpu/cpufreq/${policy_name}/ondemand"
|
||||
# Check if the per-policy ondemand directory exists.
|
||||
if [ -d "$ondemand_dir" ]; then
|
||||
# Read ondemand parameters from UCI, or use default values (e.g., 80 and 20) if not set.
|
||||
local up_threshold=$(uci_get_by_type settings up_threshold${policy_id} 80)
|
||||
local sampling_down_factor=$(uci_get_by_type settings sampling_down_factor${policy_id} 20)
|
||||
|
||||
# Write the values to the system files.
|
||||
echo $up_threshold > "${ondemand_dir}/up_threshold"
|
||||
echo $sampling_down_factor > "${ondemand_dir}/sampling_down_factor"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
reload() {
|
||||
start
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
# luci-app-amlogic-js: post-install configuration tweaks.
|
||||
|
||||
# Detect running platform from /etc/flippy-openwrt-release. The original Lua
|
||||
# controller hid the "Install OpenWrt" menu unless platform=="amlogic" or
|
||||
# SHOW_INSTALL_MENU=="yes", and hid "CPU Settings" on the qemu platform.
|
||||
# Since menu.d JSON cannot run shell logic, we mirror those checks once here
|
||||
# and store the result as uci flags that the menu entries depend on.
|
||||
|
||||
PLATFORM=$(grep -E '^PLATFORM=' /etc/flippy-openwrt-release 2>/dev/null | \
|
||||
awk -F= '{print $2}' | tr -d '"' | xargs)
|
||||
SHOW_INSTALL=$(grep -E '^SHOW_INSTALL_MENU=' /etc/flippy-openwrt-release 2>/dev/null | \
|
||||
awk -F= '{print $2}' | tr -d '"' | xargs)
|
||||
|
||||
# Detect whether the root fs is already on eMMC (mmcblk*p* with device/type == "MMC").
|
||||
# Mirrors root_on_internal_storage() in luci.amlogic: all other boot media
|
||||
# (TF/SD, NVMe, SATA, USB) keep the install menu visible.
|
||||
ROOT_PT=$(df / | tail -n1 | awk '{print $1}' | awk -F '/' '{print $3}')
|
||||
IS_INSTALLED=0
|
||||
case "$ROOT_PT" in
|
||||
mmcblk*p*)
|
||||
BASE_DEV="${ROOT_PT%p*}"
|
||||
[ "$(cat /sys/block/${BASE_DEV}/device/type 2>/dev/null | xargs)" = "MMC" ] && IS_INSTALLED=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$PLATFORM" in
|
||||
*amlogic*|*allwinner*) CAN_INSTALL=yes ;;
|
||||
*) [ "$SHOW_INSTALL" = "yes" ] && CAN_INSTALL=yes || CAN_INSTALL=no ;;
|
||||
esac
|
||||
if [ "$CAN_INSTALL" = "yes" ] && [ "$IS_INSTALLED" = "0" ]; then
|
||||
MENU_INSTALL=yes
|
||||
else
|
||||
MENU_INSTALL=no
|
||||
fi
|
||||
|
||||
case "$PLATFORM" in
|
||||
*qemu*) MENU_ARMCPU=no ;;
|
||||
*) MENU_ARMCPU=yes ;;
|
||||
esac
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@amlogic[-1]
|
||||
add ucitrack amlogic
|
||||
set ucitrack.@amlogic[-1].init=amlogic
|
||||
commit ucitrack
|
||||
set amlogic.config.menu_install=${MENU_INSTALL}
|
||||
set amlogic.config.menu_armcpu=${MENU_ARMCPU}
|
||||
commit amlogic
|
||||
EOF
|
||||
|
||||
rm -rf /tmp/luci-*
|
||||
exit 0
|
||||
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use File::Basename;
|
||||
|
||||
my $uci_config_name;
|
||||
if(-f "/etc/config/amlogic") {
|
||||
$uci_config_name="amlogic";
|
||||
} elsif(-f "/etc/config/cpufreq") {
|
||||
$uci_config_name="cpufreq";
|
||||
} else {
|
||||
print "Can not found amlogic or cpufreq config file!\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
my @policy_ids;
|
||||
my @policy_homes = </sys/devices/system/cpu/cpufreq/policy?>;
|
||||
if(@policy_homes) {
|
||||
foreach my $policy_home (@policy_homes) {
|
||||
push @policy_ids, substr($policy_home, -1);
|
||||
}
|
||||
} else {
|
||||
print "Can not found any policy!\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
our $need_commit = 0;
|
||||
for(my $i=0; $i <= $#policy_ids; $i++) {
|
||||
&fix_invalid_value($uci_config_name, $policy_ids[$i], $policy_homes[$i]);
|
||||
|
||||
}
|
||||
|
||||
if($need_commit > 0) {
|
||||
&uci_commit($uci_config_name);
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
################################# function ####################################
|
||||
sub fix_invalid_value {
|
||||
my($uci_config, $policy_id, $policy_home) = @_;
|
||||
|
||||
my %gove_hash = &get_gove_hash($policy_home);
|
||||
my @freqs = &get_freq_list($policy_home);
|
||||
my %freq_hash = &get_freq_hash(@freqs);
|
||||
my $min_freq = &get_min_freq(@freqs);
|
||||
my $max_freq = &get_max_freq(@freqs);
|
||||
|
||||
my $uci_section = "settings";
|
||||
my $uci_option;
|
||||
if($uci_config eq "cpufreq" ) {
|
||||
$uci_option = "governor";
|
||||
} else {
|
||||
$uci_option = "governor" . $policy_id;
|
||||
}
|
||||
# 如果未设置 governor, 或该 goveernor 不存在, 则修败默认值为 schedutil
|
||||
my $config_gove = &uci_get_by_type($uci_config, $uci_section, $uci_option, "NA");
|
||||
if( ($config_gove eq "NA") ||
|
||||
($gove_hash{$config_gove} != 1)) {
|
||||
&uci_set_by_type($uci_config, $uci_section, $uci_option, "schedutil");
|
||||
$need_commit++;
|
||||
}
|
||||
|
||||
# 如果出现不存在的 minfreq, 则修改为实际的 min_freq
|
||||
if($uci_config eq "cpufreq" ) {
|
||||
# "minifreq" is a spelling error that has always existed in the upstream source code
|
||||
$uci_option = "minifreq";
|
||||
} else {
|
||||
$uci_option = "minfreq" . $policy_id;
|
||||
}
|
||||
my $config_min_freq = &uci_get_by_type($uci_config, $uci_section, $uci_option, "0");
|
||||
if($freq_hash{$config_min_freq} != 1) {
|
||||
&uci_set_by_type($uci_config, $uci_section, $uci_option, $min_freq);
|
||||
$need_commit++;
|
||||
}
|
||||
|
||||
# 如果出现不存在的 maxfreq
|
||||
# 或 maxfreq < minfreq, 则修改为实际的 max_freq
|
||||
if($uci_config eq "cpufreq" ) {
|
||||
$uci_option = "maxfreq";
|
||||
} else {
|
||||
$uci_option = "maxfreq" . $policy_id;
|
||||
}
|
||||
my $config_max_freq = &uci_get_by_type($uci_config, $uci_section, $uci_option, "0");
|
||||
if( ( $freq_hash{$config_max_freq} != 1) ||
|
||||
( $config_max_freq < $config_min_freq)) {
|
||||
&uci_set_by_type($uci_config, $uci_section, $uci_option, $max_freq);
|
||||
$need_commit++;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_freq_list {
|
||||
my $policy_home = shift;
|
||||
my @ret_ary;
|
||||
open my $fh, "<", "${policy_home}/scaling_available_frequencies" or die;
|
||||
$_ = <$fh>;
|
||||
chomp;
|
||||
@ret_ary = split /\s+/;
|
||||
close($fh);
|
||||
return @ret_ary;
|
||||
}
|
||||
|
||||
sub get_freq_hash {
|
||||
my @freq_ary = @_;
|
||||
my %ret_hash;
|
||||
foreach my $freq (@freq_ary) {
|
||||
if($freq =~ m/\d+/) {
|
||||
$ret_hash{$freq} = 1;
|
||||
}
|
||||
}
|
||||
return %ret_hash;
|
||||
}
|
||||
|
||||
sub get_min_freq {
|
||||
my @freq_ary = @_;
|
||||
return (sort {$a<=>$b} @freq_ary)[0];
|
||||
}
|
||||
|
||||
sub get_max_freq {
|
||||
my @freq_ary = @_;
|
||||
return (sort {$a<=>$b} @freq_ary)[-1];
|
||||
}
|
||||
|
||||
sub get_gove_hash {
|
||||
my $policy_home = shift;
|
||||
my %ret_hash;
|
||||
open my $fh, "<", "${policy_home}/scaling_available_governors" or die;
|
||||
$_ = <$fh>;
|
||||
chomp;
|
||||
my @gov_ary = split /\s+/;
|
||||
foreach my $gov (@gov_ary) {
|
||||
#print "gov: $gov\n";
|
||||
if($gov =~ m/\w+/) {
|
||||
$ret_hash{$gov} = 1;
|
||||
}
|
||||
}
|
||||
close($fh);
|
||||
return %ret_hash;
|
||||
}
|
||||
|
||||
sub uci_get_by_type {
|
||||
my($config,$section,$option,$default) = @_;
|
||||
my $ret;
|
||||
$ret=`uci get ${config}.\@${section}\[0\].${option} 2>/dev/null`;
|
||||
# 消除回车换行
|
||||
$ret =~ s/[\n\r]//g;
|
||||
if($ret eq '') {
|
||||
return $default;
|
||||
} else {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
sub uci_set_by_type {
|
||||
my($config,$section,$option,$value) = @_;
|
||||
my $ret;
|
||||
system("uci set ${config}.\@${section}\[0\].${option}=${value}");
|
||||
return;
|
||||
}
|
||||
|
||||
sub uci_commit {
|
||||
my $config = shift;
|
||||
system("uci commit ${config}");
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
#!/bin/bash
|
||||
#======================================================================
|
||||
# Function: Backup and restore config files in the /etc directory
|
||||
# Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic
|
||||
#======================================================================
|
||||
|
||||
VERSION="v1.3"
|
||||
ZSTD_LEVEL=6
|
||||
SNAPSHOT_PRESTR=".snapshots/"
|
||||
BACKUP_DIR="/.reserved"
|
||||
BACKUP_NAME="openwrt_config.tar.gz"
|
||||
BACKUP_FILE="${BACKUP_DIR}/${BACKUP_NAME}"
|
||||
# Customize backup list
|
||||
backup_list_conf="/etc/amlogic_backup_list.conf"
|
||||
if [[ -s "${backup_list_conf}" ]]; then
|
||||
while IFS= read -r line; do
|
||||
BACKUP_LIST+="${line} "
|
||||
done <"${backup_list_conf}"
|
||||
else
|
||||
BACKUP_LIST='./etc/AdGuardHome.yaml \
|
||||
./etc/amlogic_backup_list.conf \
|
||||
./etc/adblocklist/ \
|
||||
./etc/amule/ \
|
||||
./etc/balance_irq \
|
||||
./etc/bluetooth/ \
|
||||
./etc/china_ssr.txt \
|
||||
./etc/cifs/cifsdpwd.db \
|
||||
./etc/smbd/smbdpwd.db \
|
||||
./etc/ksmbd/ksmbdpwd.db \
|
||||
./etc/config/ \
|
||||
./etc/crontabs/ \
|
||||
./etc/dae/ \
|
||||
./etc/daed/ \
|
||||
./usr/share/openclash/core/ \
|
||||
./etc/openclash/backup/ \
|
||||
./etc/openclash/config/ \
|
||||
./etc/openclash/custom/ \
|
||||
./etc/openclash/game_rules/ \
|
||||
./etc/openclash/rule_provider/ \
|
||||
./etc/openclash/proxy_provider/ \
|
||||
./etc/dnsforwarder/ \
|
||||
./etc/dnsmasq.conf \
|
||||
./etc/dnsmasq.d/ \
|
||||
./etc/dnsmasq.oversea/ \
|
||||
./etc/dnsmasq.ssr/ \
|
||||
./etc/docker/daemon.json \
|
||||
./etc/docker/key.json \
|
||||
./etc/dropbear/ \
|
||||
./etc/easy-rsa/ \
|
||||
./etc/environment \
|
||||
./etc/exports \
|
||||
./etc/firewall.user \
|
||||
./etc/gfwlist/ \
|
||||
./etc/haproxy.cfg \
|
||||
./etc/hosts \
|
||||
./etc/ipsec.conf \
|
||||
./etc/ipsec.d/ \
|
||||
./etc/ipsec.secrets \
|
||||
./etc/ipsec.user \
|
||||
./etc/ipset/ \
|
||||
./etc/mosdns/config.yaml \
|
||||
./etc/mwan3.user \
|
||||
./etc/nginx/nginx.conf \
|
||||
./etc/ocserv/ \
|
||||
./etc/openvpn/ \
|
||||
./etc/pptpd.conf \
|
||||
./etc/qBittorrent/ \
|
||||
./etc/rc.local \
|
||||
./etc/samba/smbpasswd \
|
||||
./etc/shadow \
|
||||
./etc/smartdns/ \
|
||||
./etc/sqm/ \
|
||||
./etc/ssh/*key* \
|
||||
./etc/ssl/private/ \
|
||||
./etc/ssrplus/ \
|
||||
./etc/sysupgrade.conf \
|
||||
./etc/tailscale/ \
|
||||
./etc/transmission/ \
|
||||
./etc/uhttpd.crt \
|
||||
./etc/uhttpd.key \
|
||||
./etc/urandom.seed \
|
||||
./etc/v2raya/ \
|
||||
./etc/verysync/ \
|
||||
./root/.ssh/'
|
||||
fi
|
||||
|
||||
error_msg() {
|
||||
echo -e " [ERROR] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if dmesg | grep 'meson' >/dev/null 2>&1; then
|
||||
PLATFORM="amlogic"
|
||||
elif dmesg | grep 'rockchip' >/dev/null 2>&1; then
|
||||
PLATFORM="rockchip"
|
||||
elif dmesg | grep 'sun50i-h6' >/dev/null 2>&1; then
|
||||
PLATFORM="allwinner"
|
||||
else
|
||||
source /etc/flippy-openwrt-release
|
||||
case ${PLATFORM} in
|
||||
amlogic | rockchip | allwinner | qemu-aarch64) : ;;
|
||||
*) error_msg "Unknown platform, only support amlogic or rockchip or allwinner h6 or qemu-aarch64!" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
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}" ]] && error_msg "Cannot find the root partition!"
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Get the partition message of the root file system
|
||||
get_root_partition_msg() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_msg=$(lsblk -l -o NAME,PATH,MOUNTPOINT,UUID,FSTYPE,LABEL | awk '$3 ~ "^" "'"${path}"'" "$" {print $0}')
|
||||
[[ -n "${partition_msg}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_msg}" ]] && error_msg "Cannot find the root partition message!"
|
||||
echo "${partition_msg}"
|
||||
}
|
||||
|
||||
backup() {
|
||||
cd /
|
||||
echo -n "Backup config files ... "
|
||||
[ -d "${BACKUP_DIR}" ] || mkdir -p "${BACKUP_DIR}"
|
||||
eval tar czf "${BACKUP_FILE}" "${BACKUP_LIST}" 2>/dev/null
|
||||
sync
|
||||
if [ -f "${BACKUP_FILE}" ]; then
|
||||
echo "Has been backed up to [ ${BACKUP_FILE} ], please download and save."
|
||||
exit 0
|
||||
else
|
||||
error_msg "Backup failed!"
|
||||
fi
|
||||
}
|
||||
|
||||
restore() {
|
||||
# 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"
|
||||
LB_PRE="EMMC_"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
EMMC_NAME=$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')
|
||||
PARTITION_NAME=""
|
||||
LB_PRE=""
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -d "${BACKUP_DIR}" ] || mkdir -p "${BACKUP_DIR}"
|
||||
[ -f "/tmp/upload/${BACKUP_NAME}" ] && mv -f "/tmp/upload/${BACKUP_NAME}" ${BACKUP_FILE}
|
||||
[ -f "/mnt/${EMMC_NAME}${PARTITION_NAME}4/${BACKUP_NAME}" ] && mv -f "/mnt/${EMMC_NAME}${PARTITION_NAME}4/${BACKUP_NAME}" ${BACKUP_FILE}
|
||||
sync
|
||||
|
||||
if [ -f "${BACKUP_FILE}" ]; then
|
||||
echo -n "restore config files ... "
|
||||
cd /
|
||||
tar xzf "${BACKUP_FILE}" 2>/dev/null && sync
|
||||
|
||||
echo "Successful recovery. Will start automatically, please refresh later!"
|
||||
sleep 3
|
||||
reboot
|
||||
exit 0
|
||||
else
|
||||
error_msg "The backup file [ ${BACKUP_FILE} ] not found!"
|
||||
fi
|
||||
}
|
||||
|
||||
gen_fstab() {
|
||||
# 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=""
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
ROOT_MSG="$(get_root_partition_msg)"
|
||||
ROOT_NAME=$(echo $ROOT_MSG | awk '{print $1}')
|
||||
ROOT_DEV=$(echo $ROOT_MSG | awk '{print $2}')
|
||||
ROOT_UUID=$(echo $ROOT_MSG | awk '{print $4}')
|
||||
ROOT_FSTYPE=$(echo $ROOT_MSG | awk '{print $5}')
|
||||
ROOT_LABEL=$(echo $ROOT_MSG | awk '{print $6}')
|
||||
|
||||
BOOT_NAME="${EMMC_NAME}${PARTITION_NAME}1"
|
||||
BOOT_MSG=$(lsblk -l -o NAME,UUID,FSTYPE,LABEL | grep "${BOOT_NAME}")
|
||||
BOOT_DEV="/dev/${BOOT_NAME}"
|
||||
BOOT_UUID=$(echo $BOOT_MSG | awk '{print $2}')
|
||||
BOOT_FSTYPE=$(echo $BOOT_MSG | awk '{print $3}')
|
||||
BOOT_LABEL=$(echo $BOOT_MSG | awk '{print $4}')
|
||||
|
||||
cat >/etc/config/fstab <<EOF
|
||||
config global
|
||||
option anon_swap '0'
|
||||
option anon_mount '1'
|
||||
option auto_swap '0'
|
||||
option auto_mount '1'
|
||||
option delay_root '5'
|
||||
option check_fs '0'
|
||||
|
||||
config mount
|
||||
option target '/rom'
|
||||
option uuid '${ROOT_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype '${ROOT_FSTYPE}'
|
||||
EOF
|
||||
|
||||
if [ "${ROOT_FSTYPE}" == "btrfs" ]; then
|
||||
echo " option options 'compress=zstd:${ZSTD_LEVEL}'" >>/etc/config/fstab
|
||||
fi
|
||||
|
||||
cat >>/etc/config/fstab <<EOF
|
||||
|
||||
config mount
|
||||
option target '/boot'
|
||||
EOF
|
||||
|
||||
if [ "${BOOT_FSTYPE}" == "vfat" ]; then
|
||||
echo " option label '${BOOT_LABEL}'" >>/etc/config/fstab
|
||||
else
|
||||
echo " option uuid '${BOOT_UUID}'" >>/etc/config/fstab
|
||||
fi
|
||||
|
||||
cat >>/etc/config/fstab <<EOF
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype '${BOOT_FSTYPE}'
|
||||
|
||||
EOF
|
||||
echo "/etc/config/fstab generated."
|
||||
echo "Please reboot before continuing."
|
||||
exit 0
|
||||
}
|
||||
|
||||
print_list() {
|
||||
echo "${BACKUP_LIST}"
|
||||
exit 0
|
||||
}
|
||||
|
||||
list_snapshot() {
|
||||
echo "----------------------------------------------------------------"
|
||||
btrfs subvolume list -rt /
|
||||
echo "----------------------------------------------------------------"
|
||||
read -p "Press [ enter ] to return." q
|
||||
}
|
||||
|
||||
create_snapshot() {
|
||||
default_snap_name="etc-$(date +"%m.%d.%H%M%S")"
|
||||
echo "The default snapshot name is: ${default_snap_name}"
|
||||
echo "If you want to modify the snapshot name, please enter it below. Cannot contain spaces."
|
||||
echo "If you do not want to modify it, just press [ Enter ]. Or press the [ q ] key to go back directly."
|
||||
while :; do
|
||||
read -p "[${default_snap_name}] : " nname
|
||||
if [ "${nname}" == "" ]; then
|
||||
snap_name="${default_snap_name}"
|
||||
break
|
||||
elif echo "${nname}" | grep -E "\s+" >/dev/null; then
|
||||
echo "The name [${nname}] contains spaces, please re-enter!"
|
||||
continue
|
||||
elif [ "${nname}" == "q" ] || [ "${nname}" == "Q" ]; then
|
||||
return
|
||||
else
|
||||
if btrfs subvolume list -rt / | awk '{print $4}' | grep "^\\${SNAPSHOT_PRESTR}${nname}$" >/dev/null; then
|
||||
echo "Name: [ ${nname} ] has been used, please re-enter!"
|
||||
continue
|
||||
else
|
||||
snap_name="${nname}"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cd /
|
||||
chattr -ia etc/config/fstab
|
||||
btrfs subvolume snapshot -r /etc "${SNAPSHOT_PRESTR}${snap_name}"
|
||||
if [[ "$?" -eq "0" ]]; then
|
||||
echo "The snapshot is created successfully: ${snap_name}"
|
||||
else
|
||||
echo "Snapshot creation failed!"
|
||||
fi
|
||||
)
|
||||
read -p "Press [ enter ] to return." q
|
||||
}
|
||||
|
||||
restore_snapshot() {
|
||||
echo "Below are the existing etc snapshots, please enter the name of one of them."
|
||||
echo "Tip: [ etc-000 ] This is the factory initial configuration."
|
||||
echo " [ etc-001 ] if it exists, it is the initial configuration after upgrading from the previous version."
|
||||
echo "----------------------------------------------------------------"
|
||||
btrfs subvolume list -rt /
|
||||
echo "----------------------------------------------------------------"
|
||||
read -p "Please enter the name of the snapshot to be restored (only the part after ${SNAPSHOT_PRESTR} needs to be entered): " snap_name
|
||||
if btrfs subvolume list -rt / | grep "${SNAPSHOT_PRESTR}${snap_name}" >/dev/null; then
|
||||
while :; do
|
||||
echo "Once the snapshot is restored, the current [ /etc ] will be overwritten!"
|
||||
read -p "Are you sure you want to restore the snapshot: [$snap_name]? y/n [n] " yn
|
||||
case $yn in
|
||||
y | Y)
|
||||
(
|
||||
cd /
|
||||
chattr -ia etc/config/fstab
|
||||
mv etc etc.backup
|
||||
btrfs subvolume snapshot "${SNAPSHOT_PRESTR}${snap_name}" etc
|
||||
if [[ "$?" -eq "0" ]]; then
|
||||
btrfs subvolume delete -c etc.backup
|
||||
echo "Successfully restored, please enter [ reboot ] to restart the openwrt."
|
||||
else
|
||||
rm -rf etc
|
||||
mv etc.backup etc
|
||||
echo "Recovery failed, [ etc ] has not changed!"
|
||||
fi
|
||||
)
|
||||
read -p "Press [ enter ] to return." q
|
||||
break
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
read -p "The snapshot name is incorrect, please run the program again! Press [ Enter ] to go back." q
|
||||
fi
|
||||
}
|
||||
|
||||
delete_snapshot() {
|
||||
echo "Below are the existing [ etc ] snapshots, please enter the name of one of them."
|
||||
echo "Tip: [ etc-000 ] This is the factory initial configuration (cannot be deleted)"
|
||||
echo " [ etc-001 ] if it exists, it is the initial configuration after upgrading from the previous version (cannot be deleted)"
|
||||
echo "----------------------------------------------------------------"
|
||||
btrfs subvolume list -rt /
|
||||
echo "----------------------------------------------------------------"
|
||||
read -p "Please enter the name of the snapshot to be deleted (only the part after ${SNAPSHOT_PRESTR} needs to be entered): " snap_name
|
||||
if [ "${snap_name}" == "etc-000" ] || [ "${snap_name}" == "etc-001" ]; then
|
||||
read -p "The key snapshot cannot be deleted! Press [ enter ] to return." q
|
||||
elif [ "${snap_name}" == "" ]; then
|
||||
read -p "Name is empty! Press [ enter ] to return." q
|
||||
else
|
||||
if btrfs subvolume list -rt / | grep "${SNAPSHOT_PRESTR}${snap_name}" >/dev/null; then
|
||||
read -p "Are you sure you want to delete ${snap_name}? y/n [n] " yn
|
||||
case $yn in
|
||||
y | Y)
|
||||
(
|
||||
cd /
|
||||
btrfs subvolume delete -c "${SNAPSHOT_PRESTR}${snap_name}"
|
||||
if [[ "$?" -eq "0" ]]; then
|
||||
echo "Snapshot [ ${snap_name} ] has been deleted."
|
||||
else
|
||||
echo "Snapshot [ ${snap_name} ] failed to delete!"
|
||||
fi
|
||||
)
|
||||
read -p "Press [ Enter ] to return." q
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
else
|
||||
read -p "The name of the snapshot is incorrect, press [ Enter ] to return." q
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
migrate_snapshot() {
|
||||
cur_rootdev="$(get_root_partition_name)"
|
||||
|
||||
dev_pre=$(echo "${cur_rootdev}" | awk '{print substr($1, 1, length($1)-1);}')
|
||||
rootdev_idx=$(echo "${cur_rootdev}" | awk '{print substr($1, length($1),1);}')
|
||||
case $rootdev_idx in
|
||||
2)
|
||||
old_rootpath="/mnt/${dev_pre}3"
|
||||
;;
|
||||
3)
|
||||
old_rootpath="/mnt/${dev_pre}2"
|
||||
;;
|
||||
*)
|
||||
echo "Judge the old version of rootfs path failed!"
|
||||
read -p "Press [ enter ] to return." q
|
||||
return
|
||||
;;
|
||||
esac
|
||||
echo "The following are snapshots of etc found from the old version of rootfs, please enter the name of one of them."
|
||||
echo "Tip: Automatically exclude etc-000 and etc-001"
|
||||
echo "-----------------------------------------------------------------------------------"
|
||||
btrfs subvolume list -rt "${old_rootpath}" | grep -v "${SNAPSHOT_PRESTR}etc-000" | grep -v "${SNAPSHOT_PRESTR}etc-001"
|
||||
echo "-----------------------------------------------------------------------------------"
|
||||
read -p "Please enter the name of the snapshot to be migrated (only the part after $(SNAPSHOT_PRESTR) needs to be entered): " old_snap_name
|
||||
if [ "${old_snap_name}" == "" ]; then
|
||||
read -p "The name is empty, Press [ enter ] to return." q
|
||||
return
|
||||
elif ! btrfs subvolume list -rt "${old_rootpath}" | awk '{print $4}' | grep "^${SNAPSHOT_PRESTR}${old_snap_name}$" >/dev/null; then
|
||||
echo "The name was entered incorrectly, and the corresponding snapshot was not found!"
|
||||
read -p "Press [ enter ] to return." q
|
||||
return
|
||||
elif [ "${old_snap_name}" == "etc-000" ] || [ "${old_snap_name}" == "etc-001" ]; then
|
||||
echo "Critical snapshots are not allowed to migrate!"
|
||||
read -p "Press [ enter ] to return." q
|
||||
return
|
||||
fi
|
||||
|
||||
# Find out if there is a snapshot with the same name under the current rootfs
|
||||
if btrfs subvolume list -rt / | awk '{print $4}' | grep "^\\${SNAPSHOT_PRESTR}${old_snap_name}$" >/dev/null; then
|
||||
echo "A snapshot with the name [ ${old_snap_name} ] already exists and cannot be migrated! (But you can delete the existing snapshot with the same name and then migrate)"
|
||||
read -p "Press [ enter ] to return." q
|
||||
return
|
||||
fi
|
||||
|
||||
need_size=$(du -h -d0 ${old_rootpath}/${SNAPSHOT_PRESTR}${old_snap_name} | tail -n1 | awk '{print $1}')
|
||||
echo "----------------------------------------------------------------------------------------------"
|
||||
df -h
|
||||
echo "----------------------------------------------------------------------------------------------"
|
||||
echo -e "Note: To migrate the snapshot [ ${old_snap_name} ] of [ ${old_rootpath} ] to the current rootfs, it takes about [ ${need_size} ] space,"
|
||||
echo -e " Please confirm whether the partition [/dev/${cur_rootdev}] where [/] is located has enough free space (Available)?"
|
||||
read -p "Are you sure to migrate? y/n [n] " yn
|
||||
if [ "$yn" == "y" ] || [ "$yn" == "Y" ]; then
|
||||
(
|
||||
cd /
|
||||
btrfs send ${old_rootpath}/${SNAPSHOT_PRESTR}${old_snap_name} | btrfs receive ${SNAPSHOT_PRESTR}
|
||||
if [ $? -eq 0 ]; then
|
||||
btrfs property set -ts ${SNAPSHOT_PRESTR}${old_snap_name} ro false
|
||||
cp ${SNAPSHOT_PRESTR}etc-000/config/fstab ${SNAPSHOT_PRESTR}${old_snap_name}/config/
|
||||
cp ${SNAPSHOT_PRESTR}etc-000/fstab ${SNAPSHOT_PRESTR}${old_snap_name}/
|
||||
cp ${SNAPSHOT_PRESTR}etc-000/openwrt_release ${SNAPSHOT_PRESTR}${old_snap_name}/
|
||||
cp ${SNAPSHOT_PRESTR}etc-000/openwrt_version ${SNAPSHOT_PRESTR}${old_snap_name}/
|
||||
cp ${SNAPSHOT_PRESTR}etc-000/flippy-openwrt-release ${SNAPSHOT_PRESTR}${old_snap_name}/
|
||||
cp ${SNAPSHOT_PRESTR}etc-000/banner ${SNAPSHOT_PRESTR}${old_snap_name}/banner
|
||||
btrfs property set -ts ${SNAPSHOT_PRESTR}${old_snap_name} ro true
|
||||
echo "The migration is complete, if you want to apply the snapshot [ ${old_snap_name} ], please use the restore snapshot function."
|
||||
else
|
||||
echo "The migration failed!"
|
||||
fi
|
||||
read -p "Press [ enter ] to return." q
|
||||
return
|
||||
)
|
||||
fi
|
||||
}
|
||||
|
||||
snapshot_help() {
|
||||
clear
|
||||
cat <<EOF
|
||||
============================================================================================================================
|
||||
1. What is a snapshot?
|
||||
A snapshot is the state record of a certain subvolume at a certain point in time, and a snapshot is a special subvolume;
|
||||
When the local snapshot is first generated, it shares the disk space with the original subvolume,
|
||||
so it does not occupy additional space, and only the files that have changed subsequently occupy space;
|
||||
Snapshots migrated from other places are not snapshots in essence, but just ordinary subvolumes, so they take up space.
|
||||
2. How to display existing snapshots?
|
||||
input the command: btrfs subvolume list -rt /
|
||||
---------------------------------------------------------------------------------------------
|
||||
EOF
|
||||
btrfs subvolume list -rt /
|
||||
cat <<EOF
|
||||
---------------------------------------------------------------------------------------------
|
||||
2. How to create a snapshot?
|
||||
btrfs subvolume snapshot -r /etc /.snapshots/snapshot_1 # -r It means to generate a read-only snapshot
|
||||
3. How to delete a snapshot?
|
||||
btrfs subvolume delete -c /.snapshots/snapshot_1 # -c 是 commit 的意思
|
||||
4. How to rename a snapshot?
|
||||
Just use the [ mv ] command of the operating system, for example:
|
||||
mv /.snapshots/snapshot_1 /.snapshots/snapshot_2
|
||||
5. How to restore the snapshot?
|
||||
mv /etc /etc.backup # Back up /etc to /etc.backup, that is, rename the subvolume
|
||||
btrfs subvolume snapshot /.snapshots/etc-001 /etc # Use snapshot etc-001 to generate snapshot etc, no -r parameter
|
||||
# (Yes, snapshots can also be re-generated snapshots)
|
||||
btrfs delete -c /etc.backup # After the previous step is successful, you can delete the etc backup just now
|
||||
6. How to restore a certain file from the snapshot?
|
||||
Example A: Restore the mount point configuration file from snapshot etc-000: /etc/config/fstab
|
||||
cp /.snapshots/etc-000/config/fstab /etc/config/
|
||||
Example B: Restore network configuration from snapshot etc-001:
|
||||
cp /.snapshots/etc-001/config/network /etc/config/
|
||||
Example C: Restore the ssr configuration file from snapshot etc-001:
|
||||
cp /.snapshots/etc-001/config/shadowsocksr /etc/config/
|
||||
# (Yes, it is the [ cp ] command of the operating system)
|
||||
7. How to migrate snapshots from remote?
|
||||
btrfs supports ssh remote migration snapshots, for example:
|
||||
ssh 192.168.1.1 btrfs send /.snapshots/snapshot_x | btrfs receive /.snapshots
|
||||
After the command is completed, the remote snapshot_x is copied to the local /.snapshots/snapshot_x
|
||||
(as mentioned above, the migration is a subvolume, which takes up space)
|
||||
============================================================================================================================
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
print_help() {
|
||||
echo "Usage: $0 -b [ backup ]"
|
||||
echo " $0 -r [ restore ]"
|
||||
echo " $0 -g [ generate fstab ]"
|
||||
echo " $0 -p [ print backup list ]"
|
||||
echo " $0 -l [ list snapshots ]"
|
||||
echo " $0 -c [ create snapshot ]"
|
||||
echo " $0 -s [ restore snapshot ]"
|
||||
echo " $0 -d [ delete snapshot ]"
|
||||
echo " $0 -h [ help ]"
|
||||
echo " $0 -q [ quit ]"
|
||||
exit 0
|
||||
}
|
||||
|
||||
menu() {
|
||||
while :; do
|
||||
clear
|
||||
cat <<EOF
|
||||
|
||||
┌────────[ backup config ]────────┐
|
||||
│ │
|
||||
│ b. backup config │
|
||||
│ r. restore config │
|
||||
│ g. generate fstab │
|
||||
│ p. print backup list │
|
||||
│ │
|
||||
├─────[ Snapshot management ]─────┤
|
||||
│ │
|
||||
│ l. list snapshots │
|
||||
│ c. create snapshot │
|
||||
│ d. delete snapshot │
|
||||
│ R. restore snapshot │
|
||||
│ m. migrate snapshot │
|
||||
│ s. snapshot help │
|
||||
│ │
|
||||
╞═════════════════════════════════╡
|
||||
│ │
|
||||
│ h. help │
|
||||
│ q. quit │
|
||||
│ │
|
||||
└─────────────────────────────────┘
|
||||
|
||||
EOF
|
||||
echo -ne "please select: [ ]\b\b"
|
||||
read select
|
||||
case $select in
|
||||
b | backup) backup ;;
|
||||
r | restore)
|
||||
restore
|
||||
gen_fstab
|
||||
;;
|
||||
g | gen_fstab) gen_fstab ;;
|
||||
p | print_list) print_list ;;
|
||||
l | list_snapshot) list_snapshot ;;
|
||||
c | create_snapshot) create_snapshot ;;
|
||||
d | delete_snapshot) delete_snapshot ;;
|
||||
R | restore_snapshot) restore_snapshot ;;
|
||||
m | migrate_snapshot) migrate_snapshot ;;
|
||||
s | snapshot_help) snapshot_help ;;
|
||||
h | help) print_help ;;
|
||||
q | quit) exit 0 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
getopts 'brgplcRmsdhq' opts
|
||||
case $opts in
|
||||
b | backup) backup ;;
|
||||
r | restore)
|
||||
restore
|
||||
gen_fstab
|
||||
;;
|
||||
g | gen_fstab) gen_fstab ;;
|
||||
p | print_list) print_list ;;
|
||||
l | list_snapshot) list_snapshot ;;
|
||||
c | create_snapshot) create_snapshot ;;
|
||||
d | delete_snapshot) delete_snapshot ;;
|
||||
R | restore_snapshot) restore_snapshot ;;
|
||||
m | migrate_snapshot) migrate_snapshot ;;
|
||||
s | snapshot_help) snapshot_help ;;
|
||||
h | help) print_help ;;
|
||||
q | quit) exit 0 ;;
|
||||
*) menu ;;
|
||||
esac
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/bin/bash
|
||||
#===========================================================================
|
||||
#
|
||||
# This file is licensed under the terms of the GNU General Public
|
||||
# License version 2. This program is licensed "as is" without any
|
||||
# warranty of any kind, whether express or implied.
|
||||
#
|
||||
# This file is a part of the make OpenWrt for Amlogic s9xxx tv box
|
||||
# https://github.com/ophub/luci-app-amlogic
|
||||
#
|
||||
# Description: Backup and restore the system in emmc
|
||||
# Copyright (C) 2017- The function borrowed from armbian/ddbr, Author: xXx
|
||||
# Copyright (C) 2021- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021- https://github.com/ophub/luci-app-amlogic
|
||||
#
|
||||
# Command: openwrt-ddbr
|
||||
#
|
||||
#========================= Set default parameters ===========================
|
||||
#
|
||||
# Set font color
|
||||
blue_font_prefix="\033[34m"
|
||||
purple_font_prefix="\033[35m"
|
||||
green_font_prefix="\033[32m"
|
||||
yellow_font_prefix="\033[33m"
|
||||
red_font_prefix="\033[31m"
|
||||
font_color_suffix="\033[0m"
|
||||
INFO="[${blue_font_prefix}INFO${font_color_suffix}]"
|
||||
STEPS="[${purple_font_prefix}STEPS${font_color_suffix}]"
|
||||
SUCCESS="[${green_font_prefix}SUCCESS${font_color_suffix}]"
|
||||
OPT="[${yellow_font_prefix}OPT${font_color_suffix}]"
|
||||
ERROR="[${red_font_prefix}ERROR${font_color_suffix}]"
|
||||
#
|
||||
# Check the output path
|
||||
out_path="/ddbr"
|
||||
# File name for backup/restore
|
||||
ddbr_image="BACKUP-arm-64-emmc.img.gz"
|
||||
# Need remaining space, unit: GB
|
||||
need_space="2"
|
||||
#
|
||||
#===========================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "${ERROR} ${1}"
|
||||
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}" ]] && error_msg "Cannot find the root partition!"
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Check emmc
|
||||
do_checkemmc() {
|
||||
# Get device name
|
||||
mydevice_name="$(cat /proc/device-tree/model | tr -d '\000')"
|
||||
echo -e "${INFO} The device name: [ ${mydevice_name} ]"
|
||||
|
||||
# Find the partition where root is located
|
||||
root_ptname="$(get_root_partition_name)"
|
||||
|
||||
# Find the EMMC drive
|
||||
emmc="$(lsblk -l -o NAME | grep -oE "mmcblk[0-9]boot0" | sort | uniq | sed "s/boot0//g")"
|
||||
# Find emmc disk, find emmc that does not contain the boot0 partition
|
||||
[[ -z "${emmc}" ]] && emmc="$(lsblk -l -o NAME | grep -oE '(mmcblk[0-9]?)' | grep -vE ^${root_ptname:0:-2} | uniq)"
|
||||
# Check if emmc exists
|
||||
[[ -z "${emmc}" ]] && error_msg "The eMMC storage not found in this device!"
|
||||
# Show the emmc name
|
||||
echo -e "${INFO} The device eMMC name: [ /dev/${emmc} ]"
|
||||
|
||||
# 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])
|
||||
disk_name="$(echo ${root_ptname} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
if lsblk -l -o NAME | grep "${disk_name}boot0" >/dev/null; then
|
||||
error_msg "The current system is running on emmc. Please perform backup/restore operation in [ SD/TF/USB ]!"
|
||||
fi
|
||||
link_ptname="p"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
disk_name="$(echo ${root_ptname} | awk '{print substr($1, 1, length($1)-1)}')"
|
||||
link_ptname=""
|
||||
;;
|
||||
nvme?n?p[1-4])
|
||||
disk_name="$(echo ${root_ptname} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
link_ptname="p"
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${root_ptname}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Set check parameters
|
||||
out_path="/mnt/${disk_name}${link_ptname}4"
|
||||
dev_intsize="$(fdisk -s /dev/${emmc})"
|
||||
[[ -z "$(echo "${dev_intsize}" | sed -n "/^[0-9]\+$/p")" ]] && error_msg "Unable to get EMMC size."
|
||||
echo -e "${INFO} The device EMMC size: [ $(($dev_intsize / 1024 / 1024))GB ]"
|
||||
|
||||
# check directory
|
||||
[[ -d "${out_path}" ]] || mkdir -p ${out_path}
|
||||
echo -e "${INFO} The ddbr file path: [ ${out_path}/${ddbr_image} ]\n"
|
||||
}
|
||||
|
||||
# Check the remaining space
|
||||
do_checkspace() {
|
||||
remaining_space="$(df -Tk ${out_path} | grep '/dev/' | awk '{print $5}' | echo $(($(xargs) / 1024 / 1024)))"
|
||||
if [[ -z "$(echo "${remaining_space}" | sed -n "/^[0-9]\+$/p")" ]]; then
|
||||
error_msg "The path is not available, the remaining space cannot be obtained."
|
||||
fi
|
||||
if [[ "${remaining_space}" -lt "${need_space}" ]]; then
|
||||
error_msg "The remaining space is [ ${remaining_space} ] Gb, and more than [ ${need_space} ] Gb space is required."
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup the emmc system
|
||||
do_backup() {
|
||||
echo -e "${STEPS} Start to backup the system in emmc."
|
||||
do_checkspace
|
||||
echo -e "Saving and Compressing [ /dev/${emmc} ] to [ ${out_path}/${ddbr_image} ], Please wait..."
|
||||
rm -f ${out_path}/${ddbr_image} 2>/dev/null && sync
|
||||
dd if=/dev/${emmc} | pv -s ${dev_intsize}"K" | gzip >${out_path}/${ddbr_image}
|
||||
[[ "${?}" -eq "0" ]] && sync && echo -e "${SUCCESS} Backup is complete."
|
||||
}
|
||||
|
||||
# Restore the emmc system
|
||||
do_restore() {
|
||||
echo -e "${STEPS} Start to restore the system in emmc."
|
||||
[[ ! -f ${out_path}/${ddbr_image} ]] && error_msg "The [ ${out_path}/${ddbr_image} ] File not found."
|
||||
echo -e "Restoring [ ${out_path}/${ddbr_image} ] to [ /dev/${emmc} ], Please wait..."
|
||||
gunzip -c ${out_path}/${ddbr_image} | pv -s ${dev_intsize}"K" | dd of=/dev/${emmc}
|
||||
[[ "${?}" -eq "0" ]] && sync && echo -e "${SUCCESS} Restore is complete."
|
||||
}
|
||||
|
||||
echo -e "${STEPS} Welcome to use the EMMC system backup/restore service."
|
||||
# Check script permission
|
||||
[[ -x "/usr/sbin/openwrt-ddbr" ]] || error_msg "Please grant execution permission: [ chmod +x /usr/sbin/openwrt-ddbr ]"
|
||||
# Check emmc
|
||||
do_checkemmc
|
||||
# Prompt the user to select backup/restore
|
||||
echo -ne "${OPT} Do you want to backup or restore? Backup=(b) Restore=(r): "
|
||||
read br
|
||||
case "${br}" in
|
||||
b | B | backup) do_backup ;;
|
||||
r | R | restore) do_restore ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
@@ -0,0 +1,721 @@
|
||||
#!/bin/bash
|
||||
#======================================================================================
|
||||
# Function: Install openwrt to emmc for Amlogic S9xxx STB
|
||||
# Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic
|
||||
#======================================================================================
|
||||
#
|
||||
# The script supports directly setting parameters for installation, skipping interactive selection
|
||||
# openwrt-install-amlogic ${AUTO_MAINLINE_UBOOT} ${ID} ${FDTFILE}:${SOC}:${UBOOT_OVERLOAD} ${SHARED_FSTYPE}
|
||||
# E.g: openwrt-install-amlogic yes 11 auto ext4
|
||||
# E.g: openwrt-install-amlogic no 99 meson-gxl-s905d-phicomm-n1.dtb:s905d:u-boot-n1.bin ext4
|
||||
# Tip: When custom dtb file, set ${SOC_ID} to 99, and parameter ${FDTFILE}:${SOC}:${UBOOT_OVERLOAD} must be set
|
||||
# Tip: ${SHARED_FSTYPE}: Shared partition can be ext4, xfs, btrfs, f2fs
|
||||
|
||||
# You can also execute the script directly, and interactively select related functions
|
||||
# E.g: openwrt-install-amlogic
|
||||
#
|
||||
#======================================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "[ERROR] ${1}"
|
||||
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}" ]] && error_msg "Cannot find the root partition!"
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Receive one-key command related parameters
|
||||
AUTO_MAINLINE_UBOOT="${1}"
|
||||
ZSTD_LEVEL="6"
|
||||
op_release="/etc/flippy-openwrt-release"
|
||||
|
||||
# For [luci-app-amlogic] input parameter: DTB, SOC & UBOOT_OVERLOAD
|
||||
# When there is no input parameter, select manually
|
||||
SPECIFY_ID=""
|
||||
SPECIFY_SOC=""
|
||||
SPECIFY_DTB=""
|
||||
SPECIFY_UBOOT=""
|
||||
[[ -n "${2}" ]] && {
|
||||
SPECIFY_ID="${2}"
|
||||
if [[ "${2}" -eq "99" ]]; then
|
||||
if [[ -n "${3}" ]]; then
|
||||
# E.g: meson-gxl-s905d-phicomm-n1.dtb:s905d:u-boot-n1.bin
|
||||
SPECIFY_DTB="$(echo "${3}" | awk -F ':' '{print $1}')"
|
||||
SPECIFY_SOC="$(echo "${3}" | awk -F ':' '{print $2}')"
|
||||
SPECIFY_UBOOT="$(echo "${3}" | awk -F ':' '{print $3}')"
|
||||
else
|
||||
error_msg "Please enter the DTB file name!"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# shared partition can be ext4, xfs, btrfs, f2fs
|
||||
SHARED_FSTYPE="${4}"
|
||||
|
||||
echo "AUTO_MAINLINE_UBOOT: ${AUTO_MAINLINE_UBOOT}"
|
||||
echo "SPECIFY_DTB: ${SPECIFY_DTB}"
|
||||
echo "SPECIFY_SOC: ${SPECIFY_SOC}"
|
||||
echo "SPECIFY_UBOOT: ${SPECIFY_UBOOT}"
|
||||
echo "SHARED_FSTYPE: ${SHARED_FSTYPE}"
|
||||
|
||||
# Current device model
|
||||
MYDEVICE_NAME=$(cat /proc/device-tree/model | tr -d '\000')
|
||||
if [[ -z "${MYDEVICE_NAME}" ]]; then
|
||||
error_msg "The device name is empty and cannot be recognized."
|
||||
elif [[ ! -f "${op_release}" ]]; then
|
||||
error_msg "The [ ${op_release} ] file is missing."
|
||||
else
|
||||
echo -e "Current device: ${MYDEVICE_NAME} [ amlogic ]"
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Find the device name of /
|
||||
root_devname="$(get_root_partition_name)"
|
||||
if lsblk -l | grep -E "^${root_devname}boot0" >/dev/null; then
|
||||
error_msg "you are running in emmc mode, please boot system with usb or tf card!"
|
||||
fi
|
||||
|
||||
install_emmc="$(lsblk -l -o NAME | grep -oE '(mmcblk[0-9]?boot0)' | sed "s/boot0//g")"
|
||||
if [[ "${install_emmc}" == "" ]]; then
|
||||
error_msg "No emmc can be found to install the openwrt system!"
|
||||
fi
|
||||
|
||||
# EMMC DEVICE NAME
|
||||
EMMC_NAME="${install_emmc}"
|
||||
EMMC_DEVPATH="/dev/${EMMC_NAME}"
|
||||
echo ${EMMC_DEVPATH}
|
||||
EMMC_SIZE=$(lsblk -l -b -o NAME,SIZE | grep ${EMMC_NAME} | sort | uniq | head -n1 | awk '{print $2}')
|
||||
echo "${EMMC_NAME} : ${EMMC_SIZE} bytes"
|
||||
|
||||
ROOT_NAME=$(lsblk -l -o NAME,MAJ:MIN,MOUNTPOINT | grep -e '/$' | awk '{print $1}')
|
||||
echo "ROOTFS: ${ROOT_NAME}"
|
||||
|
||||
BOOT_NAME=$(lsblk -l -o NAME,MAJ:MIN,MOUNTPOINT | grep -e '/boot$' | awk '{print $1}')
|
||||
echo "BOOT: ${BOOT_NAME}"
|
||||
|
||||
# box model database
|
||||
# The field separator is :
|
||||
# " " or "" or NA or NULL means this field is null
|
||||
# The fields list:
|
||||
# 1. id
|
||||
# 2. model name
|
||||
# 3. SOC
|
||||
# 4. FDTFILE
|
||||
# 5. UBOOT_OVERLOAD
|
||||
# 6. MAINLINE_UBOOT
|
||||
# 7. ANDROID_UBOOT
|
||||
# 8. brief description
|
||||
#
|
||||
|
||||
# allow use external modal database
|
||||
if [[ -f "/etc/model_database.txt" ]]; then
|
||||
model_database="$(cat /etc/model_database.txt)"
|
||||
else
|
||||
error_msg "[ /etc/model_database.txt ] file is missing."
|
||||
fi
|
||||
|
||||
function display_database() {
|
||||
while read -r line; do
|
||||
if [[ "$line" =~ ^# ]]; then
|
||||
# Process comment lines, starting with #
|
||||
line="${line/#+\s+/}"
|
||||
echo "$line >>>"
|
||||
else
|
||||
# Process data lines, starting with id
|
||||
IFS=':' read -r -a fields <<<"$line"
|
||||
printf "%5s %-48s%-10s%-s\n" "${fields[0]}" "${fields[1]}" "${fields[2]}" "${fields[7]}"
|
||||
fi
|
||||
done < <(echo "${model_database}")
|
||||
}
|
||||
|
||||
function search_model() {
|
||||
local id="${1}"
|
||||
local ret_count="$(echo "${model_database}" | awk -F ':' "\$1~/^$id\$/ {print \$0}" | wc -l)"
|
||||
if [[ "${ret_count}" -eq "1" ]]; then
|
||||
echo "${model_database}" | awk -F ':' "\$1~/^$id\$/ {print \$0}" | sed -e 's/NA//g' -e 's/NULL//g' -e 's/[ ][ ]*//g'
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Please select s9xxx box model:"
|
||||
echo "----------------------------------------------------------------------------------------------------"
|
||||
display_database
|
||||
echo "----------------------------------------------------------------------------------------------------"
|
||||
|
||||
# For [luci-app-amlogic] input parameter: SOC & DTB
|
||||
# When there is no input parameter, select manually
|
||||
if [[ -n "${SPECIFY_ID}" ]]; then
|
||||
boxtype="${SPECIFY_ID}"
|
||||
else
|
||||
echo -n "Please choose: "
|
||||
read boxtype
|
||||
fi
|
||||
|
||||
if [[ "${boxtype}" -eq "99" ]]; then
|
||||
FDTFILE="${SPECIFY_DTB}"
|
||||
AMLOGIC_SOC="${SPECIFY_SOC}"
|
||||
UBOOT_OVERLOAD="${SPECIFY_UBOOT}"
|
||||
MAINLINE_UBOOT=""
|
||||
ANDROID_UBOOT=""
|
||||
elif [[ "${boxtype}" -eq "0" ]]; then
|
||||
read -p "Please Input SoC Name(such as s9xxx): " AMLOGIC_SOC
|
||||
AMLOGIC_SOC="${AMLOGIC_SOC}"
|
||||
|
||||
read -p "Please Input DTB Name(such as meson-xxx.dtb): " FDTFILE
|
||||
FDTFILE="${FDTFILE}"
|
||||
|
||||
read -p "Please Input UBOOT_OVERLOAD Name(such as u-boot-xxx.bin): " UBOOT_OVERLOAD
|
||||
UBOOT_OVERLOAD="${UBOOT_OVERLOAD}"
|
||||
|
||||
read -p "Please Input MAINLINE_UBOOT Name(such as /lib/u-boot/xxx-u-boot.bin.sd.bin): " MAINLINE_UBOOT
|
||||
MAINLINE_UBOOT="${MAINLINE_UBOOT}"
|
||||
|
||||
read -p "Please Input ANDROID_UBOOT Name(such as /lib/u-boot/xxx-bootloader.img): " ANDROID_UBOOT
|
||||
ANDROID_UBOOT="${ANDROID_UBOOT}"
|
||||
else
|
||||
ret="$(search_model ${boxtype})"
|
||||
if [[ -z "${ret}" ]]; then
|
||||
error_msg "Input error, exit!"
|
||||
fi
|
||||
# 3. soc
|
||||
# 4. FDTFILE
|
||||
# 5. UBOOT_OVERLOAD
|
||||
# 6. MAINLINE_UBOOT
|
||||
# 7. ANDROID_UBOOT
|
||||
AMLOGIC_SOC="$(echo "${ret}" | awk -F ':' '{print $3}')"
|
||||
FDTFILE="$(echo "${ret}" | awk -F ':' '{print $4}')"
|
||||
UBOOT_OVERLOAD="$(echo "${ret}" | awk -F ':' '{print $5}')"
|
||||
MAINLINE_UBOOT="$(echo "${ret}" | awk -F ':' '{print $6}')"
|
||||
ANDROID_UBOOT="$(echo "${ret}" | awk -F ':' '{print $7}')"
|
||||
fi
|
||||
|
||||
if [[ -z "${FDTFILE}" || ! -f "/boot/dtb/amlogic/${FDTFILE}" ]]; then
|
||||
error_msg "/boot/dtb/amlogic/${FDTFILE} does not exist!"
|
||||
fi
|
||||
|
||||
echo "AMLOGIC_SOC Value [ ${AMLOGIC_SOC} ]"
|
||||
echo "FDTFILE Value [ ${FDTFILE} ]"
|
||||
echo "UBOOT_OVERLOAD Value [ ${UBOOT_OVERLOAD} ]"
|
||||
echo "MAINLINE_UBOOT Value [ ${MAINLINE_UBOOT} ]"
|
||||
echo "ANDROID_UBOOT Value [ ${ANDROID_UBOOT} ]"
|
||||
|
||||
sed -i "s|^SOC=.*|SOC='${AMLOGIC_SOC}'|g" ${op_release} 2>/dev/null
|
||||
sed -i "s|^FDTFILE=.*|FDTFILE='${FDTFILE}'|g" ${op_release} 2>/dev/null
|
||||
sed -i "s|^UBOOT_OVERLOAD=.*|UBOOT_OVERLOAD='${UBOOT_OVERLOAD}'|g" ${op_release} 2>/dev/null
|
||||
sed -i "s|^MAINLINE_UBOOT=.*|MAINLINE_UBOOT='${MAINLINE_UBOOT}'|g" ${op_release} 2>/dev/null
|
||||
sed -i "s|^ANDROID_UBOOT=.*|ANDROID_UBOOT='${ANDROID_UBOOT}'|g" ${op_release} 2>/dev/null
|
||||
|
||||
K510="1"
|
||||
[[ "$(hexdump -n 15 -x "/boot/zImage" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0"
|
||||
echo -e "K510 [ ${K510} ]"
|
||||
|
||||
# backup old bootloader
|
||||
if [[ ! -f "/root/BackupOldBootloader.img" ]]; then
|
||||
echo "Backup bootloader -> [ BackupOldBootloader.img ] ... "
|
||||
dd if=/dev/$EMMC_NAME of=/root/BackupOldBootloader.img bs=1M count=4 conv=fsync
|
||||
echo "Backup bootloader complete."
|
||||
echo
|
||||
fi
|
||||
|
||||
swapoff -a
|
||||
|
||||
# umount all other mount points
|
||||
MOUNTS=$(lsblk -l -o MOUNTPOINT)
|
||||
for mnt in $MOUNTS; do
|
||||
if [ "$mnt" == "MOUNTPOINT" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$mnt" == "" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$mnt" == "/" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$mnt" == "/boot" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$mnt" == "/opt" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$mnt" == "[SWAP]" ]; then
|
||||
echo "swapoff -a"
|
||||
swapoff -a
|
||||
continue
|
||||
fi
|
||||
|
||||
if echo $mnt | grep $EMMC_NAME; then
|
||||
echo "umount -f $mnt"
|
||||
umount -f $mnt
|
||||
if [ $? -ne 0 ]; then
|
||||
error_msg "$mnt Cannot be uninstalled, the installation process is aborted."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete old partition if exists
|
||||
p=$(lsblk -l | grep -e "${EMMC_NAME}p" | wc -l)
|
||||
echo "A total of [ $p ] old partitions on EMMC will be deleted"
|
||||
>/tmp/fdisk.script
|
||||
while [ $p -ge 1 ]; do
|
||||
echo "d" >>/tmp/fdisk.script
|
||||
if [ $p -gt 1 ]; then
|
||||
echo "$p" >>/tmp/fdisk.script
|
||||
fi
|
||||
p=$((p - 1))
|
||||
done
|
||||
|
||||
# you can change ROOT size(MB) >= 320
|
||||
ROOT1="1280"
|
||||
ROOT2="1280"
|
||||
if [[ "${AMLOGIC_SOC}" == "s912" ]] && [[ "${boxtype}" == "213" || "${boxtype}" == "2e" ]]; then
|
||||
BOOT="512"
|
||||
BLANK1="700"
|
||||
BLANK2="220"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${AMLOGIC_SOC}" == "s912" || "${AMLOGIC_SOC}" == "s905d" ]]; then
|
||||
BOOT="512"
|
||||
BLANK1="68"
|
||||
BLANK2="220"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${AMLOGIC_SOC}" == "s905x" ]]; then
|
||||
BOOT="160"
|
||||
BLANK1="700"
|
||||
BLANK2="0"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${FDTFILE}" == "meson-sm1-skyworth-lb2004-a4091.dtb" ]]; then
|
||||
BOOT="512"
|
||||
BLANK1="108"
|
||||
BLANK2="562"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${AMLOGIC_SOC}" == "s905l3a" ]] && [[ "${boxtype}" == "304" || "${boxtype}" == "34" ]]; then
|
||||
# e900v22c/d(s905l3a)
|
||||
BOOT="256"
|
||||
BLANK1="570"
|
||||
BLANK2="0"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${AMLOGIC_SOC}" == "s905l3a" ]] && [[ "${boxtype}" == "305" || "${boxtype}" == "33" ]]; then
|
||||
# CM311-1a-YST(s905l3a)
|
||||
BOOT="512"
|
||||
BLANK1="108"
|
||||
BLANK2="778"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${AMLOGIC_SOC}" == "s905l3b" ]]; then
|
||||
# M302A/M304A(s905l3b)
|
||||
BOOT="512"
|
||||
BLANK1="128"
|
||||
BLANK2="720"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${AMLOGIC_SOC}" == "s905x3" ]] && [[ "${boxtype}" == "525" || "${boxtype}" == "5n" ]]; then
|
||||
# Whale(s905x3)
|
||||
BOOT="512"
|
||||
BLANK1="108"
|
||||
BLANK2="650"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
elif [[ "${boxtype}" =~ ^(409|410|49|4a)$ ]]; then
|
||||
# WXY-OES(A311D):409/49, WXY-OES-PLUS(S922X):410/4a
|
||||
BOOT="512"
|
||||
BLANK1="700"
|
||||
BLANK2="0"
|
||||
BLANK3="0"
|
||||
BLANK4="0"
|
||||
else
|
||||
BOOT="160"
|
||||
BLANK1="68"
|
||||
BLANK2="0"
|
||||
BLANK3="162"
|
||||
BLANK4="0"
|
||||
fi
|
||||
|
||||
DST_TOTAL_MB=$((EMMC_SIZE / 1024 / 1024))
|
||||
|
||||
start1=$((BLANK1 * 2048))
|
||||
end1=$((start1 + (BOOT * 2048) - 1))
|
||||
|
||||
start2=$(((BLANK2 * 2048) + end1 + 1))
|
||||
end2=$((start2 + (ROOT1 * 2048) - 1))
|
||||
|
||||
start3=$(((BLANK3 * 2048) + end2 + 1))
|
||||
end3=$((start3 + (ROOT2 * 2048) - 1))
|
||||
|
||||
start4=$(((BLANK4 * 2048) + end3 + 1))
|
||||
end4=$((DST_TOTAL_MB * 2048 - 1))
|
||||
|
||||
cat >>/tmp/fdisk.script <<EOF
|
||||
n
|
||||
p
|
||||
1
|
||||
$start1
|
||||
$end1
|
||||
n
|
||||
p
|
||||
2
|
||||
$start2
|
||||
$end2
|
||||
n
|
||||
p
|
||||
3
|
||||
$start3
|
||||
$end3
|
||||
n
|
||||
p
|
||||
$start4
|
||||
$end4
|
||||
t
|
||||
1
|
||||
c
|
||||
t
|
||||
2
|
||||
83
|
||||
t
|
||||
3
|
||||
83
|
||||
t
|
||||
4
|
||||
83
|
||||
w
|
||||
EOF
|
||||
|
||||
fdisk /dev/${EMMC_NAME} </tmp/fdisk.script 2>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "The fdisk partition fails, Please try again."
|
||||
dd if=/root/BackupOldBootloader.img of=/dev/${EMMC_NAME} conv=fsync && sync
|
||||
dd if=/dev/zero of=/dev/${EMMC_NAME} bs=512 count=1 && sync
|
||||
exit 1
|
||||
fi
|
||||
echo "Partition complete."
|
||||
|
||||
# write some zero data to part begin
|
||||
seek=$((start1 / 2048))
|
||||
dd if=/dev/zero of=/dev/${EMMC_NAME} bs=1M count=1 seek=$seek conv=fsync
|
||||
|
||||
seek=$((start2 / 2048))
|
||||
dd if=/dev/zero of=/dev/${EMMC_NAME} bs=1M count=1 seek=$seek conv=fsync
|
||||
|
||||
seek=$((start3 / 2048))
|
||||
dd if=/dev/zero of=/dev/${EMMC_NAME} bs=1M count=1 seek=$seek conv=fsync
|
||||
|
||||
seek=$((start4 / 2048))
|
||||
dd if=/dev/zero of=/dev/${EMMC_NAME} bs=1M count=1 seek=$seek conv=fsync
|
||||
|
||||
#Mainline U-BOOT detection
|
||||
FLASH_MAINLINE_UBOOT=0
|
||||
if [[ -n "${MAINLINE_UBOOT}" && -f "${MAINLINE_UBOOT}" ]]; then
|
||||
cat <<EOF
|
||||
----------------------------------------------------------------------------------
|
||||
Found an available mainline bootloader (Mainline u-boot), you can flash into EMMC.
|
||||
----------------------------------------------------------------------------------
|
||||
EOF
|
||||
while :; do
|
||||
# For [luci-app-amlogic] input parameter: SOC & DTB
|
||||
# When there is no input parameter, select manually
|
||||
if [[ "${AUTO_MAINLINE_UBOOT}" == "yes" ]]; then
|
||||
if [[ "${K510}" -eq "1" ]]; then
|
||||
yn="y"
|
||||
else
|
||||
yn="n"
|
||||
fi
|
||||
elif [[ "${AUTO_MAINLINE_UBOOT}" == "no" ]]; then
|
||||
yn="n"
|
||||
else
|
||||
read -p "Please choose whether to write the mainline bootloader to EMMC? y/n " yn
|
||||
fi
|
||||
case $yn in
|
||||
y | Y)
|
||||
FLASH_MAINLINE_UBOOT=1
|
||||
break
|
||||
;;
|
||||
n | N)
|
||||
FLASH_MAINLINE_UBOOT=0
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "${FLASH_MAINLINE_UBOOT}" -eq "1" ]]; then
|
||||
echo -e "Write Mainline bootloader: [ ${MAINLINE_UBOOT} ]"
|
||||
dd if=${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=1 count=444 conv=fsync
|
||||
dd if=${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=512 skip=1 seek=1 conv=fsync
|
||||
elif [[ -n "${ANDROID_UBOOT}" && -f "${ANDROID_UBOOT}" ]]; then
|
||||
echo -e "Write Android bootloader: [ ${ANDROID_UBOOT} ]"
|
||||
dd if=${ANDROID_UBOOT} of=/dev/${EMMC_NAME} bs=1 count=444 conv=fsync
|
||||
dd if=${ANDROID_UBOOT} of=/dev/${EMMC_NAME} bs=512 skip=1 seek=1 conv=fsync
|
||||
else
|
||||
echo "Did not change the original bootloader."
|
||||
fi
|
||||
|
||||
# fix wifi macaddr
|
||||
if [ -x /usr/bin/fix_wifi_macaddr.sh ]; then
|
||||
/usr/bin/fix_wifi_macaddr.sh
|
||||
fi
|
||||
|
||||
# mkfs
|
||||
echo "Start creating file system ... "
|
||||
echo "Create a boot file system ... "
|
||||
|
||||
echo "format boot partiton..."
|
||||
mkfs.fat -n EMMC_BOOT -F 32 /dev/${EMMC_NAME}p1
|
||||
mkdir -p /mnt/${EMMC_NAME}p1
|
||||
sleep 2
|
||||
umount -f /mnt/${EMMC_NAME}p1 2>/dev/null
|
||||
|
||||
echo "format rootfs1 partiton..."
|
||||
ROOTFS1_UUID=$(/usr/bin/uuidgen)
|
||||
mkfs.btrfs -f -U ${ROOTFS1_UUID} -L EMMC_ROOTFS1 -m single /dev/${EMMC_NAME}p2
|
||||
mkdir -p /mnt/${EMMC_NAME}p2
|
||||
sleep 2
|
||||
umount -f /mnt/${EMMC_NAME}p2 2>/dev/null
|
||||
|
||||
echo "format rootfs2 partiton..."
|
||||
ROOTFS2_UUID=$(/usr/bin/uuidgen)
|
||||
mkfs.btrfs -f -U ${ROOTFS2_UUID} -L EMMC_ROOTFS2 -m single /dev/${EMMC_NAME}p3
|
||||
mkdir -p /mnt/${EMMC_NAME}p3
|
||||
sleep 2
|
||||
umount -f /mnt/${EMMC_NAME}p3 2>/dev/null
|
||||
|
||||
# mount and copy
|
||||
echo "Wait for the boot file system to mount ... "
|
||||
i=1
|
||||
max_try=10
|
||||
while [ $i -le $max_try ]; do
|
||||
mount -t vfat /dev/${EMMC_NAME}p1 /mnt/${EMMC_NAME}p1 2>/dev/null
|
||||
sleep 2
|
||||
mnt=$(lsblk -l -o MOUNTPOINT | grep /mnt/${EMMC_NAME}p1)
|
||||
|
||||
if [ "$mnt" == "" ]; then
|
||||
if [ $i -lt $max_try ]; then
|
||||
echo "Not mounted successfully, try again ..."
|
||||
i=$((i + 1))
|
||||
else
|
||||
error_msg "Cannot mount the boot file system, give up!"
|
||||
fi
|
||||
else
|
||||
echo "Successfully mounted."
|
||||
echo "copy boot ..."
|
||||
cd /mnt/${EMMC_NAME}p1
|
||||
rm -rf /boot/'System Volume Information/'
|
||||
(cd /boot && tar cf - .) | tar xf -
|
||||
sync
|
||||
|
||||
echo "Edit uEnv.txt ..."
|
||||
[[ -f "uEnv.txt" ]] && {
|
||||
sed -i -E \
|
||||
-e "s|/dtb/amlogic.*|/dtb/amlogic/${FDTFILE}|" \
|
||||
-e "s|UUID=[^ ]*|UUID=${ROOTFS1_UUID}|" \
|
||||
-e "s|rootflags=compress=zstd:[^ ]*|rootflags=compress=zstd:${ZSTD_LEVEL}|" \
|
||||
uEnv.txt
|
||||
}
|
||||
|
||||
# Replace the extlinux/extlinux.conf if it exists
|
||||
[[ -f "extlinux/extlinux.conf" ]] && {
|
||||
sed -i -E \
|
||||
-e "s|/dtb/amlogic.*|/dtb/amlogic/${FDTFILE}|" \
|
||||
-e "s|UUID=[^ ]*|UUID=${ROOTFS1_UUID}|" \
|
||||
-e "s|rootflags=compress=zstd:[^ ]*|rootflags=compress=zstd:${ZSTD_LEVEL}|" \
|
||||
extlinux/extlinux.conf
|
||||
}
|
||||
|
||||
rm -f s905_autoscript* aml_autoscript*
|
||||
|
||||
if [ ${K510} -eq 1 ]; then
|
||||
if [ -f ${UBOOT_OVERLOAD} ]; then
|
||||
cp -f -v ${UBOOT_OVERLOAD} u-boot.emmc
|
||||
elif [ -f "u-boot.ext" ]; then
|
||||
cp -f -v u-boot.ext u-boot.emmc
|
||||
fi
|
||||
fi
|
||||
|
||||
mv -f boot-emmc.ini boot.ini
|
||||
mv -f boot-emmc.cmd boot.cmd
|
||||
mv -f boot-emmc.scr boot.scr
|
||||
|
||||
sync
|
||||
echo "complete."
|
||||
cd /
|
||||
umount -f /mnt/${EMMC_NAME}p1
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "complete."
|
||||
|
||||
echo "Wait for the rootfs file system to mount ... "
|
||||
i=1
|
||||
while [ $i -le $max_try ]; do
|
||||
mount -t btrfs -o compress=zstd:${ZSTD_LEVEL} /dev/${EMMC_NAME}p2 /mnt/${EMMC_NAME}p2 2>/dev/null
|
||||
sleep 2
|
||||
mnt=$(lsblk -l -o MOUNTPOINT | grep /mnt/${EMMC_NAME}p2)
|
||||
if [ "$mnt" == "" ]; then
|
||||
if [ $i -lt $max_try ]; then
|
||||
echo "Not mounted successfully, try again ..."
|
||||
i=$((i + 1))
|
||||
else
|
||||
error_msg "Cannot mount rootfs file system, give up!"
|
||||
fi
|
||||
else
|
||||
echo "Successfully mounted"
|
||||
echo "Create folder ... "
|
||||
cd /mnt/${EMMC_NAME}p2
|
||||
btrfs subvolume create etc
|
||||
mkdir -p bin boot dev lib opt mnt overlay proc rom root run sbin sys tmp usr www .reserved .snapshots
|
||||
ln -sf lib/ lib64
|
||||
ln -sf tmp/ var
|
||||
sync
|
||||
echo "complete."
|
||||
|
||||
COPY_SRC="root etc bin sbin lib opt usr www"
|
||||
echo "Copy data ... "
|
||||
for src in $COPY_SRC; do
|
||||
echo "copy [ $src ] ..."
|
||||
(cd / && tar cf - $src) | tar xf -
|
||||
sync
|
||||
done
|
||||
echo "Copy complete."
|
||||
sync
|
||||
|
||||
cat >etc/docker/daemon.json <<EOF
|
||||
{
|
||||
"bip": "172.31.0.1/24",
|
||||
"data-root": "/mnt/${EMMC_NAME}p4/docker/",
|
||||
"log-level": "warn",
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "5"
|
||||
},
|
||||
"registry-mirrors": [
|
||||
"https://mirror.baidubce.com/",
|
||||
"https://hub-mirror.c.163.com"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
# change data_root value in /etc/config/dockerd
|
||||
if [[ -f "/etc/init.d/dockerman" ]] && [[ -f "/etc/config/dockerd" ]]; then
|
||||
sed -i "s|option data_root.*|option data_root '/mnt/${EMMC_NAME}p4/docker/'|g" etc/config/dockerd
|
||||
fi
|
||||
rm -rf opt/docker && ln -sf /mnt/${EMMC_NAME}p4/docker/ opt/docker >/dev/null
|
||||
rm -rf usr/bin/AdGuardHome && ln -sf /mnt/${EMMC_NAME}p4/AdGuardHome usr/bin/ >/dev/null
|
||||
|
||||
echo "Edit configuration file ..."
|
||||
#cd /mnt/${EMMC_NAME}p2/usr/bin/
|
||||
#rm -f openwrt-install-amlogic openwrt-update-amlogic
|
||||
cd /mnt/${EMMC_NAME}p2/etc/rc.d
|
||||
ln -sf ../init.d/dockerd S99dockerd
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
cd /mnt/${EMMC_NAME}p2/etc
|
||||
cat >fstab <<EOF
|
||||
UUID=${ROOTFS1_UUID} / btrfs compress=zstd:${ZSTD_LEVEL} 0 1
|
||||
LABEL=EMMC_BOOT /boot vfat defaults 0 2
|
||||
#tmpfs /tmp tmpfs defaults,nosuid 0 0
|
||||
EOF
|
||||
|
||||
cd /mnt/${EMMC_NAME}p2/etc/config
|
||||
cat >fstab <<EOF
|
||||
config global
|
||||
option anon_swap '0'
|
||||
option anon_mount '1'
|
||||
option auto_swap '0'
|
||||
option auto_mount '1'
|
||||
option delay_root '5'
|
||||
option check_fs '0'
|
||||
|
||||
config mount
|
||||
option target '/rom'
|
||||
option uuid '${ROOTFS1_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'btrfs'
|
||||
option options 'compress=zstd:${ZSTD_LEVEL}'
|
||||
|
||||
config mount
|
||||
option target '/boot'
|
||||
option label 'EMMC_BOOT'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'vfat'
|
||||
|
||||
EOF
|
||||
|
||||
echo -n "Create initial etc snapshot -> .snapshots/etc-000"
|
||||
cd /mnt/${EMMC_NAME}p2 &&
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-000
|
||||
sync
|
||||
|
||||
cd /
|
||||
umount -f /mnt/${EMMC_NAME}p2
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "complete."
|
||||
|
||||
echo "Create a shared file system."
|
||||
mkdir -p /mnt/${EMMC_NAME}p4
|
||||
|
||||
# When there is no input parameter, select manually
|
||||
if [[ -n "${SHARED_FSTYPE}" ]]; then
|
||||
TARGET_SHARED_FSTYPE=${SHARED_FSTYPE}
|
||||
else
|
||||
cat <<EOF
|
||||
---------------------------------------------------------------------------------
|
||||
Please select the type of shared file system:
|
||||
1. ext4: [Default options] suitable for general use.
|
||||
2. btrfs: Which can extend the service life of ssd/mmc.
|
||||
3. f2fs: Fast reading and writing speed, but the compatibility is slightly poor.
|
||||
4. xfs: Very good file system, alternative to ext4.
|
||||
---------------------------------------------------------------------------------
|
||||
EOF
|
||||
read -p "Please Input ID: " TARGET_SHARED_FSTYPE
|
||||
fi
|
||||
case $TARGET_SHARED_FSTYPE in
|
||||
2 | btrfs)
|
||||
mkfs.btrfs -f -L EMMC_SHARED -m single /dev/${EMMC_NAME}p4 >/dev/null
|
||||
mount -t btrfs /dev/${EMMC_NAME}p4 /mnt/${EMMC_NAME}p4
|
||||
;;
|
||||
3 | f2fs)
|
||||
mkfs.f2fs -f -l EMMC_SHARED /dev/${EMMC_NAME}p4 >/dev/null
|
||||
mount -t f2fs /dev/${EMMC_NAME}p4 /mnt/${EMMC_NAME}p4
|
||||
;;
|
||||
4 | xfs)
|
||||
mkfs.xfs -f -L EMMC_SHARED /dev/${EMMC_NAME}p4 >/dev/null
|
||||
mount -t xfs /dev/${EMMC_NAME}p4 /mnt/${EMMC_NAME}p4
|
||||
;;
|
||||
*)
|
||||
mkfs.ext4 -F -L EMMC_SHARED /dev/${EMMC_NAME}p4 >/dev/null
|
||||
mount -t ext4 /dev/${EMMC_NAME}p4 /mnt/${EMMC_NAME}p4
|
||||
;;
|
||||
esac
|
||||
mkdir -p /mnt/${EMMC_NAME}p4/docker /mnt/${EMMC_NAME}p4/AdGuardHome/data
|
||||
sync
|
||||
|
||||
echo "Successful installed, please unplug the USB, re-insert the power supply to start the openwrt."
|
||||
exit 0
|
||||
@@ -0,0 +1,516 @@
|
||||
#!/bin/bash
|
||||
#====================================================================================
|
||||
#
|
||||
# Function: Update the kernel for OpenWrt (Amlogic s9xxx, Allwinner, Rockchip)
|
||||
# Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic
|
||||
#
|
||||
# Support the kernel: boot-*.tar.gz, dtb-*.tar.gz, modules-*.tar.gz
|
||||
# It is recommended to install MAINLINE_UBOOT for kernel versions above 5.10.y
|
||||
# openwrt-kernel ${AUTO_MAINLINE_UBOOT}
|
||||
# E.g: openwrt-kernel yes
|
||||
# openwrt-kernel no
|
||||
#
|
||||
#================================== Functions list ==================================
|
||||
#
|
||||
# error_msg : Output error message
|
||||
# get_textoffset : Get kernel TEXT_OFFSET
|
||||
# init_var : Initialize all variables
|
||||
# check_kernel : Check kernel files list
|
||||
# chech_files_same : Check file consistency
|
||||
# restore_kernel : Restore current kernel
|
||||
# update_kernel : Update the kernel
|
||||
# update_uboot : Update the uboot
|
||||
#
|
||||
#============================== Set default parameters ==============================
|
||||
#
|
||||
# Receive one-key command related parameters
|
||||
AUTO_MAINLINE_UBOOT="no"
|
||||
# Set the release check file
|
||||
release_file="/etc/flippy-openwrt-release"
|
||||
#
|
||||
#====================================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "[Error] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the /boot file system
|
||||
get_boot_partition_name() {
|
||||
local paths="/boot"
|
||||
local partition_name
|
||||
|
||||
partition_name=$(df "${paths}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Get kernel TEXT_OFFSET, For u-boot.ext and u-boot.emmc
|
||||
get_textoffset() {
|
||||
boot_tgz_file="${1}"
|
||||
vmlinuz_name="${2}"
|
||||
K510="1"
|
||||
temp_dir="$(mktemp -d)"
|
||||
(
|
||||
cd ${temp_dir}
|
||||
tar --no-same-owner -xf "${boot_tgz_file}" "${vmlinuz_name}"
|
||||
)
|
||||
# With TEXT_OFFSET patch is [ 0108 ], without TEXT_OFFSET patch is [ 0000 ]
|
||||
[[ "$(hexdump -n 15 -x "${temp_dir}/${vmlinuz_name}" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0"
|
||||
}
|
||||
|
||||
init_var() {
|
||||
# Receive one-key command related parameters
|
||||
[[ "${1}" == "yes" ]] && AUTO_MAINLINE_UBOOT="yes"
|
||||
|
||||
# Check dependencies
|
||||
[[ -n "$(busybox which tar)" ]] || error_msg "Missing [ tar ] in OpenWrt firmware, unable to update kernel"
|
||||
|
||||
# Check release file
|
||||
if [[ -s "${release_file}" ]]; then
|
||||
source "${release_file}"
|
||||
PLATFORM="${PLATFORM}"
|
||||
MODEL_ID="${MODEL_ID}"
|
||||
UBOOT_OVERLOAD="${UBOOT_OVERLOAD}"
|
||||
MAINLINE_UBOOT="${MAINLINE_UBOOT}"
|
||||
ANDROID_UBOOT="${ANDROID_UBOOT}"
|
||||
SOC="${SOC}"
|
||||
LOCK_KERNEL="${LOCK_KERNEL}"
|
||||
else
|
||||
error_msg "${release_file} file is missing!"
|
||||
fi
|
||||
[[ -n "${PLATFORM}" ]] || error_msg "Missing ${PLATFORM} value in ${release_file} file."
|
||||
|
||||
# Define supported platforms
|
||||
support_platform=("allwinner" "rockchip" "amlogic" "qemu-aarch64")
|
||||
[[ -n "$(echo "${support_platform[@]}" | grep -w "${PLATFORM}")" ]] || error_msg "[ ${PLATFORM} ] is not supported."
|
||||
|
||||
# Check and repair partition mount points
|
||||
[[ -z "$(get_boot_partition_name)" || -z "$(get_root_partition_name)" ]] && {
|
||||
echo -e "Partition mount point error, start repairing..."
|
||||
openwrt-backup -g
|
||||
}
|
||||
|
||||
# Set /boot/vmlinuz-* replication names for different SoCs
|
||||
MYBOOT_VMLINUZ="$(ls -l /boot/*Image 2>/dev/null | awk '{print $9}' | head -n 1)"
|
||||
MYBOOT_VMLINUZ="${MYBOOT_VMLINUZ##*/}"
|
||||
[[ -n "${MYBOOT_VMLINUZ}" ]] || error_msg "Failed to get Image name: [ ${MYBOOT_VMLINUZ} ]"
|
||||
|
||||
# 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-9])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
PARTITION_NAME="p"
|
||||
;;
|
||||
[hsv]d[a-z][1-9])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')"
|
||||
PARTITION_NAME=""
|
||||
;;
|
||||
nvme?n?p[1-9])
|
||||
EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')"
|
||||
PARTITION_NAME="p"
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
P4_PATH="/mnt/${EMMC_NAME}${PARTITION_NAME}4"
|
||||
# Move kernel related files to the ${P4_PATH} directory
|
||||
mv -f /tmp/upload/* ${P4_PATH} 2>/dev/null
|
||||
|
||||
# Current device model
|
||||
MYDEVICE_NAME="$(cat /proc/device-tree/model | tr -d '\000')"
|
||||
[[ "${PLATFORM}" == "qemu-aarch64" ]] && MYDEVICE_NAME="KVM Virtual Machine"
|
||||
echo -e "Current device: ${MYDEVICE_NAME} [ ${PLATFORM} ], Use in [ ${EMMC_NAME} ]"
|
||||
sync && echo ""
|
||||
}
|
||||
|
||||
# Check kernel files list
|
||||
check_kernel() {
|
||||
cd ${P4_PATH}
|
||||
|
||||
# Determine custom kernel filename
|
||||
kernel_boot="$(ls boot-*.tar.gz 2>/dev/null | head -n 1)"
|
||||
kernel_name="${kernel_boot:5:-7}"
|
||||
|
||||
KERNEL_VERSION="$(echo ${kernel_name} | grep -oE '^[1-9].[0-9]{1,3}.[0-9]+')"
|
||||
echo -e "Kernel name: ${kernel_name}"
|
||||
|
||||
# check if kernel is locked
|
||||
if [ -n "${LOCK_KERNEL}" ]; then
|
||||
if [ "${LOCK_KERNEL}" != "${kernel_name}" ]; then
|
||||
if ! echo "${kernel_name}" | grep -E '^5.10.\d+-.*?rk35.*?$' >/dev/null; then
|
||||
error_msg "The kernel version is locked to [ ${LOCK_KERNEL} ], but your kernel is [ ${kernel_name} ]. "
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if the file is added with TEXT_OFFSET patch
|
||||
get_textoffset "${P4_PATH}/${kernel_boot}" "vmlinuz-${kernel_name}"
|
||||
echo -e "K510 [ ${K510} ]"
|
||||
if [[ "${PLATFORM}" == "amlogic" && "${K510}" -eq "1" ]]; then
|
||||
[[ -n "${UBOOT_OVERLOAD}" && -f "/boot/${UBOOT_OVERLOAD}" ]] || error_msg "The UBOOT_OVERLOAD file is missing and cannot be update."
|
||||
fi
|
||||
|
||||
# Check the sha256sums file
|
||||
sha256sums_file="sha256sums"
|
||||
sha256sums_check="1"
|
||||
[[ -s "${sha256sums_file}" && -n "$(cat ${sha256sums_file})" ]] || sha256sums_check="0"
|
||||
[[ -n "$(busybox which sha256sum)" ]] || sha256sums_check="0"
|
||||
[[ "${sha256sums_check}" -eq "1" ]] && echo -e "Enable sha256sum checking..."
|
||||
|
||||
# Loop check file
|
||||
i="1"
|
||||
if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then
|
||||
kernel_list=("boot" "modules")
|
||||
else
|
||||
kernel_list=("boot" "dtb-${PLATFORM}" "modules")
|
||||
fi
|
||||
for kernel_file in ${kernel_list[*]}; do
|
||||
# Set check filename
|
||||
tmp_file="${kernel_file}-${kernel_name}.tar.gz"
|
||||
# Check if file exists
|
||||
[[ -s "${tmp_file}" ]] || error_msg "The [ ${kernel_file} ] file is missing."
|
||||
# Check if the file sha256sum is correct
|
||||
if [[ "${sha256sums_check}" -eq "1" ]]; then
|
||||
tmp_sha256sum="$(sha256sum "${tmp_file}" | awk '{print $1}')"
|
||||
tmp_checkcode="$(cat ${sha256sums_file} | grep ${tmp_file} | awk '{print $1}')"
|
||||
[[ "${tmp_sha256sum}" == "${tmp_checkcode}" ]] || error_msg "${tmp_file}: sha256sum verification failed."
|
||||
echo -e "(${i}/3) [ ${tmp_file} ] file sha256sum check same."
|
||||
fi
|
||||
let i++
|
||||
done
|
||||
|
||||
sync && echo ""
|
||||
}
|
||||
|
||||
# Check the consistency of amlogic device files
|
||||
chech_files_same() {
|
||||
i="0"
|
||||
max_try="5"
|
||||
while [[ "${i}" -le "${max_try}" ]]; do
|
||||
if [[ "$(sha256sum "${1}" | awk '{print $1}')" == "$(sha256sum "${2}" | awk '{print $1}')" ]]; then
|
||||
echo "" && break
|
||||
else
|
||||
cp -f ${1} ${2}
|
||||
i="$((i + 1))"
|
||||
fi
|
||||
done
|
||||
[[ "${i}" -gt "${max_try}" ]] && echo "God, it's different after ${max_try} copies: [ ${1} ]"
|
||||
}
|
||||
|
||||
# Restore the kernel when the update fails
|
||||
restore_kernel() {
|
||||
(
|
||||
cd /boot
|
||||
rm -rf \
|
||||
config-${kernel_name} \
|
||||
System.map-${kernel_name} \
|
||||
initrd.img-${kernel_name} \
|
||||
uInitrd-${kernel_name} \
|
||||
vmlinuz-${kernel_name} \
|
||||
initrd.img \
|
||||
uInitrd \
|
||||
zImage \
|
||||
Image \
|
||||
vmlinuz \
|
||||
dtb* 2>/dev/null
|
||||
tar -xzf /tmp/boot-backup.tar.gz 2>/dev/null
|
||||
)
|
||||
error_msg "Kernel update failed and has been reverted."
|
||||
}
|
||||
|
||||
# Update the kernel
|
||||
update_kernel() {
|
||||
local cur_kernel_name=$(uname -r)
|
||||
local boot_fstype=$(df -T /boot | tail -n1 | awk '{print $2}')
|
||||
echo -e "Start unpacking the kernel..."
|
||||
|
||||
# 01. for /boot five files
|
||||
# Backup the current_kernel
|
||||
(
|
||||
cd /boot
|
||||
tar -czf /tmp/boot-backup.tar.gz \
|
||||
config-${cur_kernel_name} \
|
||||
System.map-${cur_kernel_name} \
|
||||
initrd.img-${cur_kernel_name} \
|
||||
uInitrd-${cur_kernel_name} \
|
||||
vmlinuz-${cur_kernel_name} \
|
||||
initrd.img \
|
||||
uInitrd \
|
||||
zImage \
|
||||
Image \
|
||||
vmlinuz \
|
||||
dtb* 2>/dev/null
|
||||
rm -rf \
|
||||
config-${cur_kernel_name} \
|
||||
System.map-${cur_kernel_name} \
|
||||
initrd.img-${cur_kernel_name} \
|
||||
uInitrd-${cur_kernel_name} \
|
||||
vmlinuz-${cur_kernel_name} \
|
||||
initrd.img \
|
||||
uInitrd \
|
||||
zImage \
|
||||
Image \
|
||||
vmlinuz \
|
||||
dtb* 2>/dev/null
|
||||
)
|
||||
# Extract the new kernel
|
||||
tar --no-same-owner -xf ${P4_PATH}/boot-${kernel_name}.tar.gz -C /boot
|
||||
|
||||
# Check if the file exists
|
||||
local valid_files
|
||||
if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then
|
||||
valid_files="vmlinuz-${kernel_name} initrd.img-${kernel_name} config-${kernel_name} System.map-${kernel_name}"
|
||||
rm -f /boot/uInitrd*
|
||||
else
|
||||
valid_files="vmlinuz-${kernel_name} uInitrd-${kernel_name} config-${kernel_name} System.map-${kernel_name}"
|
||||
# wxy-oect: MODEL_ID numbers r306,r307 and w[0-9]{3}, require special handling of uInitrd
|
||||
[[ "${MODEL_ID}" =~ ^(r306|r307|w[0-9]{3})$ ]] || rm -f /boot/initrd.img*
|
||||
fi
|
||||
for f in ${valid_files}; do [[ -f "/boot/${f}" ]] || restore_kernel; done
|
||||
|
||||
# Check if the files are the same
|
||||
(
|
||||
cd /boot
|
||||
if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then
|
||||
ln -sf initrd.img-${kernel_name} initrd.img
|
||||
ln -sf vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ}
|
||||
elif [[ "$boot_fstype" == "vfat" ]]; then
|
||||
cp -f uInitrd-${kernel_name} uInitrd
|
||||
[[ -z "$(chech_files_same uInitrd-${kernel_name} uInitrd)" ]] || restore_kernel
|
||||
cp -f vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ}
|
||||
[[ -z "$(chech_files_same vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ})" ]] || restore_kernel
|
||||
else
|
||||
ln -sf uInitrd-${kernel_name} uInitrd
|
||||
ln -sf vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ}
|
||||
fi
|
||||
|
||||
# wxy-oect: MODEL_ID numbers r306 and r307, require special handling of uInitrd
|
||||
[[ "${MODEL_ID}" =~ ^(r306|r307)$ ]] && ln -sf initrd.img-${kernel_name} uInitrd
|
||||
)
|
||||
|
||||
echo -e "(1/3) Unpacking [ boot-${kernel_name}.tar.gz ] done."
|
||||
|
||||
if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then
|
||||
echo -e "(2/3) skip unpack dtb files."
|
||||
else
|
||||
# 02. for /boot/dtb/${PLATFORM}/*
|
||||
if [[ "${boot_fstype}" == "vfat" ]]; then
|
||||
(cd /boot && mkdir -p dtb/${PLATFORM})
|
||||
else
|
||||
(cd /boot && mkdir -p dtb-${kernel_name}/${PLATFORM} && ln -sf dtb-${kernel_name} dtb)
|
||||
fi
|
||||
tar --no-same-owner -xf ${P4_PATH}/dtb-${PLATFORM}-${kernel_name}.tar.gz -C /boot/dtb/${PLATFORM}
|
||||
[[ "$(ls /boot/dtb/${PLATFORM} -l 2>/dev/null | grep "^-" | wc -l)" -ge "1" ]] || error_msg "/boot/dtb/${PLATFORM} file is missing."
|
||||
echo -e "(2/3) Unpacking [ dtb-${PLATFORM}-${kernel_name}.tar.gz ] done."
|
||||
fi
|
||||
|
||||
# 03. for /lib/modules/*
|
||||
rm -rf /lib/modules/*
|
||||
tar --no-same-owner -xf ${P4_PATH}/modules-${kernel_name}.tar.gz -C /lib/modules
|
||||
(
|
||||
cd /lib/modules/${kernel_name}
|
||||
rm -f *.ko
|
||||
find ./ -type f -name '*.ko' -exec ln -s {} ./ \;
|
||||
sync && sleep 3
|
||||
x=$(ls *.ko -l 2>/dev/null | grep "^l" | wc -l)
|
||||
[[ "${x}" -eq "0" ]] && error_msg "Error *.ko Files not found."
|
||||
)
|
||||
echo -e "(3/3) Unpacking [ modules-${kernel_name}.tar.gz ] done."
|
||||
|
||||
# Delete kernel tmpfiles
|
||||
rm -f ${P4_PATH}/*-${kernel_name}.tar.gz
|
||||
rm -f ${P4_PATH}/sha256sums
|
||||
|
||||
sync && echo ""
|
||||
}
|
||||
|
||||
# Update the uboot
|
||||
update_uboot() {
|
||||
# Only amlogic SoCs needs to be updated
|
||||
if [[ "${PLATFORM}" == "amlogic" ]]; then
|
||||
# Copy u-boot.ext and u-boot.emmc
|
||||
if [[ "${K510}" -eq "1" && -n "${UBOOT_OVERLOAD}" && -f "/boot/${UBOOT_OVERLOAD}" ]]; then
|
||||
[[ ! -f "/boot/u-boot.ext" ]] && cp -f "/boot/${UBOOT_OVERLOAD}" /boot/u-boot.ext && chmod +x /boot/u-boot.ext
|
||||
[[ ! -f "/boot/u-boot.emmc" ]] && cp -f "/boot/u-boot.ext" /boot/u-boot.emmc && chmod +x /boot/u-boot.emmc
|
||||
echo -e "The ${UBOOT_OVERLOAD} file copy is complete."
|
||||
elif [[ "${K510}" -eq "0" ]]; then
|
||||
rm -f "/boot/u-boot.ext" "/boot/u-boot.emmc"
|
||||
fi
|
||||
|
||||
# Write Mainline bootloader
|
||||
if [[ -f "${MAINLINE_UBOOT}" && "${AUTO_MAINLINE_UBOOT}" == "yes" ]]; then
|
||||
echo -e "Write Mainline bootloader: [ ${MAINLINE_UBOOT} ] to [ /dev/${EMMC_NAME} ]"
|
||||
dd if=${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=1 count=442 conv=fsync
|
||||
dd if=${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=512 skip=1 seek=1 conv=fsync
|
||||
echo -e "The MAINLINE_UBOOT file write is complete."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update release file
|
||||
sed -i "s|^KERNEL_VERSION=.*|KERNEL_VERSION='${kernel_name}'|g" ${release_file} 2>/dev/null
|
||||
# Update banner file
|
||||
sed -i "s| Kernel.*| Kernel: ${kernel_name}|g" /etc/banner 2>/dev/null
|
||||
|
||||
sync && echo ""
|
||||
}
|
||||
|
||||
# Rescue the kernel
|
||||
sos_kernel() {
|
||||
echo -e "Start rescuing kernel..."
|
||||
|
||||
# Supports specifying disks, such as: [ openwrt-kernel -s mmcblk1 ]
|
||||
box_disk="${2}"
|
||||
|
||||
if [[ -n "${box_disk}" ]]; then
|
||||
# Format the disk names
|
||||
box_disk="${box_disk//\/dev\//}"
|
||||
|
||||
# Check if the disk exists
|
||||
[[ -b "/dev/${box_disk}" ]] || error_msg "The specified disk [ ${box_disk} ] does not exist."
|
||||
|
||||
# Check if the disk is the same as the current system disk
|
||||
[[ "${box_disk}" == "${EMMC_NAME}" ]] && error_msg "The specified disk [ ${box_disk} ] is the same as the current system disk [ ${EMMC_NAME} ]."
|
||||
|
||||
echo -e "The device name of the specified disk: [ ${box_disk} ]"
|
||||
else
|
||||
# Find emmc disk, first find emmc containing boot0 partition
|
||||
box_disk="$(lsblk -l -o NAME | grep -oE '(mmcblk[0-9]?|nvme[0-9]?n[0-9]?|[hsv]d[a-z])' | grep -vE ^${EMMC_NAME} | sort -u | head -n 1)"
|
||||
|
||||
# Check if disk exists
|
||||
[[ -z "${box_disk}" ]] && error_msg "Unable to locate the storage requiring rescue."
|
||||
|
||||
echo -e "The device name of the target disk: [ ${box_disk} ]"
|
||||
fi
|
||||
|
||||
rescue_disk="/dev/${box_disk}"
|
||||
echo -e "The current OpenWrt is running on [ /dev/${EMMC_NAME} ], and the target disk for restoration is [ ${rescue_disk} ]."
|
||||
|
||||
# Create a temporary mount directory
|
||||
umount ${P4_PATH}/bootfs 2>/dev/null
|
||||
umount ${P4_PATH}/rootfs 2>/dev/null
|
||||
rm -rf ${P4_PATH}/bootfs ${P4_PATH}/rootfs 2>/dev/null
|
||||
mkdir -p ${P4_PATH}/{bootfs/,rootfs/} && sync
|
||||
[[ "${?}" -ne "0" ]] && error_msg "Failed to create temporary mount directory [ ${P4_PATH} ]"
|
||||
|
||||
# Mount target bootfs partition
|
||||
[[ "${box_disk}" =~ ^([hsv]d[a-z]) ]] && rescue_disk_partition_name="" || rescue_disk_partition_name="p"
|
||||
mount ${rescue_disk}${rescue_disk_partition_name}1 ${P4_PATH}/bootfs
|
||||
[[ "${?}" -ne "0" ]] && error_msg "mount ${rescue_disk}${PARTITION_NAME}1 failed!"
|
||||
echo -e "The [ ${rescue_disk}${rescue_disk_partition_name}1 ] partition is mounted on [ ${P4_PATH}/bootfs ]."
|
||||
|
||||
# Search uuid file
|
||||
if [[ -f "${P4_PATH}/bootfs/uEnv.txt" ]]; then
|
||||
search_file="uEnv.txt"
|
||||
elif [[ -f "${P4_PATH}/bootfs/armbianEnv.txt" ]]; then
|
||||
search_file="armbianEnv.txt"
|
||||
elif [[ -f "${P4_PATH}/bootfs/extlinux/extlinux.conf" ]]; then
|
||||
search_file="extlinux/extlinux.conf"
|
||||
else
|
||||
error_msg "The [ uEnv.txt, armbianEnv.txt, extlinux/extlinux.conf ] file does not exist, stop rescuing."
|
||||
fi
|
||||
|
||||
# Get the target partition uuid and rootfs
|
||||
target_parttion_uuid="$(grep '=UUID=' ${P4_PATH}/bootfs/${search_file} | sed -n 's/.*=UUID=\([a-f0-9-]*\).*/\1/p')"
|
||||
[[ -z "${target_parttion_uuid}" ]] && error_msg "The [ ${search_file} ] file does not contain the UUID value."
|
||||
target_rootfs="$(blkid | grep ${target_parttion_uuid} | awk -F':' '{print $1;}')"
|
||||
[[ -z "${target_rootfs}" ]] && error_msg "The [ ${target_parttion_uuid} ] UUID does not exist in the system."
|
||||
|
||||
# Mount target rootfs partition
|
||||
mount ${target_rootfs} ${P4_PATH}/rootfs
|
||||
[[ "${?}" -ne "0" ]] && error_msg "mount ${rescue_disk}${PARTITION_NAME}2 failed!"
|
||||
echo -e "The [ ${target_rootfs} ] partition is mounted on [ ${P4_PATH}/rootfs ]."
|
||||
|
||||
# Identify the current kernel files
|
||||
kernel_signature="$(uname -r)"
|
||||
|
||||
# 01. For /boot files
|
||||
[[ -d "${P4_PATH}/bootfs" ]] && {
|
||||
cd ${P4_PATH}/bootfs
|
||||
rm -rf config-* initrd.img-* System.map-* vmlinuz-* uInitrd* *Image dtb* u-boot.ext u-boot.emmc
|
||||
[[ -f "/boot/u-boot.ext" ]] && {
|
||||
cp -f /boot/u-boot.ext .
|
||||
cp -f /boot/u-boot.ext u-boot.emmc
|
||||
chmod +x u-boot.ext u-boot.emmc
|
||||
}
|
||||
cp -rf /boot/{*-${kernel_signature},uInitrd,*Image,dtb} .
|
||||
[[ "${?}" -ne "0" ]] && error_msg "(1/2) [ boot ] kernel files rescue failed."
|
||||
echo -e "(1/2) [ boot ] kernel files rescue succeeded."
|
||||
|
||||
[[ -f "/boot/emmc_autoscript.cmd" ]] && cp -f /boot/emmc_autoscript.cmd .
|
||||
[[ -f "/boot/emmc_autoscript" ]] && cp -f /boot/emmc_autoscript .
|
||||
[[ -f "/boot/s905_autoscript.cmd" ]] && cp -f /boot/s905_autoscript.cmd .
|
||||
[[ -f "/boot/s905_autoscript" ]] && cp -f /boot/s905_autoscript .
|
||||
} || error_msg "(1/2) The [ ${P4_PATH}/bootfs ] folder does not exist, stop rescuing."
|
||||
|
||||
# 02. For /lib/modules/${kernel_signature}
|
||||
[[ -d "${P4_PATH}/rootfs/lib/modules" ]] && {
|
||||
cd ${P4_PATH}/rootfs/lib/modules
|
||||
rm -rf *
|
||||
cp -rf /lib/modules/${kernel_signature} .
|
||||
[[ "${?}" -ne "0" ]] && error_msg "(2/2) [ modules ] kernel files rescue failed."
|
||||
echo -e "(2/2) [ modules ] kernel files rescue succeeded."
|
||||
} || error_msg "(2/2) The [ ${P4_PATH}/rootfs/lib/modules ] folder does not exist, stop rescuing."
|
||||
|
||||
# Unmount the emmc partition
|
||||
cd ${P4_PATH}
|
||||
umount -f ${P4_PATH}/bootfs
|
||||
[[ "${?}" -ne "0" ]] && error_msg "Failed to umount [ ${P4_PATH}/bootfs ]"
|
||||
umount -f ${P4_PATH}/rootfs
|
||||
[[ "${?}" -ne "0" ]] && error_msg "Failed to umount [ ${P4_PATH}/rootfs ]"
|
||||
# Remove the temporary mount directory
|
||||
rm -rf ${P4_PATH}/bootfs ${P4_PATH}/rootfs
|
||||
|
||||
sync && echo ""
|
||||
}
|
||||
|
||||
echo -e "Welcome to the OpenWrt Kernel Management Tool."
|
||||
# Operation environment check
|
||||
[[ -x "/usr/sbin/openwrt-kernel" ]] || error_msg "Please grant execution permission: chmod +x /usr/sbin/openwrt-kernel"
|
||||
|
||||
# Execute relevant functions based on the options
|
||||
if [[ "${@}" =~ ^-s(\s)* ]]; then
|
||||
# Initialize all variables
|
||||
init_var "${@}"
|
||||
# Start rescuing the kernel
|
||||
sos_kernel "${@}"
|
||||
|
||||
# Kernel restore successful
|
||||
sync && sleep 3
|
||||
echo -e "Kernel rescue successful, please remove the disk and restart the OpenWrt system."
|
||||
exit 0
|
||||
else
|
||||
# Initialize all variables
|
||||
init_var "${@}"
|
||||
# Check kernel files list
|
||||
check_kernel
|
||||
# Update the kernel
|
||||
update_kernel
|
||||
# Update the uboot
|
||||
update_uboot
|
||||
|
||||
# Kernel update successful
|
||||
sync && sleep 3
|
||||
echo "Successfully updated, automatic restarting..."
|
||||
reboot
|
||||
exit 0
|
||||
fi
|
||||
@@ -0,0 +1,662 @@
|
||||
#!/bin/bash
|
||||
#======================================================================================
|
||||
# Function: Update openwrt to emmc for Allwinner STB
|
||||
# Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic
|
||||
#======================================================================================
|
||||
#
|
||||
# The script supports directly setting parameters for update, skipping interactive selection
|
||||
# openwrt-update-allwinner ${OPENWRT_FILE} ${AUTO_MAINLINE_UBOOT} ${RESTORE_CONFIG}
|
||||
# E.g: openwrt-update-allwinner openwrt_s905d.img.gz yes restore
|
||||
# E.g: openwrt-update-allwinner openwrt_s905d.img.gz no no-restore
|
||||
|
||||
# You can also execute the script directly, and interactively select related functions
|
||||
# E.g: openwrt-update-allwinner
|
||||
#
|
||||
#======================================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "[ERROR] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the /boot file system
|
||||
get_boot_partition_name() {
|
||||
local paths="/boot"
|
||||
local partition_name
|
||||
|
||||
partition_name=$(df "${paths}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Get the partition message of the root file system
|
||||
get_root_partition_msg() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_msg=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ "^" "'"${path}"'" "$" {print $0}')
|
||||
[[ -n "${partition_msg}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_msg}" ]] && error_msg "Cannot find the root partition message!"
|
||||
echo "${partition_msg}"
|
||||
}
|
||||
|
||||
# Receive one-key command related parameters
|
||||
IMG_NAME=${1}
|
||||
AUTO_MAINLINE_UBOOT=${2}
|
||||
BACKUP_RESTORE_CONFIG=${3}
|
||||
|
||||
# Current FDT file
|
||||
if [[ -f "/boot/uEnv.txt" ]]; then
|
||||
source /boot/uEnv.txt 2>/dev/null
|
||||
MYDTB_FDTFILE=$(basename $FDT)
|
||||
elif [[ -f "/boot/armbianEnv.txt" ]]; then
|
||||
source /boot/armbianEnv.txt 2>/dev/null
|
||||
MYDTB_FDTFILE="$(basename $fdtfile)"
|
||||
elif [[ -f "/etc/flippy-openwrt-release" ]]; then
|
||||
source /etc/flippy-openwrt-release 2>/dev/null
|
||||
MYDTB_FDTFILE="${FDTFILE}"
|
||||
fi
|
||||
|
||||
[[ -z "${MYDTB_FDTFILE}" ]] && error_msg "Invalid FDTFILE: [ ${MYDTB_FDTFILE} ]"
|
||||
|
||||
# Current device model
|
||||
MYDEVICE_NAME=$(cat /proc/device-tree/model | tr -d '\000')
|
||||
if [[ -z "${MYDEVICE_NAME}" ]]; then
|
||||
error_msg "The device name is empty and cannot be recognized."
|
||||
elif [[ "$(echo ${MYDEVICE_NAME} | grep "V-Plus Cloud")" == "" ]]; then
|
||||
error_msg "[ ${MYDEVICE_NAME} ] is not [ V-Plus Cloud ] device, please select the correct script."
|
||||
else
|
||||
echo -e "Current device: ${MYDEVICE_NAME} [ vplus ]"
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Check and repair partition mount points
|
||||
[[ -z "$(get_boot_partition_name)" || -z "$(get_root_partition_name)" ]] && {
|
||||
echo -e "Partition mount point error, start repairing..."
|
||||
openwrt-backup -g
|
||||
}
|
||||
|
||||
# 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"
|
||||
LB_PRE="EMMC_"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
EMMC_NAME=$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')
|
||||
PARTITION_NAME=""
|
||||
LB_PRE=""
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
cd /mnt/${EMMC_NAME}${PARTITION_NAME}4/
|
||||
mv -f /tmp/upload/* . 2>/dev/null && sync
|
||||
|
||||
if [[ "${IMG_NAME}" == *.img ]]; then
|
||||
echo -e "Update using [ ${IMG_NAME} ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
echo -e "Update using [ ${IMG_NAME} ] ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img.xz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
xz_file=$(ls *.img.xz | head -n 1)
|
||||
echo -e "Update using [ ${xz_file} ] file. Please wait a moment ..."
|
||||
xz -d "${xz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.img.gz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.img.gz | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
gzip -df "${gz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.7z -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.7z | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
bsdtar -xmf "${gz_file}" 2>/dev/null
|
||||
[ $? -eq 0 ] || 7z x "${gz_file}" -aoa -y 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.zip -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
zip_file=$(ls *.zip | head -n 1)
|
||||
echo -e "Update using [ ${zip_file} ] file. Please wait a moment ..."
|
||||
unzip -o "${zip_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
else
|
||||
echo -e "Please upload or specify the update openwrt firmware file."
|
||||
echo -e "Upload method: system menu → Amlogic Service → Manually Upload Update"
|
||||
echo -e "Specify method: Place the openwrt firmware file in [ /mnt/${EMMC_NAME}${PARTITION_NAME}4/ ]"
|
||||
echo -e "The supported file suffixes are: *.img, *.img.xz, *.img.gz, *.7z, *.zip"
|
||||
echo -e "After upload the openwrt firmware file, run again."
|
||||
exit 1
|
||||
fi
|
||||
sync
|
||||
|
||||
# check file
|
||||
if [ ! -f "${IMG_NAME}" ]; then
|
||||
error_msg "No update file found."
|
||||
else
|
||||
echo "Start update from [ ${IMG_NAME} ]"
|
||||
fi
|
||||
|
||||
# find boot partition
|
||||
BOOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ /^\/boot$/ {print $0}')
|
||||
if [ "${BOOT_PART_MSG}" == "" ]; then
|
||||
error_msg "The boot partition is not exists or not mounted, so it cannot be upgraded with this script!"
|
||||
fi
|
||||
|
||||
BR_FLAG=1
|
||||
echo -ne "Whether to backup and restore the current config files? y/n [y]\b\b"
|
||||
if [[ ${BACKUP_RESTORE_CONFIG} == "restore" ]]; then
|
||||
yn="y"
|
||||
elif [[ ${BACKUP_RESTORE_CONFIG} == "no-restore" ]]; then
|
||||
yn="n"
|
||||
else
|
||||
read yn
|
||||
fi
|
||||
case $yn in
|
||||
n* | N*)
|
||||
BR_FLAG=0
|
||||
;;
|
||||
esac
|
||||
|
||||
BOOT_NAME=$(echo ${BOOT_PART_MSG} | awk '{print $1}')
|
||||
BOOT_PATH=$(echo ${BOOT_PART_MSG} | awk '{print $2}')
|
||||
BOOT_UUID=$(echo ${BOOT_PART_MSG} | awk '{print $4}')
|
||||
|
||||
# find root partition
|
||||
ROOT_PART_MSG="$(get_root_partition_msg)"
|
||||
ROOT_NAME=$(echo ${ROOT_PART_MSG} | awk '{print $1}')
|
||||
ROOT_PATH=$(echo ${ROOT_PART_MSG} | awk '{print $2}')
|
||||
ROOT_UUID=$(echo ${ROOT_PART_MSG} | awk '{print $4}')
|
||||
case ${ROOT_NAME} in
|
||||
${EMMC_NAME}${PARTITION_NAME}2)
|
||||
NEW_ROOT_NAME="${EMMC_NAME}${PARTITION_NAME}3"
|
||||
NEW_ROOT_LABEL="${LB_PRE}ROOTFS2"
|
||||
;;
|
||||
${EMMC_NAME}${PARTITION_NAME}3)
|
||||
NEW_ROOT_NAME="${EMMC_NAME}${PARTITION_NAME}2"
|
||||
NEW_ROOT_LABEL="${LB_PRE}ROOTFS1"
|
||||
;;
|
||||
*)
|
||||
error_msg "The root partition location is invalid, so it cannot be upgraded with this script!"
|
||||
;;
|
||||
esac
|
||||
|
||||
# find new root partition
|
||||
NEW_ROOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | grep "${NEW_ROOT_NAME}" | awk '$3 ~ /^part$/ && $5 !~ /^\/$/ && $5 !~ /^\/boot$/ {print $0}')
|
||||
if [ "${NEW_ROOT_PART_MSG}" == "" ]; then
|
||||
error_msg "The new root partition is not exists, so it cannot be upgraded with this script!"
|
||||
fi
|
||||
NEW_ROOT_NAME=$(echo $NEW_ROOT_PART_MSG | awk '{print $1}')
|
||||
NEW_ROOT_PATH=$(echo $NEW_ROOT_PART_MSG | awk '{print $2}')
|
||||
NEW_ROOT_UUID=$(echo $NEW_ROOT_PART_MSG | awk '{print $4}')
|
||||
NEW_ROOT_MP=$(echo $NEW_ROOT_PART_MSG | awk '{print $5}')
|
||||
|
||||
# losetup
|
||||
losetup -f -P $IMG_NAME
|
||||
if [ $? -eq 0 ]; then
|
||||
LOOP_DEV=$(losetup | grep "$IMG_NAME" | awk '{print $1}')
|
||||
if [ "$LOOP_DEV" == "" ]; then
|
||||
error_msg "loop device not found!"
|
||||
fi
|
||||
else
|
||||
error_msg "losetup $IMG_FILE failed!"
|
||||
fi
|
||||
|
||||
# fix loopdev issue in kernel 5.19
|
||||
function fix_loopdev() {
|
||||
local parentdev=${1##*/}
|
||||
if [ ! -d /sys/block/${parentdev} ]; then
|
||||
return
|
||||
fi
|
||||
subdevs=$(lsblk -l -o NAME | grep -E "^${parentdev}.+\$")
|
||||
for subdev in $subdevs; do
|
||||
if [ ! -d /sys/block/${parentdev}/${subdev} ]; then
|
||||
return
|
||||
elif [ -b /dev/${sub_dev} ]; then
|
||||
continue
|
||||
fi
|
||||
source /sys/block/${parentdev}/${subdev}/uevent
|
||||
mknod /dev/${subdev} b ${MAJOR} ${MINOR}
|
||||
unset MAJOR MINOR DEVNAME DEVTYPE DISKSEQ PARTN PARTNAME
|
||||
done
|
||||
}
|
||||
fix_loopdev ${LOOP_DEV}
|
||||
|
||||
WAIT=3
|
||||
echo -n "The loopdev is $LOOP_DEV, wait ${WAIT} seconds "
|
||||
while [ $WAIT -ge 1 ]; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
WAIT=$((WAIT - 1))
|
||||
done
|
||||
echo
|
||||
|
||||
# umount loop devices (openwrt will auto mount some partition)
|
||||
MOUNTED_DEVS=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "$LOOP_DEV" | awk '$3 !~ /^$/ {print $2}')
|
||||
for dev in $MOUNTED_DEVS; do
|
||||
while :; do
|
||||
echo -n "umount $dev ... "
|
||||
umount -f $dev
|
||||
sleep 1
|
||||
mnt=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "$dev" | awk '$3 !~ /^$/ {print $2}')
|
||||
if [ "$mnt" == "" ]; then
|
||||
echo "ok"
|
||||
break
|
||||
else
|
||||
echo "try again ..."
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# mount src part
|
||||
WORK_DIR=$PWD
|
||||
P1=${WORK_DIR}/boot
|
||||
P2=${WORK_DIR}/root
|
||||
mkdir -p $P1 $P2
|
||||
echo -n "mount ${LOOP_DEV}p1 -> ${P1} ... "
|
||||
mount -t vfat -o ro ${LOOP_DEV}p1 ${P1}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "mount failed"
|
||||
losetup -D
|
||||
exit 1
|
||||
else
|
||||
echo "ok"
|
||||
fi
|
||||
|
||||
echo -n "mount ${LOOP_DEV}p2 -> ${P2} ... "
|
||||
ZSTD_LEVEL=6
|
||||
mount -t btrfs -o ro,compress=zstd:${ZSTD_LEVEL} ${LOOP_DEV}p2 ${P2}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "mount failed"
|
||||
umount -f ${P1}
|
||||
losetup -D
|
||||
exit 1
|
||||
else
|
||||
echo "ok"
|
||||
fi
|
||||
|
||||
# Prepare the dockerman config file
|
||||
if [ -f ${P2}/etc/init.d/dockerman ] && [ -f ${P2}/etc/config/dockerd ]; then
|
||||
|
||||
flg=0
|
||||
# get current docker data root
|
||||
data_root=$(uci get dockerd.globals.data_root 2>/dev/null)
|
||||
if [ "$data_root" == "" ]; then
|
||||
flg=1
|
||||
# get current config from /etc/docker/daemon.json
|
||||
if [ -f "/etc/docker/daemon.json" ] && [ -x "/usr/bin/jq" ]; then
|
||||
data_root=$(jq -r '."data-root"' /etc/docker/daemon.json)
|
||||
|
||||
bip=$(jq -r '."bip"' /etc/docker/daemon.json)
|
||||
[ "$bip" == "null" ] && bip="172.31.0.1/24"
|
||||
|
||||
log_level=$(jq -r '."log-level"' /etc/docker/daemon.json)
|
||||
[ "$log_level" == "null" ] && log_level="warn"
|
||||
|
||||
_iptables=$(jq -r '."iptables"' /etc/docker/daemon.json)
|
||||
[ "$_iptables" == "null" ] && _iptables="true"
|
||||
|
||||
registry_mirrors=$(jq -r '."registry-mirrors"[]' /etc/docker/daemon.json 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$data_root" == "" ]; then
|
||||
data_root="/opt/docker/" # the default data root
|
||||
fi
|
||||
|
||||
if ! uci get dockerd.globals >/dev/null 2>&1; then
|
||||
uci set dockerd.globals='globals'
|
||||
uci commit
|
||||
fi
|
||||
|
||||
# delete alter config , use inner config
|
||||
if uci get dockerd.globals.alt_config_file >/dev/null 2>&1; then
|
||||
uci delete dockerd.globals.alt_config_file
|
||||
uci commit
|
||||
fi
|
||||
|
||||
if [ $flg -eq 1 ]; then
|
||||
uci set dockerd.globals.data_root=$data_root
|
||||
[ "$bip" != "" ] && uci set dockerd.globals.bip=$bip
|
||||
[ "$log_level" != "" ] && uci set dockerd.globals.log_level=$log_level
|
||||
[ "$_iptables" != "" ] && uci set dockerd.globals.iptables=$_iptables
|
||||
if [ "$registry_mirrors" != "" ]; then
|
||||
for reg in $registry_mirrors; do
|
||||
uci add_list dockerd.globals.registry_mirrors=$reg
|
||||
done
|
||||
fi
|
||||
uci set dockerd.globals.auto_start='1'
|
||||
uci commit
|
||||
fi
|
||||
fi
|
||||
|
||||
#format NEW_ROOT
|
||||
echo "umount ${NEW_ROOT_MP}"
|
||||
umount -f "${NEW_ROOT_MP}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "umount failed, please reboot and try again!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "format ${NEW_ROOT_PATH}"
|
||||
NEW_ROOT_UUID=$(uuidgen)
|
||||
mkfs.btrfs -f -U ${NEW_ROOT_UUID} -L ${NEW_ROOT_LABEL} ${NEW_ROOT_PATH}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "format ${NEW_ROOT_PATH} failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "mount ${NEW_ROOT_PATH} to ${NEW_ROOT_MP}"
|
||||
mount -t btrfs -o compress=zstd:${ZSTD_LEVEL} ${NEW_ROOT_PATH} ${NEW_ROOT_MP}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "mount ${NEW_ROOT_PATH} to ${NEW_ROOT_MP} failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# begin copy rootfs
|
||||
cd ${NEW_ROOT_MP}
|
||||
echo "Start copy data from ${P2} to ${NEW_ROOT_MP} ..."
|
||||
ENTRYS=$(ls)
|
||||
for entry in $ENTRYS; do
|
||||
if [ "$entry" == "lost+found" ]; then
|
||||
continue
|
||||
fi
|
||||
echo -n "remove old $entry ... "
|
||||
rm -rf $entry
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ok"
|
||||
else
|
||||
error_msg "failed"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
|
||||
echo "create etc subvolume ..."
|
||||
btrfs subvolume create etc
|
||||
echo -n "make dirs ... "
|
||||
mkdir -p .snapshots .reserved bin boot dev lib opt mnt overlay proc rom root run sbin sys tmp usr www
|
||||
ln -sf lib/ lib64
|
||||
ln -sf tmp/ var
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
COPY_SRC="root etc bin sbin lib opt usr www"
|
||||
echo "copy data ... "
|
||||
for src in $COPY_SRC; do
|
||||
echo -n "copy $src ... "
|
||||
(cd ${P2} && tar cf - $src) | tar xf -
|
||||
sync
|
||||
echo "done"
|
||||
done
|
||||
|
||||
SHFS="/mnt/${EMMC_NAME}${PARTITION_NAME}4"
|
||||
echo "Modify config files ... "
|
||||
rm -f "./etc/rc.local.orig" "./etc/first_run.sh" "./etc/part_size"
|
||||
rm -f ./etc/bench.log
|
||||
if [ -x ./usr/sbin/balethirq.pl ]; then
|
||||
if grep "balethirq.pl" "./etc/rc.local"; then
|
||||
echo "balance irq is enabled"
|
||||
else
|
||||
echo "enable balance irq"
|
||||
sed -e "/exit/i\/usr/sbin/balethirq.pl" -i ./etc/rc.local
|
||||
fi
|
||||
fi
|
||||
cat >./etc/fstab <<EOF
|
||||
UUID=${NEW_ROOT_UUID} / btrfs compress=zstd:${ZSTD_LEVEL} 0 1
|
||||
LABEL=${LB_PRE}BOOT /boot vfat defaults 0 2
|
||||
#tmpfs /tmp tmpfs defaults,nosuid 0 0
|
||||
EOF
|
||||
|
||||
cat >./etc/config/fstab <<EOF
|
||||
config global
|
||||
option anon_swap '0'
|
||||
option anon_mount '1'
|
||||
option auto_swap '0'
|
||||
option auto_mount '1'
|
||||
option delay_root '5'
|
||||
option check_fs '0'
|
||||
|
||||
config mount
|
||||
option target '/rom'
|
||||
option uuid '${NEW_ROOT_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'btrfs'
|
||||
option options 'compress=zstd:${ZSTD_LEVEL}'
|
||||
|
||||
config mount
|
||||
option target '/boot'
|
||||
option label '${LB_PRE}BOOT'
|
||||
option enabled '1'
|
||||
option enabled_fsck '0'
|
||||
option fstype 'vfat'
|
||||
|
||||
EOF
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
sync
|
||||
|
||||
echo "create the first etc snapshot -> .snapshots/etc-000"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-000
|
||||
|
||||
[ -d ${SHFS}/docker ] || mkdir -p ${SHFS}/docker
|
||||
rm -rf opt/docker && ln -sf ${SHFS}/docker/ opt/docker
|
||||
|
||||
if [ -f /mnt/${NEW_ROOT_NAME}/etc/config/AdGuardHome ]; then
|
||||
[ -d ${SHFS}/AdGuardHome/data ] || mkdir -p ${SHFS}/AdGuardHome/data
|
||||
if [ ! -L /usr/bin/AdGuardHome ]; then
|
||||
[ -d /usr/bin/AdGuardHome ] &&
|
||||
cp -a /usr/bin/AdGuardHome/* ${SHFS}/AdGuardHome/
|
||||
fi
|
||||
ln -sf ${SHFS}/AdGuardHome /mnt/${NEW_ROOT_NAME}/usr/bin/AdGuardHome
|
||||
fi
|
||||
|
||||
BOOTLOADER="./lib/u-boot/u-boot-sunxi-with-spl.bin"
|
||||
if [ -f ${BOOTLOADER} ]; then
|
||||
echo "update u-boot ... "
|
||||
# erase from 8kb to 4mb
|
||||
dd if=/dev/zero of=/dev/${EMMC_NAME} bs=1024 seek=8 count=4088 conv=fsync
|
||||
# write u-boot
|
||||
dd if=${BOOTLOADER} of=/dev/${EMMC_NAME} bs=1024 seek=8 conv=fsync
|
||||
echo "done"
|
||||
fi
|
||||
sync
|
||||
echo "copy done"
|
||||
echo
|
||||
|
||||
BACKUP_LIST=$(${P2}/usr/sbin/openwrt-backup -p)
|
||||
if [ $BR_FLAG -eq 1 ]; then
|
||||
echo -n "Restore your old config files ... "
|
||||
(
|
||||
cd /
|
||||
eval tar czf ${NEW_ROOT_MP}/.reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
)
|
||||
tar xzf ${NEW_ROOT_MP}/.reserved/openwrt_config.tar.gz
|
||||
[ -f ./etc/config/dockerman ] && sed -e "s/option wan_mode 'false'/option wan_mode 'true'/" -i ./etc/config/dockerman 2>/dev/null
|
||||
[ -f ./etc/config/dockerd ] && sed -e "s/option wan_mode '0'/option wan_mode '1'/" -i ./etc/config/dockerd 2>/dev/null
|
||||
[ -f ./etc/config/verysync ] && sed -e 's/config setting/config verysync/' -i ./etc/config/verysync
|
||||
|
||||
# 还原 fstab
|
||||
cp -f .snapshots/etc-000/fstab ./etc/fstab
|
||||
cp -f .snapshots/etc-000/config/fstab ./etc/config/fstab
|
||||
# 还原 luci
|
||||
cp -f .snapshots/etc-000/config/luci ./etc/config/luci
|
||||
# 还原/etc/config/rpcd
|
||||
cp -f .snapshots/etc-000/config/rpcd ./etc/config/rpcd
|
||||
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
fi
|
||||
|
||||
rm -f ./etc/bench.log
|
||||
cat >>./etc/crontabs/root <<EOF
|
||||
17 3 * * * /etc/coremark.sh
|
||||
EOF
|
||||
|
||||
sed -e 's/ttyAMA0/ttyS0/' -i ./etc/inittab
|
||||
sss=$(date +%s)
|
||||
ddd=$((sss / 86400))
|
||||
sed -e "s/:0:0:99999:7:::/:${ddd}:0:99999:7:::/" -i ./etc/shadow
|
||||
# 修复amule每次升级后重复添加条目的问题
|
||||
sed -e "/amule:x:/d" -i ./etc/shadow
|
||||
# 修复dropbear每次升级后重复添加sshd条目的问题
|
||||
sed -e "/sshd:x:/d" -i ./etc/shadow
|
||||
if ! grep "sshd:x:22:sshd" ./etc/group >/dev/null; then
|
||||
echo "sshd:x:22:sshd" >>./etc/group
|
||||
fi
|
||||
if ! grep "sshd:x:22:22:sshd:" ./etc/passwd >/dev/null; then
|
||||
echo "sshd:x:22:22:sshd:/var/run/sshd:/bin/false" >>./etc/passwd
|
||||
fi
|
||||
if ! grep "sshd:x:" ./etc/shadow >/dev/null; then
|
||||
echo "sshd:x:${ddd}:0:99999:7:::" >>./etc/shadow
|
||||
fi
|
||||
|
||||
if [ $BR_FLAG -eq 1 ]; then
|
||||
if [ -x ./bin/bash ] && [ -f ./etc/profile.d/30-sysinfo.sh ]; then
|
||||
sed -e 's/\/bin\/ash/\/bin\/bash/' -i ./etc/passwd
|
||||
fi
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
fi
|
||||
sed -e "s/option hw_flow '1'/option hw_flow '0'/" -i ./etc/config/turboacc
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
sync
|
||||
|
||||
eval tar czf .reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
|
||||
rm -f ./etc/part_size ./etc/first_run.sh
|
||||
mv ./etc/rc.local ./etc/rc.local.orig
|
||||
cat >"./etc/rc.local" <<EOF
|
||||
if ! ls /etc/rc.d/S??dockerd >/dev/null 2>&1;then
|
||||
/etc/init.d/dockerd enable
|
||||
/etc/init.d/dockerd start
|
||||
fi
|
||||
if ! ls /etc/rc.d/S??dockerman >/dev/null 2>&1 && [ -f /etc/init.d/dockerman ];then
|
||||
/etc/init.d/dockerman enable
|
||||
/etc/init.d/dockerman start
|
||||
fi
|
||||
opkg remove --force-removal-of-dependent-packages shairport-sync-openssl
|
||||
mv /etc/rc.local.orig /etc/rc.local
|
||||
chmod 755 /etc/rc.local
|
||||
exec /etc/rc.local
|
||||
exit
|
||||
EOF
|
||||
chmod 755 ./etc/rc.local*
|
||||
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
|
||||
echo "create the second etc snapshot -> .snapshots/etc-001"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-001
|
||||
|
||||
# 2021.04.01添加
|
||||
# 强制锁定fstab,防止用户擅自修改挂载点
|
||||
# 开启了快照功能之后,不再需要锁定fstab
|
||||
#chattr +ia ./etc/config/fstab
|
||||
|
||||
cd ${WORK_DIR}
|
||||
|
||||
echo "Start copy data from ${P1} to /boot ..."
|
||||
cd /boot
|
||||
echo -n "remove old boot files ..."
|
||||
rm -rf *
|
||||
echo "done"
|
||||
echo -n "copy new boot files ... "
|
||||
(cd ${P1} && tar cf - .) | tar xf -
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
echo -n "Update boot args ... "
|
||||
cat >uEnv.txt <<EOF
|
||||
LINUX=/zImage
|
||||
INITRD=/uInitrd
|
||||
|
||||
# Example:
|
||||
# FDT=/dtb/allwinner/sun50i-h6-vplus-cloud.dtb
|
||||
# FDT=/dtb/allwinner/sun50i-h6-vplus-cloud-2ghz.dtb
|
||||
FDT=/dtb/allwinner/${MYDTB_FDTFILE}
|
||||
|
||||
APPEND=root=UUID=${NEW_ROOT_UUID} rootfstype=btrfs rootflags=compress=zstd:${ZSTD_LEVEL} console=ttyS0,115200n8 no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
|
||||
EOF
|
||||
|
||||
# Compatible with armbianEnv.txt file
|
||||
[[ -f "armbianEnv.txt" ]] && {
|
||||
echo "Update armbianEnv.txt settings."
|
||||
sed -i "s|^rootdev=.*|rootdev=UUID=${NEW_ROOT_UUID}|g" armbianEnv.txt
|
||||
rm -f uEnv.txt
|
||||
}
|
||||
|
||||
# Replace the UUID for extlinux/extlinux.conf if it exists
|
||||
[[ -f "extlinux/extlinux.conf" ]] && {
|
||||
sed -i -E "s|UUID=[^ ]*|UUID=${NEW_ROOT_UUID}|" extlinux/extlinux.conf 2>/dev/null
|
||||
}
|
||||
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
cd $WORK_DIR
|
||||
umount -f ${P1} ${P2} 2>/dev/null
|
||||
losetup -D 2>/dev/null
|
||||
rmdir ${P1} ${P2} 2>/dev/null
|
||||
rm -f ${IMG_NAME} 2>/dev/null
|
||||
rm -f sha256sums 2>/dev/null
|
||||
sync
|
||||
|
||||
echo "Successfully updated, automatic restarting..."
|
||||
sleep 3
|
||||
reboot
|
||||
exit 0
|
||||
@@ -0,0 +1,859 @@
|
||||
#!/bin/bash
|
||||
#======================================================================================
|
||||
# Function: Update openwrt to emmc for Amlogic S9xxx STB
|
||||
# Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic
|
||||
#======================================================================================
|
||||
#
|
||||
# The script supports directly setting parameters for update, skipping interactive selection
|
||||
# openwrt-update-amlogic ${OPENWRT_FILE} ${AUTO_MAINLINE_UBOOT} ${RESTORE_CONFIG}
|
||||
# E.g: openwrt-update-amlogic openwrt_s905d.img.gz yes restore
|
||||
# E.g: openwrt-update-amlogic openwrt_s905d.img.gz no no-restore
|
||||
|
||||
# You can also execute the script directly, and interactively select related functions
|
||||
# E.g: openwrt-update-amlogic
|
||||
#
|
||||
#======================================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "[ERROR] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the /boot file system
|
||||
get_boot_partition_name() {
|
||||
local paths="/boot"
|
||||
local partition_name
|
||||
|
||||
partition_name=$(df "${paths}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Get the partition message of the root file system
|
||||
get_root_partition_msg() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_msg=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ "^" "'"${path}"'" "$" {print $0}')
|
||||
[[ -n "${partition_msg}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_msg}" ]] && error_msg "Cannot find the root partition message!"
|
||||
echo "${partition_msg}"
|
||||
}
|
||||
|
||||
# Receive one-key command related parameters
|
||||
IMG_NAME="${1}"
|
||||
AUTO_MAINLINE_UBOOT="${2}"
|
||||
BACKUP_RESTORE_CONFIG="${3}"
|
||||
|
||||
# Current device model
|
||||
MYDEVICE_NAME=$(cat /proc/device-tree/model | tr -d '\000')
|
||||
if [[ -z "${MYDEVICE_NAME}" ]]; then
|
||||
error_msg "The device name is empty and cannot be recognized."
|
||||
elif [[ ! -f "/etc/flippy-openwrt-release" ]]; then
|
||||
error_msg "The [ /etc/flippy-openwrt-release ] file is missing."
|
||||
else
|
||||
echo -e "Current device: ${MYDEVICE_NAME} [ amlogic ]"
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Check and repair partition mount points
|
||||
[[ -z "$(get_boot_partition_name)" || -z "$(get_root_partition_name)" ]] && {
|
||||
echo -e "Partition mount point error, start repairing..."
|
||||
openwrt-backup -g
|
||||
}
|
||||
|
||||
# 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)}')
|
||||
if lsblk -l -o NAME | grep "${EMMC_NAME}boot0" >/dev/null; then
|
||||
ROOT_DISK_TYPE="EMMC"
|
||||
else
|
||||
ROOT_DISK_TYPE="SD"
|
||||
fi
|
||||
PARTITION_NAME="p"
|
||||
LB_PRE="${ROOT_DISK_TYPE}_"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
EMMC_NAME=$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')
|
||||
ROOT_DISK_TYPE="USB"
|
||||
PARTITION_NAME=""
|
||||
LB_PRE="${ROOT_DISK_TYPE}_"
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
DOCKER_ROOT="/mnt/${EMMC_NAME}${PARTITION_NAME}4/docker/"
|
||||
cd /mnt/${EMMC_NAME}${PARTITION_NAME}4/
|
||||
mv -f /tmp/upload/* . 2>/dev/null && sync
|
||||
|
||||
if [[ "${IMG_NAME}" == *.img ]]; then
|
||||
echo -e "Update using [ ${IMG_NAME} ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
echo -e "Update using [ ${IMG_NAME} ] ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img.xz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
xz_file=$(ls *.img.xz | head -n 1)
|
||||
echo -e "Update using [ ${xz_file} ] file. Please wait a moment ..."
|
||||
xz -d "${xz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.img.gz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.img.gz | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
gzip -df "${gz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.7z -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.7z | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
bsdtar -xmf "${gz_file}" 2>/dev/null
|
||||
[ $? -eq 0 ] || 7z x "${gz_file}" -aoa -y 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.zip -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
zip_file=$(ls *.zip | head -n 1)
|
||||
echo -e "Update using [ ${zip_file} ] file. Please wait a moment ..."
|
||||
unzip -o "${zip_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
else
|
||||
echo -e "Please upload or specify the update openwrt firmware file."
|
||||
echo -e "Upload method: system menu → Amlogic Service → Manually Upload Update"
|
||||
echo -e "Specify method: Place the openwrt firmware file in [ /mnt/${EMMC_NAME}${PARTITION_NAME}4/ ]"
|
||||
echo -e "The supported file suffixes are: *.img, *.img.xz, *.img.gz, *.7z, *.zip"
|
||||
echo -e "After upload the openwrt firmware file, run again."
|
||||
exit 1
|
||||
fi
|
||||
sync
|
||||
|
||||
# check file
|
||||
if [ ! -f "${IMG_NAME}" ]; then
|
||||
error_msg "No update file found."
|
||||
else
|
||||
echo "Start update from [ ${IMG_NAME} ]"
|
||||
fi
|
||||
|
||||
# Check the necessary dependencies
|
||||
DEPENDS="lsblk uuidgen grep awk btrfs mkfs.fat mkfs.btrfs md5sum fatlabel"
|
||||
echo "Check the necessary dependencies..."
|
||||
for dep in ${DEPENDS}; do
|
||||
WITCH=$(busybox which ${dep})
|
||||
if [ "${WITCH}" == "" ]; then
|
||||
error_msg "Dependent command: ${dep} does not exist, upgrade cannot be performed, only flash through U disk/TF card!"
|
||||
else
|
||||
echo "${dep} path: ${WITCH}"
|
||||
fi
|
||||
done
|
||||
echo "Check passed"
|
||||
|
||||
# find boot partition
|
||||
BOOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ /^\/boot$/ {print $0}')
|
||||
if [ "${BOOT_PART_MSG}" == "" ]; then
|
||||
error_msg "Boot The partition does not exist, so the update cannot be continued!"
|
||||
fi
|
||||
BOOT_NAME=$(echo ${BOOT_PART_MSG} | awk '{print $1}')
|
||||
BOOT_PATH=$(echo ${BOOT_PART_MSG} | awk '{print $2}')
|
||||
BOOT_UUID=$(echo ${BOOT_PART_MSG} | awk '{print $4}')
|
||||
|
||||
BR_FLAG=1
|
||||
echo -ne "Whether to backup and restore the current config files? y/n [y]\b\b"
|
||||
if [[ ${BACKUP_RESTORE_CONFIG} == "restore" ]]; then
|
||||
yn="y"
|
||||
elif [[ ${BACKUP_RESTORE_CONFIG} == "no-restore" ]]; then
|
||||
yn="n"
|
||||
else
|
||||
read yn
|
||||
fi
|
||||
case $yn in
|
||||
n* | N*)
|
||||
BR_FLAG=0
|
||||
;;
|
||||
esac
|
||||
|
||||
# find root partition
|
||||
ROOT_PART_MSG="$(get_root_partition_msg)"
|
||||
ROOT_NAME=$(echo ${ROOT_PART_MSG} | awk '{print $1}')
|
||||
ROOT_PATH=$(echo ${ROOT_PART_MSG} | awk '{print $2}')
|
||||
ROOT_UUID=$(echo ${ROOT_PART_MSG} | awk '{print $4}')
|
||||
|
||||
case ${ROOT_NAME} in
|
||||
${EMMC_NAME}${PARTITION_NAME}2)
|
||||
NEW_ROOT_NAME="${EMMC_NAME}${PARTITION_NAME}3"
|
||||
NEW_ROOT_LABEL="${LB_PRE}ROOTFS2"
|
||||
;;
|
||||
${EMMC_NAME}${PARTITION_NAME}3)
|
||||
NEW_ROOT_NAME="${EMMC_NAME}${PARTITION_NAME}2"
|
||||
NEW_ROOT_LABEL="${LB_PRE}ROOTFS1"
|
||||
;;
|
||||
*)
|
||||
error_msg "ROOTFS The partition location is incorrect, so the update cannot continue!"
|
||||
;;
|
||||
esac
|
||||
echo "NEW_ROOT_NAME: [ ${NEW_ROOT_NAME} ]"
|
||||
|
||||
# find new root partition
|
||||
NEW_ROOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | grep "${NEW_ROOT_NAME}" | awk '$3 ~ /^part$/ && $5 !~ /^\/$/ && $5 !~ /^\/boot$/ {print $0}')
|
||||
if [ "${NEW_ROOT_PART_MSG}" == "" ]; then
|
||||
error_msg "The new ROOTFS partition does not exist, so the update cannot continue!"
|
||||
fi
|
||||
NEW_ROOT_NAME=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $1}')
|
||||
NEW_ROOT_PATH=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $2}')
|
||||
NEW_ROOT_UUID=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $4}')
|
||||
NEW_ROOT_MP=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $5}')
|
||||
echo "NEW_ROOT_MP: [ ${NEW_ROOT_MP} ]"
|
||||
|
||||
# backup old bootloader
|
||||
if [ ! -f /root/BackupOldBootloader.img ]; then
|
||||
echo "Backup bootloader -> [ BackupOldBootloader.img ] ... "
|
||||
dd if=/dev/${EMMC_NAME} of=/root/BackupOldBootloader.img bs=1M count=4 conv=fsync
|
||||
echo "Backup bootloader complete."
|
||||
echo
|
||||
fi
|
||||
|
||||
# losetup
|
||||
losetup -f -P ${IMG_NAME}
|
||||
if [ ${?} -eq 0 ]; then
|
||||
LOOP_DEV=$(losetup | grep "${IMG_NAME}" | awk '{print $1}')
|
||||
if [ "${LOOP_DEV}" == "" ]; then
|
||||
error_msg "loop device not found!"
|
||||
fi
|
||||
else
|
||||
error_msg "losetup [ ${IMG_FILE} ] failed!"
|
||||
fi
|
||||
|
||||
# fix loopdev issue in kernel 5.19
|
||||
function fix_loopdev() {
|
||||
local parentdev=${1##*/}
|
||||
if [ ! -d /sys/block/${parentdev} ]; then
|
||||
return
|
||||
fi
|
||||
subdevs=$(lsblk -l -o NAME | grep -E "^${parentdev}.+\$")
|
||||
for subdev in ${subdevs}; do
|
||||
if [ ! -d /sys/block/${parentdev}/${subdev} ]; then
|
||||
return
|
||||
elif [ -b /dev/${sub_dev} ]; then
|
||||
continue
|
||||
fi
|
||||
source /sys/block/${parentdev}/${subdev}/uevent
|
||||
mknod /dev/${subdev} b ${MAJOR} ${MINOR}
|
||||
unset MAJOR MINOR DEVNAME DEVTYPE DISKSEQ PARTN PARTNAME
|
||||
done
|
||||
}
|
||||
fix_loopdev ${LOOP_DEV}
|
||||
|
||||
WAIT=3
|
||||
echo "The loopdev is [ $LOOP_DEV ], wait [ ${WAIT} ] seconds. "
|
||||
while [[ "${WAIT}" -ge "1" ]]; do
|
||||
sleep 1
|
||||
WAIT=$((WAIT - 1))
|
||||
done
|
||||
|
||||
# umount loop devices (openwrt will auto mount some partition)
|
||||
MOUNTED_DEVS=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "${LOOP_DEV}" | awk '$3 !~ /^$/ {print $2}')
|
||||
for dev in ${MOUNTED_DEVS}; do
|
||||
while :; do
|
||||
echo "umount [ ${dev} ] ... "
|
||||
umount -f ${dev}
|
||||
sleep 1
|
||||
mnt=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "${dev}" | awk '$3 !~ /^$/ {print $2}')
|
||||
if [ "${mnt}" == "" ]; then
|
||||
break
|
||||
else
|
||||
echo "Retry ..."
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# mount src part
|
||||
WORK_DIR=${PWD}
|
||||
P1=${WORK_DIR}/boot
|
||||
P2=${WORK_DIR}/root
|
||||
mkdir -p $P1 $P2
|
||||
|
||||
echo "Mount [ ${LOOP_DEV}p1 ] -> [ ${P1} ] ... "
|
||||
mount -t vfat -o ro ${LOOP_DEV}p1 ${P1}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mount p1 [ ${LOOP_DEV}p1 ] failed!"
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Mount [ ${LOOP_DEV}p2 ] -> [ ${P2} ] ... "
|
||||
ZSTD_LEVEL=6
|
||||
mount -t btrfs -o ro,compress=zstd:${ZSTD_LEVEL} ${LOOP_DEV}p2 ${P2}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mount p2 [ ${LOOP_DEV}p2 ] failed!"
|
||||
umount -f ${P1}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prepare the dockerman config file
|
||||
if [ -f ${P2}/etc/init.d/dockerman ] && [ -f ${P2}/etc/config/dockerd ]; then
|
||||
|
||||
flg=0
|
||||
# get current docker data root
|
||||
data_root=$(uci get dockerd.globals.data_root 2>/dev/null)
|
||||
if [ "${data_root}" == "" ]; then
|
||||
flg=1
|
||||
# get current config from /etc/docker/daemon.json
|
||||
if [ -f "/etc/docker/daemon.json" ] && [ -x "/usr/bin/jq" ]; then
|
||||
data_root=$(jq -r '."data-root"' /etc/docker/daemon.json)
|
||||
|
||||
bip=$(jq -r '."bip"' /etc/docker/daemon.json)
|
||||
[ "${bip}" == "null" ] && bip="172.31.0.1/24"
|
||||
|
||||
log_level=$(jq -r '."log-level"' /etc/docker/daemon.json)
|
||||
[ "${log_level}" == "null" ] && log_level="warn"
|
||||
|
||||
_iptables=$(jq -r '."iptables"' /etc/docker/daemon.json)
|
||||
[ "${_iptables}" == "null" ] && _iptables="true"
|
||||
|
||||
registry_mirrors=$(jq -r '."registry-mirrors"[]' /etc/docker/daemon.json 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${data_root}" == "" ]; then
|
||||
data_root="/opt/docker/" # the default data root
|
||||
fi
|
||||
|
||||
if ! uci get dockerd.globals >/dev/null 2>&1; then
|
||||
uci set dockerd.globals='globals'
|
||||
uci commit
|
||||
fi
|
||||
|
||||
# delete alter config , use inner config
|
||||
if uci get dockerd.globals.alt_config_file >/dev/null 2>&1; then
|
||||
uci delete dockerd.globals.alt_config_file
|
||||
uci commit
|
||||
fi
|
||||
|
||||
if [ ${flg} -eq 1 ]; then
|
||||
uci set dockerd.globals.data_root=${data_root}
|
||||
[ "${bip}" != "" ] && uci set dockerd.globals.bip=${bip}
|
||||
[ "${log_level}" != "" ] && uci set dockerd.globals.log_level=${log_level}
|
||||
[ "${_iptables}" != "" ] && uci set dockerd.globals.iptables=${_iptables}
|
||||
if [ "${registry_mirrors}" != "" ]; then
|
||||
for reg in ${registry_mirrors}; do
|
||||
uci add_list dockerd.globals.registry_mirrors=${reg}
|
||||
done
|
||||
fi
|
||||
uci set dockerd.globals.auto_start='1'
|
||||
uci commit
|
||||
fi
|
||||
fi
|
||||
|
||||
#update version prompt
|
||||
source /boot/uEnv.txt 2>/dev/null
|
||||
CUR_FDTFILE=${FDT}
|
||||
echo -e "FDT Value [ ${CUR_FDTFILE} ]"
|
||||
cp /boot/uEnv.txt /tmp/uEnv.txt && sync
|
||||
|
||||
K510="1"
|
||||
[[ "$(hexdump -n 15 -x "${P1}/zImage" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0"
|
||||
echo -e "K510 [ ${K510} ]"
|
||||
|
||||
# flippy-openwrt-release info
|
||||
UBOOT_OVERLOAD=""
|
||||
MAINLINE_UBOOT=""
|
||||
ANDROID_UBOOT=""
|
||||
env_openwrt_file=""
|
||||
|
||||
if [ -f "${P2}/etc/flippy-openwrt-release" ]; then
|
||||
env_openwrt_file="${P2}/etc/flippy-openwrt-release"
|
||||
elif [ -f "/etc/flippy-openwrt-release" ]; then
|
||||
env_openwrt_file="/etc/flippy-openwrt-release"
|
||||
else
|
||||
env_openwrt_file=""
|
||||
fi
|
||||
|
||||
if [ -n "${env_openwrt_file}" ]; then
|
||||
source "${env_openwrt_file}" 2>/dev/null
|
||||
# Update the parameters used
|
||||
UBOOT_OVERLOAD=${UBOOT_OVERLOAD}
|
||||
MAINLINE_UBOOT=${MAINLINE_UBOOT}
|
||||
ANDROID_UBOOT=${ANDROID_UBOOT}
|
||||
|
||||
# Unused parameters
|
||||
FDTFILE=${FDTFILE}
|
||||
U_BOOT_EXT=${U_BOOT_EXT}
|
||||
KERNEL_VERSION=${KERNEL_VERSION}
|
||||
SOC=${SOC}
|
||||
fi
|
||||
|
||||
#format NEW_ROOT
|
||||
echo "umount [ ${NEW_ROOT_MP} ]"
|
||||
umount -f "${NEW_ROOT_MP}"
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
echo "Umount [ ${NEW_ROOT_MP} ] failed, Please restart and try again!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# check and fix partition
|
||||
function check_and_fix_partition() {
|
||||
local target_dev_name=${1} # mmcblk2
|
||||
local target_pt_name=${2} # p2
|
||||
local target_pt_idx=${3} # 2
|
||||
local safe_pt_begin_mb=${4} # 800
|
||||
local safe_pt_begin_byte=$((${safe_pt_begin_mb} * 1024 * 1024))
|
||||
|
||||
local cur_pt_begin_sector=$(fdisk -l /dev/${target_dev_name} | grep ${target_dev_name}${target_pt_name} | awk '{printf $2}')
|
||||
local cur_pt_begin_mb=$((${cur_pt_begin_sector} * 512 / 1024 / 1024))
|
||||
|
||||
if [ ${cur_pt_begin_mb} -ge ${safe_pt_begin_mb} ]; then
|
||||
# check pass
|
||||
return
|
||||
fi
|
||||
|
||||
local cur_pt_end_sector=$(fdisk -l /dev/${target_dev_name} | grep ${target_dev_name}${target_pt_name} | awk '{printf $3}')
|
||||
local cur_pt_end_byte=$(((${cur_pt_end_sector} + 1) * 512 - 1))
|
||||
|
||||
echo "Unsafe partition found, repairing ... "
|
||||
parted /dev/${target_dev_name} rm ${target_pt_idx} ||
|
||||
(
|
||||
error_msg "rm partion ${target_pt_idx} failed"
|
||||
)
|
||||
parted /dev/${target_dev_name} mkpart primary btrfs "${safe_pt_begin_byte}b" "${cur_pt_end_byte}b" ||
|
||||
(
|
||||
error_msg "create new partion ${target_pt_idx} failed"
|
||||
)
|
||||
echo "Partition repaired"
|
||||
}
|
||||
|
||||
# check if need fix partition
|
||||
if [ "${NEW_ROOT_NAME}" == "mmcblk2p2" ]; then
|
||||
if [ "${MYDEVICE_NAME}" == "Phicomm N1" ] || [ "${MYDEVICE_NAME}" == "Octopus Planet" ]; then
|
||||
# 最新研究结果:
|
||||
# Phicomm N1当采用官方 "天天链" 固件底包时,
|
||||
# 796MB开始的 12 字节在每次重启后会被 bootloader 覆写,
|
||||
# 因此把安全位置设定在800MB之后
|
||||
# The latest research results:
|
||||
# When Phicomm N1 uses the official "tian tian lian" firmware bottom package,
|
||||
# the 12 bytes starting from 796MB will be overwritten by bootloader after each reboot,
|
||||
# so the safe location is set after 800MB
|
||||
SAFE_PT_BEGIN_MB=800
|
||||
check_and_fix_partition "${EMMC_NAME}" "p2" 2 ${SAFE_PT_BEGIN_MB}
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Format [ ${NEW_ROOT_PATH} ]"
|
||||
NEW_ROOT_UUID=$(uuidgen)
|
||||
mkfs.btrfs -f -U ${NEW_ROOT_UUID} -L ${NEW_ROOT_LABEL} -m single ${NEW_ROOT_PATH}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Format [ ${NEW_ROOT_PATH} ] failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Mount [ ${NEW_ROOT_PATH} ] -> [ ${NEW_ROOT_MP} ]"
|
||||
mount -t btrfs -o compress=zstd:${ZSTD_LEVEL} ${NEW_ROOT_PATH} ${NEW_ROOT_MP}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mount [ ${NEW_ROOT_PATH} ] -> [ ${NEW_ROOT_MP} ] failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# begin copy rootfs
|
||||
cd ${NEW_ROOT_MP}
|
||||
echo "Start copying data, From [ ${P2} ] TO [ ${NEW_ROOT_MP} ] ..."
|
||||
ENTRYS=$(ls)
|
||||
for entry in ${ENTRYS}; do
|
||||
if [[ "${entry}" == "lost+found" ]]; then
|
||||
continue
|
||||
fi
|
||||
echo "Remove old [ ${entry} ] ... "
|
||||
rm -rf ${entry}
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
error_msg "failed."
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Create folder ... "
|
||||
btrfs subvolume create etc
|
||||
mkdir -p .snapshots .reserved bin boot dev lib opt mnt overlay proc rom root run sbin sys tmp usr www
|
||||
ln -sf lib/ lib64
|
||||
ln -sf tmp/ var
|
||||
sync
|
||||
|
||||
COPY_SRC="root etc bin sbin lib opt usr www"
|
||||
echo "Copy data begin ... "
|
||||
for src in ${COPY_SRC}; do
|
||||
echo "Copy [ ${src} ] ... "
|
||||
(cd ${P2} && tar cf - ${src}) | tar xf -
|
||||
sync
|
||||
done
|
||||
|
||||
# if not backup, then force rewrite the etc/docker/daemon.json
|
||||
if [ "${BR_FLAG}" -eq 0 ]; then
|
||||
cat >./etc/docker/daemon.json <<EOF
|
||||
{
|
||||
"bip": "172.31.0.1/24",
|
||||
"data-root": "${DOCKER_ROOT}",
|
||||
"log-level": "warn",
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "5"
|
||||
},
|
||||
"registry-mirrors": [
|
||||
"https://mirror.baidubce.com/",
|
||||
"https://hub-mirror.c.163.com"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >./etc/fstab <<EOF
|
||||
UUID=${NEW_ROOT_UUID} / btrfs compress=zstd:${ZSTD_LEVEL} 0 1
|
||||
LABEL=${LB_PRE}BOOT /boot vfat defaults 0 2
|
||||
#tmpfs /tmp tmpfs defaults,nosuid 0 0
|
||||
EOF
|
||||
|
||||
cat >./etc/config/fstab <<EOF
|
||||
config global
|
||||
option anon_swap '0'
|
||||
option anon_mount '1'
|
||||
option auto_swap '0'
|
||||
option auto_mount '1'
|
||||
option delay_root '5'
|
||||
option check_fs '0'
|
||||
|
||||
config mount
|
||||
option target '/rom'
|
||||
option uuid '${NEW_ROOT_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'btrfs'
|
||||
option options 'compress=zstd:${ZSTD_LEVEL}'
|
||||
|
||||
config mount
|
||||
option target '/boot'
|
||||
option label '${LB_PRE}BOOT'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'vfat'
|
||||
|
||||
EOF
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
sync
|
||||
|
||||
echo "Create initial etc snapshot -> .snapshots/etc-000"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-000
|
||||
sync
|
||||
|
||||
[ -d /mnt/${EMMC_NAME}${PARTITION_NAME}4/docker ] || mkdir -p /mnt/${EMMC_NAME}${PARTITION_NAME}4/docker
|
||||
rm -rf opt/docker && ln -sf /mnt/${EMMC_NAME}${PARTITION_NAME}4/docker/ opt/docker
|
||||
|
||||
if [ -f /mnt/${NEW_ROOT_NAME}/etc/config/AdGuardHome ]; then
|
||||
[ -d /mnt/${EMMC_NAME}${PARTITION_NAME}4/AdGuardHome/data ] || mkdir -p /mnt/${EMMC_NAME}${PARTITION_NAME}4/AdGuardHome/data
|
||||
if [ ! -L /usr/bin/AdGuardHome ]; then
|
||||
[ -d /usr/bin/AdGuardHome ] &&
|
||||
cp -a /usr/bin/AdGuardHome/* /mnt/${EMMC_NAME}${PARTITION_NAME}4/AdGuardHome/
|
||||
fi
|
||||
ln -sf /mnt/${EMMC_NAME}${PARTITION_NAME}4/AdGuardHome /mnt/${NEW_ROOT_NAME}/usr/bin/AdGuardHome
|
||||
fi
|
||||
|
||||
#rm -f /mnt/${NEW_ROOT_NAME}/usr/sbin/openwrt-install-amlogic
|
||||
#rm -f /mnt/${NEW_ROOT_NAME}/usr/sbin/openwrt-update-amlogic
|
||||
rm -f /mnt/${NEW_ROOT_NAME}/root/install-to-emmc.sh
|
||||
sync
|
||||
echo "Copy data complete ..."
|
||||
|
||||
BACKUP_LIST=$(${P2}/usr/sbin/openwrt-backup -p)
|
||||
if [[ "${BR_FLAG}" -eq "1" && -n "${BACKUP_LIST}" ]]; then
|
||||
echo -n "Start restoring configuration files ... "
|
||||
(
|
||||
cd /
|
||||
eval tar czf ${NEW_ROOT_MP}/.reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
)
|
||||
tar xzf ${NEW_ROOT_MP}/.reserved/openwrt_config.tar.gz
|
||||
|
||||
[ -f ./etc/config/dockerman ] && sed -e "s/option wan_mode 'false'/option wan_mode 'true'/" -i ./etc/config/dockerman 2>/dev/null
|
||||
[ -f ./etc/config/dockerd ] && sed -e "s/option wan_mode '0'/option wan_mode '1'/" -i ./etc/config/dockerd 2>/dev/null
|
||||
[ -f ./etc/config/verysync ] && sed -e 's/config setting/config verysync/' -i ./etc/config/verysync 2>/dev/null
|
||||
|
||||
# Restore fstab
|
||||
cp -f .snapshots/etc-000/fstab ./etc/fstab
|
||||
cp -f .snapshots/etc-000/config/fstab ./etc/config/fstab
|
||||
# 还原 luci
|
||||
cp -f .snapshots/etc-000/config/luci ./etc/config/luci
|
||||
# 还原/etc/config/rpcd
|
||||
cp -f .snapshots/etc-000/config/rpcd ./etc/config/rpcd
|
||||
|
||||
sync
|
||||
echo "Restore configuration information complete."
|
||||
fi
|
||||
|
||||
echo "Modify the configuration file ... "
|
||||
rm -f "./etc/rc.local.orig" "./etc/first_run.sh" "./etc/part_size"
|
||||
rm -rf "./opt/docker" && ln -sf "/mnt/${EMMC_NAME}${PARTITION_NAME}4/docker" "./opt/docker"
|
||||
rm -f ./etc/bench.log
|
||||
cat >>./etc/crontabs/root <<EOF
|
||||
37 5 * * * /etc/coremark.sh
|
||||
EOF
|
||||
|
||||
sed -e 's/ttyAMA0/ttyAML0/' -i ./etc/inittab
|
||||
sed -e 's/ttyS0/tty0/' -i ./etc/inittab
|
||||
sss=$(date +%s)
|
||||
ddd=$((sss / 86400))
|
||||
sed -e "s/:0:0:99999:7:::/:${ddd}:0:99999:7:::/" -i ./etc/shadow
|
||||
# Fix the problem of repeatedly adding amule entries after each upgrade
|
||||
sed -e "/amule:x:/d" -i ./etc/shadow
|
||||
# Fix the problem of repeatedly adding sshd entries after each upgrade of dropbear
|
||||
sed -e "/sshd:x:/d" -i ./etc/shadow
|
||||
if ! grep "sshd:x:22:sshd" ./etc/group >/dev/null; then
|
||||
echo "sshd:x:22:sshd" >>./etc/group
|
||||
fi
|
||||
if ! grep "sshd:x:22:22:sshd:" ./etc/passwd >/dev/null; then
|
||||
echo "sshd:x:22:22:sshd:/var/run/sshd:/bin/false" >>./etc/passwd
|
||||
fi
|
||||
if ! grep "sshd:x:" ./etc/shadow >/dev/null; then
|
||||
echo "sshd:x:${ddd}:0:99999:7:::" >>./etc/shadow
|
||||
fi
|
||||
|
||||
if [ "${BR_FLAG}" -eq "1" ]; then
|
||||
if [ -x ./bin/bash ] && [ -f ./etc/profile.d/30-sysinfo.sh ]; then
|
||||
sed -e 's/\/bin\/ash/\/bin\/bash/' -i ./etc/passwd
|
||||
fi
|
||||
sync
|
||||
fi
|
||||
sed -e "s/option hw_flow '1'/option hw_flow '0'/" -i ./etc/config/turboacc
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
eval tar czf .reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
|
||||
rm -f ./etc/part_size ./etc/first_run.sh
|
||||
if [ -x ./usr/sbin/balethirq.pl ]; then
|
||||
if grep "balethirq.pl" "./etc/rc.local"; then
|
||||
echo "balance irq is enabled"
|
||||
else
|
||||
echo "enable balance irq"
|
||||
sed -e "/exit/i\/usr/sbin/balethirq.pl" -i ./etc/rc.local
|
||||
fi
|
||||
fi
|
||||
|
||||
mv ./etc/rc.local ./etc/rc.local.orig
|
||||
cat >"./etc/rc.local" <<EOF
|
||||
if ! ls /etc/rc.d/S??dockerd >/dev/null 2>&1;then
|
||||
/etc/init.d/dockerd enable
|
||||
/etc/init.d/dockerd start
|
||||
fi
|
||||
if ! ls /etc/rc.d/S??dockerman >/dev/null 2>&1 && [ -f /etc/init.d/dockerman ];then
|
||||
/etc/init.d/dockerman enable
|
||||
/etc/init.d/dockerman start
|
||||
fi
|
||||
mv /etc/rc.local.orig /etc/rc.local
|
||||
chmod 755 /etc/rc.local
|
||||
exec /etc/rc.local
|
||||
exit
|
||||
EOF
|
||||
chmod 755 ./etc/rc.local*
|
||||
|
||||
#Mainline U-BOOT detection
|
||||
FLASH_MAINLINE_UBOOT=0
|
||||
if [[ -n "${MAINLINE_UBOOT}" && -f "${P2}${MAINLINE_UBOOT}" ]]; then
|
||||
cat <<EOF
|
||||
----------------------------------------------------------------------------------
|
||||
Found an available mainline bootloader (Mainline u-boot), you can flash into EMMC.
|
||||
----------------------------------------------------------------------------------
|
||||
EOF
|
||||
while :; do
|
||||
if [[ "${AUTO_MAINLINE_UBOOT}" == "yes" ]]; then
|
||||
if [[ "${K510}" -eq "1" ]]; then
|
||||
yn="y"
|
||||
else
|
||||
yn="n"
|
||||
fi
|
||||
elif [[ "${AUTO_MAINLINE_UBOOT}" == "no" ]]; then
|
||||
yn="n"
|
||||
else
|
||||
read -p "Please choose whether to write the mainline bootloader to EMMC? y/n " yn
|
||||
fi
|
||||
|
||||
case ${yn} in
|
||||
y | Y)
|
||||
FLASH_MAINLINE_UBOOT=1
|
||||
break
|
||||
;;
|
||||
n | N)
|
||||
FLASH_MAINLINE_UBOOT=0
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "${FLASH_MAINLINE_UBOOT}" -eq "1" ]]; then
|
||||
echo -e "Write Mainline bootloader: [ ${MAINLINE_UBOOT} ]"
|
||||
dd if=${P2}${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=1 count=444 conv=fsync
|
||||
dd if=${P2}${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=512 skip=1 seek=1 conv=fsync
|
||||
elif [[ -n "${ANDROID_UBOOT}" && -f "${P2}${ANDROID_UBOOT}" ]]; then
|
||||
echo -e "Write Android bootloader: [ ${ANDROID_UBOOT} ]"
|
||||
dd if=${P2}${ANDROID_UBOOT} of=/dev/${EMMC_NAME} bs=1 count=444 conv=fsync
|
||||
dd if=${P2}${ANDROID_UBOOT} of=/dev/${EMMC_NAME} bs=512 skip=1 seek=1 conv=fsync
|
||||
else
|
||||
echo "Did not change the original bootloader."
|
||||
fi
|
||||
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
|
||||
echo "Create etc snapshot -> .snapshots/etc-001"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-001
|
||||
|
||||
cd ${WORK_DIR}
|
||||
|
||||
echo "Change the label of ${BOOT_PATH} ... "
|
||||
fatlabel ${BOOT_PATH} "${LB_PRE}BOOT"
|
||||
echo "Start copying data, from [ ${P1} ] to [ /boot ] ..."
|
||||
cd /boot
|
||||
echo "Delete the old boot file ..."
|
||||
rm -rf * && sync
|
||||
|
||||
echo "Copy the new boot file ... "
|
||||
(cd ${P1} && tar cf - .) | tar xf - 2>/dev/null
|
||||
sync
|
||||
|
||||
if [ -f ${P1}/uInitrd ]; then
|
||||
i=1
|
||||
max_try=10
|
||||
while [ "${i}" -le "${max_try}" ]; do
|
||||
uInitrd_original=$(md5sum ${P1}/uInitrd | awk '{print $1}')
|
||||
uInitrd_new=$(md5sum uInitrd | awk '{print $1}')
|
||||
if [ "${uInitrd_original}" = "${uInitrd_new}" ]; then
|
||||
break
|
||||
else
|
||||
rm -f uInitrd && sync
|
||||
cp -f ${P1}/uInitrd uInitrd 2>/dev/null && sync
|
||||
let i++
|
||||
continue
|
||||
fi
|
||||
done
|
||||
[ "${i}" -eq "10" ] && error_msg "uInitrd file copy failed."
|
||||
else
|
||||
echo "uInitrd file is missing."
|
||||
fi
|
||||
|
||||
if [ -f ${P1}/zImage ]; then
|
||||
i=1
|
||||
max_try=10
|
||||
while [ "${i}" -le "${max_try}" ]; do
|
||||
zImage_original=$(md5sum ${P1}/zImage | awk '{print $1}')
|
||||
zImage_new=$(md5sum zImage | awk '{print $1}')
|
||||
if [ "${zImage_original}" = "${zImage_new}" ]; then
|
||||
break
|
||||
else
|
||||
rm -f zImage && sync
|
||||
cp -f ${P1}/zImage zImage 2>/dev/null && sync
|
||||
let i++
|
||||
continue
|
||||
fi
|
||||
done
|
||||
[ "${i}" -eq "10" ] && error_msg "zImage file copy failed."
|
||||
else
|
||||
error_msg "zImage file is missing."
|
||||
fi
|
||||
|
||||
if [ ${ROOT_DISK_TYPE} == "EMMC" ]; then
|
||||
rm -f s905_autoscript* aml_autoscript*
|
||||
mv -f boot-emmc.ini boot.ini
|
||||
mv -f boot-emmc.cmd boot.cmd
|
||||
mv -f boot-emmc.scr boot.scr
|
||||
fi
|
||||
|
||||
if [ "${K510}" -eq "1" ]; then
|
||||
if [ -f "u-boot.ext" ]; then
|
||||
cp -vf u-boot.ext u-boot.emmc
|
||||
elif [ -f ${P1}/${UBOOT_OVERLOAD} ]; then
|
||||
cp -vf ${P1}/${UBOOT_OVERLOAD} ${UBOOT_OVERLOAD}
|
||||
cp -vf ${P1}/${UBOOT_OVERLOAD} u-boot.ext
|
||||
cp -vf ${P1}/${UBOOT_OVERLOAD} u-boot.emmc
|
||||
chmod +x u-boot*
|
||||
fi
|
||||
else
|
||||
rm -f u-boot.ext 2>/dev/null
|
||||
rm -f u-boot.emmc 2>/dev/null
|
||||
fi
|
||||
|
||||
sync
|
||||
|
||||
echo "Update boot parameters ... "
|
||||
if [ -f /tmp/uEnv.txt ]; then
|
||||
lines=$(wc -l </tmp/uEnv.txt)
|
||||
lines=$((lines - 1))
|
||||
head -n $lines /tmp/uEnv.txt >uEnv.txt
|
||||
cat >>uEnv.txt <<EOF
|
||||
APPEND=root=UUID=${NEW_ROOT_UUID} rootfstype=btrfs rootflags=compress=zstd:${ZSTD_LEVEL} console=ttyAML0,115200n8 console=tty0 no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
|
||||
EOF
|
||||
else
|
||||
cat >uEnv.txt <<EOF
|
||||
LINUX=/zImage
|
||||
INITRD=/uInitrd
|
||||
FDT=${CUR_FDTFILE}
|
||||
APPEND=root=UUID=${NEW_ROOT_UUID} rootfstype=btrfs rootflags=compress=zstd:${ZSTD_LEVEL} console=ttyAML0,115200n8 console=tty0 no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
|
||||
EOF
|
||||
fi
|
||||
sync
|
||||
|
||||
# Replace the UUID for extlinux/extlinux.conf if it exists
|
||||
[[ -f "extlinux/extlinux.conf" ]] && {
|
||||
sed -i -E "s|UUID=[^ ]*|UUID=${NEW_ROOT_UUID}|" extlinux/extlinux.conf 2>/dev/null
|
||||
}
|
||||
|
||||
cd $WORK_DIR
|
||||
umount -f ${P1} ${P2} 2>/dev/null
|
||||
losetup -D 2>/dev/null
|
||||
rm -rf ${P1} ${P2} 2>/dev/null
|
||||
rm -f ${IMG_NAME} 2>/dev/null
|
||||
rm -f sha256sums 2>/dev/null
|
||||
sync
|
||||
wait
|
||||
|
||||
echo "Successfully updated, automatic restarting..."
|
||||
sleep 3
|
||||
reboot
|
||||
exit 0
|
||||
@@ -0,0 +1,674 @@
|
||||
#!/bin/bash
|
||||
#======================================================================================
|
||||
# Function: Update the QEMU AARCH64 KVM vitual machine openwrt firmware
|
||||
# Copyright (C) 2022-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2022-- https://github.com/ophub
|
||||
#======================================================================================
|
||||
#
|
||||
# The script supports directly setting parameters for update, skipping interactive selection
|
||||
# openwrt-update-amlogic ${OPENWRT_FILE} ${AUTO_MAINLINE_UBOOT} ${RESTORE_CONFIG} ${WORK_DIR}
|
||||
# E.g: openwrt-update-kvm openwrt_qemu-aarch64.img.gz no restore /mnt/vda4
|
||||
# E.g: openwrt-update-kvm openwrt_qemu-aarch64.img.gz no no-restore /mnt/data
|
||||
|
||||
# You can also execute the script directly, and interactively select related functions
|
||||
# E.g: openwrt-update-kvm
|
||||
#
|
||||
#======================================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "[ERROR] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the /boot file system
|
||||
get_boot_partition_name() {
|
||||
local paths="/boot"
|
||||
local partition_name
|
||||
|
||||
partition_name=$(df "${paths}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Get the partition message of the root file system
|
||||
get_root_partition_msg() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_msg=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ "^" "'"${path}"'" "$" {print $0}')
|
||||
[[ -n "${partition_msg}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_msg}" ]] && error_msg "Cannot find the root partition message!"
|
||||
echo "${partition_msg}"
|
||||
}
|
||||
|
||||
# Receive one-key command related parameters
|
||||
IMG_NAME="${1}"
|
||||
AUTO_MAINLINE_UBOOT="${2}"
|
||||
BACKUP_RESTORE_CONFIG="${3}"
|
||||
WORK_DIR="${4}"
|
||||
|
||||
# Check the necessary dependencies
|
||||
DEPENDS="lsblk uuidgen grep awk btrfs mkfs.fat mkfs.btrfs md5sum fatlabel jq"
|
||||
echo "Check the necessary dependencies..."
|
||||
for dep in ${DEPENDS}; do
|
||||
WITCH=$(busybox which ${dep})
|
||||
if [ "${WITCH}" == "" ]; then
|
||||
error_msg "Dependent command: ${dep} does not exist, upgrade cannot be performed!"
|
||||
else
|
||||
echo "${dep} path: ${WITCH}"
|
||||
fi
|
||||
done
|
||||
echo "Check passed"
|
||||
|
||||
# Current device model
|
||||
source /etc/flippy-openwrt-release
|
||||
if [[ -z "${PLATFORM}" ]]; then
|
||||
error_msg "The platform is empty."
|
||||
elif [[ "${PLATFORM}" != "qemu-aarch64" ]]; then
|
||||
error_msg "The platform is missing, only support qemu-aarch64."
|
||||
else
|
||||
echo -e "Current platform: [ ${PLATFORM} ]"
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Check and repair partition mount points
|
||||
[[ -z "$(get_boot_partition_name)" || -z "$(get_root_partition_name)" ]] && {
|
||||
echo -e "Partition mount point error, start repairing..."
|
||||
openwrt-backup -g
|
||||
}
|
||||
|
||||
# Find the partition where root is located
|
||||
# vda2 or vda3
|
||||
ROOT_PTNAME="$(get_root_partition_name)"
|
||||
|
||||
# Find the disk where the partition is located, only supports sd?? hd?? vd??
|
||||
case ${ROOT_PTNAME} in
|
||||
[hsv]d[a-z][1-4])
|
||||
DISK_NAME=$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')
|
||||
PART_PRESTR=""
|
||||
LABEL_PRESTR=""
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
function get_docker_root {
|
||||
local data_root
|
||||
if [[ -f /etc/config/dockerd ]]; then
|
||||
data_root=$(uci get dockerd.globals.data_root)
|
||||
fi
|
||||
if [[ -z "${data_root}" ]] && [[ -f /etc/docker/daemon.json ]]; then
|
||||
data_root=$(jq -r '."data-root"' /etc/docker/daemon.json)
|
||||
fi
|
||||
echo "$data_root"
|
||||
}
|
||||
DOCKER_ROOT=$(get_docker_root)
|
||||
|
||||
if [[ -z "${WORK_DIR}" ]]; then
|
||||
WORK_DIR="/mnt/${DISK_NAME}${PART_PRESTR}4/"
|
||||
fi
|
||||
if [[ ! -d "${WORK_DIR}" ]]; then
|
||||
error_msg "the work directory is not exists. [ ${WORK_DIR} ]"
|
||||
fi
|
||||
|
||||
cd "${WORK_DIR}"
|
||||
if [[ -d "/tmp/upload" ]] && [[ -f "/tmp/upload/*img*" ]]; then
|
||||
mv -f /tmp/upload/*img* .
|
||||
if [[ $? -ne 0 ]]; then
|
||||
error_msg "move file failed."
|
||||
fi
|
||||
fi
|
||||
sync
|
||||
|
||||
if [[ "${IMG_NAME}" == *.img ]]; then
|
||||
echo -e "Update using [ ${IMG_NAME} ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
echo -e "Update using [ ${IMG_NAME} ] ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img.xz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
xz_file=$(ls *.img.xz | head -n 1)
|
||||
echo -e "Update using [ ${xz_file} ] file. Please wait a moment ..."
|
||||
xz -d "${xz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.img.gz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.img.gz | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
gzip -df "${gz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.7z -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.7z | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
bsdtar -xmf "${gz_file}" 2>/dev/null
|
||||
[ $? -eq 0 ] || 7z x "${gz_file}" -aoa -y 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.zip -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
zip_file=$(ls *.zip | head -n 1)
|
||||
echo -e "Update using [ ${zip_file} ] file. Please wait a moment ..."
|
||||
unzip -o "${zip_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
else
|
||||
echo -e "Please upload or specify the update openwrt firmware file."
|
||||
#echo -e "Upload method: system menu → Amlogic Service → Manually Upload Update"
|
||||
echo -e "Specify method: Place the openwrt firmware file in [ ${WORK_DIR} ]"
|
||||
echo -e "The supported file suffixes are: *.img, *.img.xz, *.img.gz, *.7z, *.zip"
|
||||
echo -e "After upload the openwrt firmware file, run again."
|
||||
exit 1
|
||||
fi
|
||||
sync
|
||||
|
||||
# check file
|
||||
if [ ! -f "${IMG_NAME}" ]; then
|
||||
error_msg "No update file found."
|
||||
else
|
||||
echo "Start update from [ ${IMG_NAME} ]"
|
||||
fi
|
||||
|
||||
# find efi partition
|
||||
EFI_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ /^\/boot\/efi$/ {print $0}')
|
||||
if [ "${EFI_PART_MSG}" == "" ]; then
|
||||
error_msg "The EFI partition does not exist, so the update cannot be continued!"
|
||||
fi
|
||||
EFI_NAME=$(echo $EFI_PART_MSG | awk '{print $1}')
|
||||
EFI_DEV=$(echo $EFI_PART_MSG | awk '{print $2}')
|
||||
EFI_UUID=$(echo $EFI_PART_MSG | awk '{print $4}')
|
||||
|
||||
BR_FLAG=1
|
||||
if [[ ${BACKUP_RESTORE_CONFIG} == "restore" ]]; then
|
||||
BR_FLAG=1
|
||||
elif [[ ${BACKUP_RESTORE_CONFIG} == "no-restore" ]]; then
|
||||
BR_FLAG=0
|
||||
else
|
||||
echo -ne "Whether to backup and restore the current config files? y/n [y]\b\b"
|
||||
read yn
|
||||
case $yn in
|
||||
n* | N*) BR_FLAG=0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# find root partition
|
||||
ROOT_PART_MSG="$(get_root_partition_msg)"
|
||||
ROOT_NAME=$(echo ${ROOT_PART_MSG} | awk '{print $1}')
|
||||
ROOT_DEV=$(echo ${ROOT_PART_MSG} | awk '{print $2}')
|
||||
ROOT_UUID=$(echo ${ROOT_PART_MSG} | awk '{print $4}')
|
||||
|
||||
case ${ROOT_NAME} in
|
||||
${DISK_NAME}${PART_PRESTR}2)
|
||||
NEW_ROOT_NAME="${DISK_NAME}${PART_PRESTR}3"
|
||||
NEW_ROOT_LABEL="${LABEL_PRESTR}ROOTFS2"
|
||||
;;
|
||||
${DISK_NAME}${PART_PRESTR}3)
|
||||
NEW_ROOT_NAME="${DISK_NAME}${PART_PRESTR}2"
|
||||
NEW_ROOT_LABEL="${LABEL_PRESTR}ROOTFS1"
|
||||
;;
|
||||
*)
|
||||
error_msg "ROOTFS The partition location is incorrect, so the update cannot continue!"
|
||||
;;
|
||||
esac
|
||||
echo "NEW_ROOT_NAME: [ ${NEW_ROOT_NAME} ]"
|
||||
|
||||
# find new root partition
|
||||
NEW_ROOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | grep "${NEW_ROOT_NAME}" | awk '$3 ~ /^part$/ && $5 !~ /^\/$/ && $5 !~ /^\/boot$/ {print $0}')
|
||||
if [ "${NEW_ROOT_PART_MSG}" == "" ]; then
|
||||
error_msg "The new ROOTFS partition does not exist, so the update cannot continue!"
|
||||
fi
|
||||
NEW_ROOT_NAME=$(echo $NEW_ROOT_PART_MSG | awk '{print $1}')
|
||||
NEW_ROOT_DEV=$(echo $NEW_ROOT_PART_MSG | awk '{print $2}')
|
||||
NEW_ROOT_UUID=$(echo $NEW_ROOT_PART_MSG | awk '{print $4}')
|
||||
NEW_ROOT_MOUNTPOINT=$(echo $NEW_ROOT_PART_MSG | awk '{print $5}')
|
||||
echo "NEW_ROOT_MOUNTPOINT: [ ${NEW_ROOT_MOUNTPOINT} ]"
|
||||
|
||||
# losetup
|
||||
losetup -f -P $IMG_NAME
|
||||
if [ $? -eq 0 ]; then
|
||||
LOOP_DEV=$(losetup | grep "$IMG_NAME" | awk '{print $1}')
|
||||
if [ "$LOOP_DEV" == "" ]; then
|
||||
error_msg "loop device not found!"
|
||||
fi
|
||||
else
|
||||
error_msg "losetup [ $IMG_FILE ] failed!"
|
||||
fi
|
||||
|
||||
# fix loopdev issue in kernel 5.19
|
||||
function fix_loopdev() {
|
||||
local parentdev=${1##*/}
|
||||
if [ ! -d /sys/block/${parentdev} ]; then
|
||||
return
|
||||
fi
|
||||
subdevs=$(lsblk -l -o NAME | grep -E "^${parentdev}.+\$")
|
||||
for subdev in $subdevs; do
|
||||
if [ ! -d /sys/block/${parentdev}/${subdev} ]; then
|
||||
return
|
||||
elif [ -b /dev/${sub_dev} ]; then
|
||||
continue
|
||||
fi
|
||||
source /sys/block/${parentdev}/${subdev}/uevent
|
||||
mknod /dev/${subdev} b ${MAJOR} ${MINOR}
|
||||
unset MAJOR MINOR DEVNAME DEVTYPE DISKSEQ PARTN PARTNAME
|
||||
done
|
||||
}
|
||||
fix_loopdev ${LOOP_DEV}
|
||||
|
||||
WAIT=3
|
||||
echo "The loopdev is [ $LOOP_DEV ], wait [ ${WAIT} ] seconds. "
|
||||
while [ $WAIT -ge 1 ]; do
|
||||
sleep 1
|
||||
WAIT=$((WAIT - 1))
|
||||
done
|
||||
|
||||
# umount loop devices (openwrt will auto mount some partition)
|
||||
MOUNTED_DEVS=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "$LOOP_DEV" | awk '$3 !~ /^$/ {print $2}')
|
||||
for dev in $MOUNTED_DEVS; do
|
||||
while :; do
|
||||
echo "umount [ $dev ] ... "
|
||||
umount -f $dev
|
||||
sleep 1
|
||||
mnt=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "$dev" | awk '$3 !~ /^$/ {print $2}')
|
||||
if [ "$mnt" == "" ]; then
|
||||
break
|
||||
else
|
||||
echo "Retry ..."
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# mount src part
|
||||
P1=${WORK_DIR}/efi
|
||||
P2=${WORK_DIR}/root
|
||||
mkdir -p $P1 $P2
|
||||
|
||||
echo "Mount [ ${LOOP_DEV}p1 ] -> [ ${P1} ] ... "
|
||||
mount -t vfat -o ro ${LOOP_DEV}p1 ${P1}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mount p1 [ ${LOOP_DEV}p1 ] failed!"
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Mount [ ${LOOP_DEV}p2 ] -> [ ${P2} ] ... "
|
||||
ZSTD_LEVEL="6"
|
||||
mount -t btrfs -o ro,compress=zstd:${ZSTD_LEVEL} ${LOOP_DEV}p2 ${P2}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mount p2 [ ${LOOP_DEV}p2 ] failed!"
|
||||
umount -f ${P1}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prepare the dockerman config file
|
||||
if [ -f ${P2}/etc/init.d/dockerman ] && [ -f ${P2}/etc/config/dockerd ]; then
|
||||
|
||||
flg=0
|
||||
# get current docker data root
|
||||
data_root=$(uci get dockerd.globals.data_root 2>/dev/null)
|
||||
if [ "$data_root" == "" ]; then
|
||||
flg=1
|
||||
# get current config from /etc/docker/daemon.json
|
||||
if [ -f "/etc/docker/daemon.json" ] && [ -x "/usr/bin/jq" ]; then
|
||||
data_root=$(jq -r '."data-root"' /etc/docker/daemon.json)
|
||||
|
||||
bip=$(jq -r '."bip"' /etc/docker/daemon.json)
|
||||
[ "$bip" == "null" ] && bip="172.31.0.1/24"
|
||||
|
||||
log_level=$(jq -r '."log-level"' /etc/docker/daemon.json)
|
||||
[ "$log_level" == "null" ] && log_level="warn"
|
||||
|
||||
_iptables=$(jq -r '."iptables"' /etc/docker/daemon.json)
|
||||
[ "$_iptables" == "null" ] && _iptables="true"
|
||||
|
||||
registry_mirrors=$(jq -r '."registry-mirrors"[]' /etc/docker/daemon.json 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$data_root" == "" ]; then
|
||||
data_root="/opt/docker/" # the default data root
|
||||
fi
|
||||
|
||||
if ! uci get dockerd.globals >/dev/null 2>&1; then
|
||||
uci set dockerd.globals='globals'
|
||||
uci commit
|
||||
fi
|
||||
|
||||
# delete alter config , use inner config
|
||||
if uci get dockerd.globals.alt_config_file >/dev/null 2>&1; then
|
||||
uci delete dockerd.globals.alt_config_file
|
||||
uci commit
|
||||
fi
|
||||
|
||||
if [ $flg -eq 1 ]; then
|
||||
uci set dockerd.globals.data_root=$data_root
|
||||
[ "$bip" != "" ] && uci set dockerd.globals.bip=$bip
|
||||
[ "$log_level" != "" ] && uci set dockerd.globals.log_level=$log_level
|
||||
[ "$_iptables" != "" ] && uci set dockerd.globals.iptables=$_iptables
|
||||
if [ "$registry_mirrors" != "" ]; then
|
||||
for reg in $registry_mirrors; do
|
||||
uci add_list dockerd.globals.registry_mirrors=$reg
|
||||
done
|
||||
fi
|
||||
uci set dockerd.globals.auto_start='1'
|
||||
uci commit
|
||||
fi
|
||||
fi
|
||||
|
||||
#format NEW_ROOT
|
||||
echo "umount [ ${NEW_ROOT_MOUNTPOINT} ]"
|
||||
umount -f "${NEW_ROOT_MOUNTPOINT}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Umount [ ${NEW_ROOT_MOUNTPOINT} ] failed, Please restart and try again!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Format [ ${NEW_ROOT_DEV} ]"
|
||||
NEW_ROOT_UUID=$(uuidgen)
|
||||
mkfs.btrfs -f -U ${NEW_ROOT_UUID} -L ${NEW_ROOT_LABEL} ${NEW_ROOT_DEV}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Format [ ${NEW_ROOT_DEV} ] failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Mount [ ${NEW_ROOT_DEV} ] -> [ ${NEW_ROOT_MOUNTPOINT} ]"
|
||||
mount -t btrfs -o compress=zstd:${ZSTD_LEVEL} ${NEW_ROOT_DEV} ${NEW_ROOT_MOUNTPOINT}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mount [ ${NEW_ROOT_DEV} ] -> [ ${NEW_ROOT_MOUNTPOINT} ] failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# begin copy rootfs
|
||||
cd ${NEW_ROOT_MOUNTPOINT}
|
||||
echo "Start copying data, From [ ${P2} ] TO [ ${NEW_ROOT_MOUNTPOINT} ] ..."
|
||||
ENTRYS=$(ls)
|
||||
for entry in $ENTRYS; do
|
||||
if [ "$entry" == "lost+found" ]; then
|
||||
continue
|
||||
fi
|
||||
echo "Remove old [ $entry ] ... "
|
||||
rm -rf $entry
|
||||
if [ $? -ne 0 ]; then
|
||||
error_msg "failed."
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Create folder ... "
|
||||
btrfs subvolume create etc
|
||||
mkdir -p .snapshots .reserved bin boot dev lib opt mnt overlay proc rom root run sbin sys tmp usr www
|
||||
ln -sf lib/ lib64
|
||||
ln -sf tmp/ var
|
||||
sync
|
||||
|
||||
COPY_SRC="boot root etc bin sbin lib opt usr www"
|
||||
echo "Copy data begin ... "
|
||||
for src in $COPY_SRC; do
|
||||
echo "Copy [ $src ] ... "
|
||||
(cd ${P2} && tar cf - $src) | tar xf -
|
||||
sync
|
||||
done
|
||||
|
||||
# if not backup, then force rewrite the etc/docker/daemon.json
|
||||
if [ "${BR_FLAG}" -eq 0 ]; then
|
||||
cat >./etc/docker/daemon.json <<EOF
|
||||
{
|
||||
"bip": "172.31.0.1/24",
|
||||
"data-root": "${DOCKER_ROOT}",
|
||||
"log-level": "warn",
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "5"
|
||||
},
|
||||
"registry-mirrors": [
|
||||
"https://mirror.baidubce.com/",
|
||||
"https://hub-mirror.c.163.com"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >./etc/fstab <<EOF
|
||||
UUID=${NEW_ROOT_UUID} / btrfs compress=zstd:${ZSTD_LEVEL} 0 1
|
||||
LABEL=EFI /boot/efi vfat defaults 0 2
|
||||
#tmpfs /tmp tmpfs defaults,nosuid 0 0
|
||||
EOF
|
||||
|
||||
cat >./etc/config/fstab <<EOF
|
||||
config global
|
||||
option anon_swap '0'
|
||||
option anon_mount '1'
|
||||
option auto_swap '0'
|
||||
option auto_mount '1'
|
||||
option delay_root '5'
|
||||
option check_fs '0'
|
||||
|
||||
config mount
|
||||
option target '/rom'
|
||||
option uuid '${NEW_ROOT_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'btrfs'
|
||||
option options 'compress=zstd:${ZSTD_LEVEL}'
|
||||
|
||||
config mount
|
||||
option target '/boot/efi'
|
||||
option label 'EFI'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'vfat'
|
||||
|
||||
EOF
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
sync
|
||||
|
||||
echo "Create initial etc snapshot -> .snapshots/etc-000"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-000
|
||||
sync
|
||||
|
||||
[ -d /mnt/${DISK_NAME}${PART_PRESTR}4/docker ] || mkdir -p /mnt/${DISK_NAME}${PART_PRESTR}4/docker
|
||||
rm -rf opt/docker && ln -sf /mnt/${DISK_NAME}${PART_PRESTR}4/docker/ opt/docker
|
||||
|
||||
if [ -f /mnt/${NEW_ROOT_NAME}/etc/config/AdGuardHome ]; then
|
||||
[ -d /mnt/${DISK_NAME}${PART_PRESTR}4/AdGuardHome/data ] || mkdir -p /mnt/${DISK_NAME}${PART_PRESTR}4/AdGuardHome/data
|
||||
if [ ! -L /usr/bin/AdGuardHome ]; then
|
||||
[ -d /usr/bin/AdGuardHome ] &&
|
||||
cp -a /usr/bin/AdGuardHome/* /mnt/${DISK_NAME}${PART_PRESTR}4/AdGuardHome/
|
||||
fi
|
||||
ln -sf /mnt/${DISK_NAME}${PART_PRESTR}4/AdGuardHome /mnt/${NEW_ROOT_NAME}/usr/bin/AdGuardHome
|
||||
fi
|
||||
sync
|
||||
echo "Copy data complete ..."
|
||||
|
||||
BACKUP_LIST=$(${P2}/usr/sbin/openwrt-backup -p)
|
||||
if [[ "${BR_FLAG}" -eq "1" && -n "${BACKUP_LIST}" ]]; then
|
||||
echo -n "Start restoring configuration files ... "
|
||||
(
|
||||
cd /
|
||||
eval tar czf ${NEW_ROOT_MOUNTPOINT}/.reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
)
|
||||
tar xzf ${NEW_ROOT_MOUNTPOINT}/.reserved/openwrt_config.tar.gz
|
||||
|
||||
[ -f ./etc/config/dockerman ] && sed -e "s/option wan_mode 'false'/option wan_mode 'true'/" -i ./etc/config/dockerman 2>/dev/null
|
||||
[ -f ./etc/config/dockerd ] && sed -e "s/option wan_mode '0'/option wan_mode '1'/" -i ./etc/config/dockerd 2>/dev/null
|
||||
[ -f ./etc/config/verysync ] && sed -e 's/config setting/config verysync/' -i ./etc/config/verysync 2>/dev/null
|
||||
|
||||
# Restore fstab
|
||||
cp -f .snapshots/etc-000/fstab ./etc/fstab
|
||||
cp -f .snapshots/etc-000/config/fstab ./etc/config/fstab
|
||||
# 还原 luci
|
||||
cp -f .snapshots/etc-000/config/luci ./etc/config/luci
|
||||
# 还原/etc/config/rpcd
|
||||
cp -f .snapshots/etc-000/config/rpcd ./etc/config/rpcd
|
||||
|
||||
sync
|
||||
echo "Restore configuration information complete."
|
||||
fi
|
||||
|
||||
echo "Modify the configuration file ... "
|
||||
rm -f "./etc/rc.local.orig" "./etc/first_run.sh" "./etc/part_size"
|
||||
rm -rf "./opt/docker" && ln -sf "/mnt/${DISK_NAME}${PART_PRESTR}4/docker" "./opt/docker"
|
||||
rm -f ./etc/bench.log
|
||||
cat >>./etc/crontabs/root <<EOF
|
||||
37 5 * * * /etc/coremark.sh
|
||||
EOF
|
||||
|
||||
sss=$(date +%s)
|
||||
ddd=$((sss / 86400))
|
||||
sed -e "s/:0:0:99999:7:::/:${ddd}:0:99999:7:::/" -i ./etc/shadow
|
||||
# Fix the problem of repeatedly adding amule entries after each upgrade
|
||||
sed -e "/amule:x:/d" -i ./etc/shadow
|
||||
# Fix the problem of repeatedly adding sshd entries after each upgrade of dropbear
|
||||
sed -e "/sshd:x:/d" -i ./etc/shadow
|
||||
if ! grep "sshd:x:22:sshd" ./etc/group >/dev/null; then
|
||||
echo "sshd:x:22:sshd" >>./etc/group
|
||||
fi
|
||||
if ! grep "sshd:x:22:22:sshd:" ./etc/passwd >/dev/null; then
|
||||
echo "sshd:x:22:22:sshd:/var/run/sshd:/bin/false" >>./etc/passwd
|
||||
fi
|
||||
if ! grep "sshd:x:" ./etc/shadow >/dev/null; then
|
||||
echo "sshd:x:${ddd}:0:99999:7:::" >>./etc/shadow
|
||||
fi
|
||||
|
||||
if [ "${BR_FLAG}" -eq "1" ]; then
|
||||
if [ -x ./bin/bash ] && [ -f ./etc/profile.d/30-sysinfo.sh ]; then
|
||||
sed -e 's/\/bin\/ash/\/bin\/bash/' -i ./etc/passwd
|
||||
fi
|
||||
sync
|
||||
fi
|
||||
sed -e "s/option hw_flow '1'/option hw_flow '0'/" -i ./etc/config/turboacc
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
eval tar czf .reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
|
||||
rm -f ./etc/part_size ./etc/first_run.sh
|
||||
if [ -x ./usr/sbin/balethirq.pl ]; then
|
||||
if grep "balethirq.pl" "./etc/rc.local"; then
|
||||
echo "balance irq is enabled"
|
||||
else
|
||||
echo "enable balance irq"
|
||||
sed -e "/exit/i\/usr/sbin/balethirq.pl" -i ./etc/rc.local
|
||||
fi
|
||||
fi
|
||||
|
||||
mv ./etc/rc.local ./etc/rc.local.orig
|
||||
cat >"./etc/rc.local" <<EOF
|
||||
if ! ls /etc/rc.d/S??dockerd >/dev/null 2>&1;then
|
||||
/etc/init.d/dockerd enable
|
||||
/etc/init.d/dockerd start
|
||||
fi
|
||||
if ! ls /etc/rc.d/S??dockerman >/dev/null 2>&1 && [ -f /etc/init.d/dockerman ];then
|
||||
/etc/init.d/dockerman enable
|
||||
/etc/init.d/dockerman start
|
||||
fi
|
||||
mv /etc/rc.local.orig /etc/rc.local
|
||||
chmod 755 /etc/rc.local
|
||||
exec /etc/rc.local
|
||||
exit
|
||||
EOF
|
||||
chmod 755 ./etc/rc.local*
|
||||
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
|
||||
echo "Create etc snapshot -> .snapshots/etc-001"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-001
|
||||
|
||||
cd ${WORK_DIR}
|
||||
|
||||
echo "Copy efi files ..."
|
||||
fatlabel ${EFI_DEV} "EFI"
|
||||
cd /boot/efi
|
||||
mv ./EFI/BOOT/grub.cfg /tmp/grub.cfg.prev
|
||||
rm -rf *
|
||||
cp -a ${P1}/* .
|
||||
# 如果想启动上一个版本的openwrt,可以把 /boot/efi/EFI/BOOT/grub.cfg.prev 复制成 /boot/efi/EFI/BOOT/grub.cfg
|
||||
mv /tmp/grub.cfg.prev ./EFI/BOOT/
|
||||
|
||||
echo "Modify efi configuration ... "
|
||||
cd /boot/efi/EFI/BOOT
|
||||
cat >grub.cfg <<EOF
|
||||
echo "search fs_uuid ${NEW_ROOT_UUID} ..."
|
||||
search.fs_uuid ${NEW_ROOT_UUID} root
|
||||
echo "root=\$root"
|
||||
echo "set prefix ... "
|
||||
set prefix=(\$root)'/boot/grub2'
|
||||
echo "prefix=\$prefix"
|
||||
source \${prefix}/grub.cfg
|
||||
EOF
|
||||
|
||||
echo "Modify boot configuration ... "
|
||||
cd ${NEW_ROOT_MOUNTPOINT}/boot/grub2
|
||||
cat >grub.cfg <<EOF
|
||||
insmod gzio
|
||||
insmod part_gpt
|
||||
insmod zstd
|
||||
insmod btrfs
|
||||
terminal_input console
|
||||
terminal_output console
|
||||
|
||||
set default="0"
|
||||
set timeout=3
|
||||
|
||||
menuentry "OpenWrt" {
|
||||
echo 'Loading linux kernel ...'
|
||||
linux /boot/vmlinuz root=UUID=${NEW_ROOT_UUID} rootfstype=btrfs rootflags=compress=zstd:${ZSTD_LEVEL} console=ttyAMA0,115200n8 no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
|
||||
echo 'Loading initial ramdisk ...'
|
||||
initrd /boot/initrd.img
|
||||
}
|
||||
EOF
|
||||
|
||||
sync
|
||||
|
||||
cd $WORK_DIR
|
||||
umount -f ${P1} ${P2} 2>/dev/null
|
||||
losetup -D 2>/dev/null
|
||||
rm -rf ${P1} ${P2} 2>/dev/null
|
||||
rm -f ${IMG_NAME} 2>/dev/null
|
||||
rm -f sha256sums 2>/dev/null
|
||||
sync
|
||||
wait
|
||||
|
||||
echo "Successfully updated, automatic restarting..."
|
||||
sleep 3
|
||||
reboot
|
||||
exit 0
|
||||
@@ -0,0 +1,924 @@
|
||||
#!/bin/bash
|
||||
#======================================================================================
|
||||
# Function: Update openwrt to emmc for Rockchip STB
|
||||
# Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit
|
||||
# Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic
|
||||
#======================================================================================
|
||||
#
|
||||
# The script supports directly setting parameters for update, skipping interactive selection
|
||||
# openwrt-update-rockchip ${OPENWRT_FILE} ${AUTO_MAINLINE_UBOOT} ${RESTORE_CONFIG}
|
||||
# E.g: openwrt-update-rockchip openwrt_s905d.img.gz yes restore
|
||||
# E.g: openwrt-update-rockchip openwrt_s905d.img.gz no no-restore
|
||||
|
||||
# You can also execute the script directly, and interactively select related functions
|
||||
# E.g: openwrt-update-rockchip
|
||||
#
|
||||
#======================================================================================
|
||||
|
||||
# Encountered a serious error, abort the script execution
|
||||
error_msg() {
|
||||
echo -e "[ERROR] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get the partition name of the /boot file system
|
||||
get_boot_partition_name() {
|
||||
local paths="/boot"
|
||||
local partition_name
|
||||
|
||||
partition_name=$(df "${paths}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}')
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
echo "${partition_name}"
|
||||
}
|
||||
|
||||
# Get the partition message of the root file system
|
||||
get_root_partition_msg() {
|
||||
local paths=("/" "/overlay" "/rom")
|
||||
local partition_name
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
partition_msg=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ "^" "'"${path}"'" "$" {print $0}')
|
||||
[[ -n "${partition_msg}" ]] && break
|
||||
done
|
||||
|
||||
[[ -z "${partition_msg}" ]] && error_msg "Cannot find the root partition message!"
|
||||
echo "${partition_msg}"
|
||||
}
|
||||
|
||||
# Receive one-key command related parameters
|
||||
IMG_NAME="${1}"
|
||||
AUTO_MAINLINE_UBOOT="${2}"
|
||||
BACKUP_RESTORE_CONFIG="${3}"
|
||||
|
||||
# Current device model
|
||||
if [ -f /boot/armbianEnv.txt ]; then
|
||||
source /boot/armbianEnv.txt 2>/dev/null
|
||||
CURRENT_FDTFILE=$(basename ${fdtfile})
|
||||
fi
|
||||
MYDEVICE_NAME=$(cat /proc/device-tree/model | tr -d '\000')
|
||||
case $MYDEVICE_NAME in
|
||||
"")
|
||||
error_msg "The device name is empty and cannot be recognized."
|
||||
;;
|
||||
|
||||
"Chainedbox L1 Pro")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3328-l1pro-1296mhz.dtb"
|
||||
fi
|
||||
SOC="l1pro"
|
||||
;;
|
||||
"BeikeYun")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3328-beikeyun-1296mhz.dtb"
|
||||
fi
|
||||
SOC="beikeyun"
|
||||
;;
|
||||
"ZCuble1 Max")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3399-zcube1-max.dtb"
|
||||
fi
|
||||
SOC="zcube1 max"
|
||||
;;
|
||||
"JP TVbox 3566")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3566-jp-tvbox.dtb"
|
||||
fi
|
||||
SOC="jp-tvbox"
|
||||
;;
|
||||
"Radxa CM3 RPI CM4 IO")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3566-radxa-cm3-rpi-cm4-io.dtb"
|
||||
fi
|
||||
SOC="radxa-cm3-rpi-cm4-io"
|
||||
;;
|
||||
"FastRhino R66S" | "Lunzn FastRhino R66S")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-fastrhino-r66s.dtb"
|
||||
fi
|
||||
SOC="r66s"
|
||||
;;
|
||||
"FastRhino R68S" | "Lunzn FastRhino R68S")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-fastrhino-r68s.dtb"
|
||||
fi
|
||||
SOC="r68s"
|
||||
;;
|
||||
"RK3568 EC-X")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-ec-x.dtb"
|
||||
fi
|
||||
SOC="ec-x"
|
||||
;;
|
||||
"HINLINK OPC-H66K Board" | "Hlink H66K")
|
||||
if [ -n "${CURRENT_FDTFILE}" ] && [ "${CURRENT_FDTFILE}" != "rk3568-opc-h66k.dtb" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-hlink-h66k.dtb"
|
||||
fi
|
||||
SOC="h66k"
|
||||
;;
|
||||
"HINLINK OPC-H68K Board" | "Hlink H68K")
|
||||
if [ -n "${CURRENT_FDTFILE}" ] && [ "${CURRENT_FDTFILE}" != "rk3568-opc-h68k.dtb" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-hlink-h68k.dtb"
|
||||
fi
|
||||
SOC="h68k"
|
||||
;;
|
||||
"HINLINK OPC-H69K Board" | "Hlink H69K")
|
||||
if [ -n "${CURRENT_FDTFILE}" ] && [ "${CURRENT_FDTFILE}" != "rk3568-opc-h69k.dtb" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-hlink-h69k.dtb"
|
||||
fi
|
||||
SOC="h69k"
|
||||
;;
|
||||
"Radxa E25" | "Radxa E25 Carrier Board")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-radxa-e25.dtb"
|
||||
fi
|
||||
SOC="e25"
|
||||
;;
|
||||
"HINLINK OWL H88K-V3 Board" | "Hlink H88K-V3")
|
||||
if [ -n "${CURRENT_FDTFILE}" ] && [ "${CURRENT_FDTFILE}" != "rk3568-hinlink-h88k-v3.dtb" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588-hlink-h88k-v3.dtb"
|
||||
fi
|
||||
SOC="h88k-v3"
|
||||
;;
|
||||
"Hlink H88K V3.1")
|
||||
if [ -n "${CURRENT_FDTFILE}" ] && [ "${CURRENT_FDTFILE}" != "rk3568-hinlink-h88k-v31.dtb" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588-hlink-h88k-v31.dtb"
|
||||
fi
|
||||
SOC="h88k-v3"
|
||||
;;
|
||||
"HINLINK OWL H88K Board" | "Hlink H88K")
|
||||
if [ -n "${CURRENT_FDTFILE}" ] && [ "${CURRENT_FDTFILE}" != "rk3568-hinlink-h88k.dtb" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588-hlink-h88k.dtb"
|
||||
fi
|
||||
SOC="ak88/h88k"
|
||||
;;
|
||||
"Hlink H28K")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3528-hlink-h28k.dtb"
|
||||
fi
|
||||
SOC="h28k"
|
||||
;;
|
||||
"Hlink HT2")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3528-hlink-ht2.dtb"
|
||||
fi
|
||||
SOC="ht2"
|
||||
;;
|
||||
"XYLink XR3528")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3528-xylink-xr3528.dtb"
|
||||
fi
|
||||
SOC="xylink-xr3528"
|
||||
;;
|
||||
"Radxa E20C")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3528-radxa-e20c.dtb"
|
||||
fi
|
||||
SOC="e20c"
|
||||
;;
|
||||
"Radxa E24C")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3528-radxa-e24c.dtb"
|
||||
fi
|
||||
SOC="e24c"
|
||||
;;
|
||||
"YiXun Rs6Pro")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3528-yixun-rs6pro.dtb"
|
||||
fi
|
||||
SOC="rs6pro"
|
||||
;;
|
||||
"Ariaboard Photonicat")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-photonicat.dtb"
|
||||
fi
|
||||
SOC="photonicat"
|
||||
;;
|
||||
"NLnet Watermelon Pi V3")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-watermelon-pi-v3.dtb"
|
||||
fi
|
||||
SOC="watermelon-pi-v3"
|
||||
;;
|
||||
"NLnet Watermelon Pi")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3568-watermelon-pi.dtb"
|
||||
fi
|
||||
SOC="watermelon-pi"
|
||||
;;
|
||||
"Rockchip RK3576 DshanPi A1 Board")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3576-100ask-dshanpi-a1.dtb"
|
||||
fi
|
||||
SOC="100ask-dshanpi-a1"
|
||||
;;
|
||||
"Radxa ROCK 5B" | "Radxa ROCK 5 Model B")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588-rock-5b.dtb"
|
||||
fi
|
||||
SOC="rock5b"
|
||||
;;
|
||||
"Radxa ROCK 5C" | "Radxa ROCK 5 Model C")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588s-rock-5c.dtb"
|
||||
fi
|
||||
SOC="rock5c"
|
||||
;;
|
||||
"Radxa E52C")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588s-radxa-e52c.dtb"
|
||||
fi
|
||||
SOC="e52c"
|
||||
;;
|
||||
"Radxa E54C")
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="rk3588s-radxa-e54c.dtb"
|
||||
fi
|
||||
SOC="e54c"
|
||||
;;
|
||||
*) #default
|
||||
if [ -f "/etc/flippy-openwrt-release" ]; then
|
||||
source /etc/flippy-openwrt-release 2>/dev/null
|
||||
if [ -n "${CURRENT_FDTFILE}" ]; then
|
||||
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
|
||||
else
|
||||
MYDTB_FDTFILE="${FDTFILE}"
|
||||
fi
|
||||
SOC="${SOC}"
|
||||
else
|
||||
error_msg "Unknown device: [ ${MYDEVICE_NAME} ], Not supported."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ -z "${MYDTB_FDTFILE}" || -z "${SOC}" ]] && {
|
||||
error_msg "Invalid FDTFILE or SOC: [ ${MYDTB_FDTFILE} / ${SOC} ]"
|
||||
}
|
||||
|
||||
echo -e "Current device: ${MYDEVICE_NAME} [ ${SOC} ]"
|
||||
sleep 3
|
||||
|
||||
# Check and repair partition mount points
|
||||
[[ -z "$(get_boot_partition_name)" || -z "$(get_root_partition_name)" ]] && {
|
||||
echo -e "Partition mount point error, start repairing..."
|
||||
openwrt-backup -g
|
||||
}
|
||||
|
||||
# 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"
|
||||
LB_PRE="EMMC_"
|
||||
;;
|
||||
nvme?n?p?)
|
||||
EMMC_NAME=$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')
|
||||
PARTITION_NAME="p"
|
||||
LB_PRE="NVME_"
|
||||
;;
|
||||
[hsv]d[a-z][1-4])
|
||||
EMMC_NAME=$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')
|
||||
PARTITION_NAME=""
|
||||
LB_PRE="USB_"
|
||||
;;
|
||||
*)
|
||||
error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!"
|
||||
;;
|
||||
esac
|
||||
|
||||
cd /mnt/${EMMC_NAME}${PARTITION_NAME}4/
|
||||
mv -f /tmp/upload/* . 2>/dev/null && sync
|
||||
|
||||
if [[ "${IMG_NAME}" == *.img ]]; then
|
||||
echo -e "Update using [ ${IMG_NAME} ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
echo -e "Update using [ ${IMG_NAME} ] ] file. Please wait a moment ..."
|
||||
elif [ $(ls *.img.xz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
xz_file=$(ls *.img.xz | head -n 1)
|
||||
echo -e "Update using [ ${xz_file} ] file. Please wait a moment ..."
|
||||
xz -d "${xz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.img.gz -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.img.gz | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
gzip -df "${gz_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.7z -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
gz_file=$(ls *.7z | head -n 1)
|
||||
echo -e "Update using [ ${gz_file} ] file. Please wait a moment ..."
|
||||
bsdtar -xmf "${gz_file}" 2>/dev/null
|
||||
[ $? -eq 0 ] || 7z x "${gz_file}" -aoa -y 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
elif [ $(ls *.zip -l 2>/dev/null | grep "^-" | wc -l) -ge 1 ]; then
|
||||
zip_file=$(ls *.zip | head -n 1)
|
||||
echo -e "Update using [ ${zip_file} ] file. Please wait a moment ..."
|
||||
unzip -o "${zip_file}" 2>/dev/null
|
||||
IMG_NAME=$(ls *.img | head -n 1)
|
||||
else
|
||||
echo -e "Please upload or specify the update openwrt firmware file."
|
||||
echo -e "Upload method: system menu → Amlogic Service → Manually Upload Update"
|
||||
echo -e "Specify method: Place the openwrt firmware file in [ /mnt/${EMMC_NAME}${PARTITION_NAME}4/ ]"
|
||||
echo -e "The supported file suffixes are: *.img, *.img.xz, *.img.gz, *.7z, *.zip"
|
||||
echo -e "After upload the openwrt firmware file, run again."
|
||||
exit 1
|
||||
fi
|
||||
sync
|
||||
|
||||
# check file
|
||||
if [ ! -f "${IMG_NAME}" ]; then
|
||||
error_msg "No update file found."
|
||||
else
|
||||
echo "Start update from [ ${IMG_NAME} ]"
|
||||
fi
|
||||
|
||||
# find boot partition
|
||||
BOOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | awk '$3~/^part$/ && $5 ~ /^\/boot$/ {print $0}')
|
||||
if [ "${BOOT_PART_MSG}" == "" ]; then
|
||||
error_msg "The boot partition is not exists or not mounted, so it cannot be upgraded with this script!"
|
||||
fi
|
||||
|
||||
BR_FLAG=1
|
||||
echo -ne "Whether to backup and restore the current config files? y/n [y]\b\b"
|
||||
if [[ ${BACKUP_RESTORE_CONFIG} == "restore" ]]; then
|
||||
yn="y"
|
||||
elif [[ ${BACKUP_RESTORE_CONFIG} == "no-restore" ]]; then
|
||||
yn="n"
|
||||
else
|
||||
read yn
|
||||
fi
|
||||
case ${yn} in
|
||||
n* | N*)
|
||||
BR_FLAG=0
|
||||
;;
|
||||
esac
|
||||
|
||||
BOOT_NAME=$(echo ${BOOT_PART_MSG} | awk '{print $1}')
|
||||
BOOT_PATH=$(echo ${BOOT_PART_MSG} | awk '{print $2}')
|
||||
BOOT_UUID=$(echo ${BOOT_PART_MSG} | awk '{print $4}')
|
||||
|
||||
# find root partition
|
||||
ROOT_PART_MSG="$(get_root_partition_msg)"
|
||||
ROOT_NAME=$(echo ${ROOT_PART_MSG} | awk '{print $1}')
|
||||
ROOT_PATH=$(echo ${ROOT_PART_MSG} | awk '{print $2}')
|
||||
ROOT_UUID=$(echo ${ROOT_PART_MSG} | awk '{print $4}')
|
||||
case ${ROOT_NAME} in
|
||||
${EMMC_NAME}${PARTITION_NAME}2)
|
||||
NEW_ROOT_NAME="${EMMC_NAME}${PARTITION_NAME}3"
|
||||
NEW_ROOT_LABEL="${LB_PRE}ROOTFS2"
|
||||
;;
|
||||
${EMMC_NAME}${PARTITION_NAME}3)
|
||||
NEW_ROOT_NAME="${EMMC_NAME}${PARTITION_NAME}2"
|
||||
NEW_ROOT_LABEL="${LB_PRE}ROOTFS1"
|
||||
;;
|
||||
*)
|
||||
error_msg "The root partition location is invalid, so it cannot be upgraded with this script!"
|
||||
;;
|
||||
esac
|
||||
|
||||
# find new root partition
|
||||
NEW_ROOT_PART_MSG=$(lsblk -l -o NAME,PATH,TYPE,UUID,MOUNTPOINT | grep "${NEW_ROOT_NAME}" | awk '$3 ~ /^part$/ && $5 !~ /^\/$/ && $5 !~ /^\/boot$/ {print $0}')
|
||||
if [ "${NEW_ROOT_PART_MSG}" == "" ]; then
|
||||
error_msg "The new root partition is not exists, so it cannot be upgraded with this script!"
|
||||
fi
|
||||
NEW_ROOT_NAME=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $1}')
|
||||
NEW_ROOT_PATH=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $2}')
|
||||
NEW_ROOT_UUID=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $4}')
|
||||
NEW_ROOT_MP=$(echo ${NEW_ROOT_PART_MSG} | awk '{print $5}')
|
||||
|
||||
# losetup
|
||||
losetup -f -P ${IMG_NAME}
|
||||
if [[ "${?}" -eq "0" ]]; then
|
||||
LOOP_DEV=$(losetup | grep "${IMG_NAME}" | awk '{print $1}')
|
||||
if [ "$LOOP_DEV" == "" ]; then
|
||||
error_msg "loop device not found!"
|
||||
fi
|
||||
else
|
||||
error_msg "losetup ${IMG_FILE} failed!"
|
||||
fi
|
||||
|
||||
# fix loopdev issue in kernel 5.19
|
||||
function fix_loopdev() {
|
||||
local parentdev=${1##*/}
|
||||
if [ ! -d /sys/block/${parentdev} ]; then
|
||||
return
|
||||
fi
|
||||
subdevs=$(lsblk -l -o NAME | grep -E "^${parentdev}.+\$")
|
||||
for subdev in ${subdevs}; do
|
||||
if [ ! -d /sys/block/${parentdev}/${subdev} ]; then
|
||||
return
|
||||
elif [ -b /dev/${sub_dev} ]; then
|
||||
continue
|
||||
fi
|
||||
source /sys/block/${parentdev}/${subdev}/uevent
|
||||
mknod /dev/${subdev} b ${MAJOR} ${MINOR}
|
||||
unset MAJOR MINOR DEVNAME DEVTYPE DISKSEQ PARTN PARTNAME
|
||||
done
|
||||
}
|
||||
fix_loopdev ${LOOP_DEV}
|
||||
|
||||
WAIT=3
|
||||
echo -n "The loopdev is ${LOOP_DEV}, wait ${WAIT} seconds "
|
||||
while [ ${WAIT} -ge 1 ]; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
WAIT=$((WAIT - 1))
|
||||
done
|
||||
echo
|
||||
|
||||
# umount loop devices (openwrt will auto mount some partition)
|
||||
MOUNTED_DEVS=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "${LOOP_DEV}" | awk '$3 !~ /^$/ {print $2}')
|
||||
for dev in ${MOUNTED_DEVS}; do
|
||||
while :; do
|
||||
echo -n "umount ${dev} ... "
|
||||
umount -f ${dev}
|
||||
sleep 1
|
||||
mnt=$(lsblk -l -o NAME,PATH,MOUNTPOINT | grep "${dev}" | awk '$3 !~ /^$/ {print $2}')
|
||||
if [ "${mnt}" == "" ]; then
|
||||
echo "ok"
|
||||
break
|
||||
else
|
||||
echo "try again ..."
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# mount src part
|
||||
WORK_DIR=${PWD}
|
||||
P1=${WORK_DIR}/boot
|
||||
P2=${WORK_DIR}/root
|
||||
mkdir -p $P1 $P2
|
||||
echo -n "mount ${LOOP_DEV}p1 -> ${P1} ... "
|
||||
mount -t ext4 -o ro ${LOOP_DEV}p1 ${P1}
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
echo "mount failed"
|
||||
losetup -D
|
||||
exit 1
|
||||
else
|
||||
echo "ok"
|
||||
fi
|
||||
|
||||
echo -n "mount ${LOOP_DEV}p2 -> ${P2} ... "
|
||||
ZSTD_LEVEL=6
|
||||
mount -t btrfs -o ro,compress=zstd:${ZSTD_LEVEL} ${LOOP_DEV}p2 ${P2}
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
echo "mount failed"
|
||||
umount -f ${P1}
|
||||
losetup -D
|
||||
exit 1
|
||||
else
|
||||
echo "ok"
|
||||
fi
|
||||
|
||||
# Prepare the dockerman config file
|
||||
if [ -f ${P2}/etc/init.d/dockerman ] && [ -f ${P2}/etc/config/dockerd ]; then
|
||||
|
||||
flg=0
|
||||
# get current docker data root
|
||||
data_root=$(uci get dockerd.globals.data_root 2>/dev/null)
|
||||
if [ "${data_root}" == "" ]; then
|
||||
flg=1
|
||||
# get current config from /etc/docker/daemon.json
|
||||
if [ -f "/etc/docker/daemon.json" ] && [ -x "/usr/bin/jq" ]; then
|
||||
data_root=$(jq -r '."data-root"' /etc/docker/daemon.json)
|
||||
|
||||
bip=$(jq -r '."bip"' /etc/docker/daemon.json)
|
||||
[ "${bip}" == "null" ] && bip="172.31.0.1/24"
|
||||
|
||||
log_level=$(jq -r '."log-level"' /etc/docker/daemon.json)
|
||||
[ "${log_level}" == "null" ] && log_level="warn"
|
||||
|
||||
_iptables=$(jq -r '."iptables"' /etc/docker/daemon.json)
|
||||
[ "${_iptables}" == "null" ] && _iptables="true"
|
||||
|
||||
registry_mirrors=$(jq -r '."registry-mirrors"[]' /etc/docker/daemon.json 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${data_root}" == "" ]; then
|
||||
data_root="/opt/docker/" # the default data root
|
||||
fi
|
||||
|
||||
if ! uci get dockerd.globals >/dev/null 2>&1; then
|
||||
uci set dockerd.globals='globals'
|
||||
uci commit
|
||||
fi
|
||||
|
||||
# delete alter config , use inner config
|
||||
if uci get dockerd.globals.alt_config_file >/dev/null 2>&1; then
|
||||
uci delete dockerd.globals.alt_config_file
|
||||
uci commit
|
||||
fi
|
||||
|
||||
if [[ "${flg}" -eq "1" ]]; then
|
||||
uci set dockerd.globals.data_root=${data_root}
|
||||
[ "${bip}" != "" ] && uci set dockerd.globals.bip=${bip}
|
||||
[ "${log_level}" != "" ] && uci set dockerd.globals.log_level=${log_level}
|
||||
[ "${_iptables}" != "" ] && uci set dockerd.globals.iptables=${_iptables}
|
||||
if [ "${registry_mirrors}" != "" ]; then
|
||||
for reg in ${registry_mirrors}; do
|
||||
uci add_list dockerd.globals.registry_mirrors=${reg}
|
||||
done
|
||||
fi
|
||||
uci set dockerd.globals.auto_start='1'
|
||||
uci commit
|
||||
fi
|
||||
fi
|
||||
|
||||
#format NEW_ROOT
|
||||
echo "umount ${NEW_ROOT_MP}"
|
||||
umount -f "${NEW_ROOT_MP}"
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
echo "umount failed, please reboot and try again!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "format ${NEW_ROOT_PATH}"
|
||||
NEW_ROOT_UUID=$(uuidgen)
|
||||
mkfs.btrfs -f -U ${NEW_ROOT_UUID} -L ${NEW_ROOT_LABEL} -m single ${NEW_ROOT_PATH}
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
echo "format ${NEW_ROOT_PATH} failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "mount ${NEW_ROOT_PATH} to ${NEW_ROOT_MP}"
|
||||
mount -t btrfs -o compress=zstd:${ZSTD_LEVEL} ${NEW_ROOT_PATH} ${NEW_ROOT_MP}
|
||||
if [[ "${?}" -ne "0" ]]; then
|
||||
echo "mount ${NEW_ROOT_PATH} to ${NEW_ROOT_MP} failed!"
|
||||
umount -f ${P1}
|
||||
umount -f ${P2}
|
||||
losetup -D
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# begin copy rootfs
|
||||
cd ${NEW_ROOT_MP}
|
||||
echo "Start copy data from ${P2} to ${NEW_ROOT_MP} ..."
|
||||
ENTRYS=$(ls)
|
||||
for entry in ${ENTRYS}; do
|
||||
if [ "${entry}" == "lost+found" ]; then
|
||||
continue
|
||||
fi
|
||||
echo -n "remove old ${entry} ... "
|
||||
rm -rf ${entry}
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "failed"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo
|
||||
|
||||
echo "create etc subvolume ..."
|
||||
btrfs subvolume create etc
|
||||
echo -n "make dirs ... "
|
||||
mkdir -p .snapshots .reserved bin boot dev lib opt mnt overlay proc rom root run sbin sys tmp usr www
|
||||
ln -sf lib/ lib64
|
||||
ln -sf tmp/ var
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
COPY_SRC="root etc bin sbin lib opt usr www"
|
||||
echo "copy data ... "
|
||||
for src in ${COPY_SRC}; do
|
||||
echo -n "copy ${src} ... "
|
||||
(cd ${P2} && tar cf - ${src}) | tar xf -
|
||||
sync
|
||||
echo "done"
|
||||
done
|
||||
|
||||
SHFS="/mnt/${EMMC_NAME}${PARTITION_NAME}4"
|
||||
echo "Modify config files ... "
|
||||
if [ -x ./usr/sbin/balethirq.pl ]; then
|
||||
if grep "balethirq.pl" "./etc/rc.local"; then
|
||||
echo "balance irq is enabled"
|
||||
else
|
||||
echo "enable balance irq"
|
||||
sed -e "/exit/i\/usr/sbin/balethirq.pl" -i ./etc/rc.local
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f ./etc/bench.log
|
||||
cat >./etc/fstab <<EOF
|
||||
UUID=${NEW_ROOT_UUID} / btrfs compress=zstd:${ZSTD_LEVEL} 0 1
|
||||
UUID=${BOOT_UUID} /boot ext4 defaults 0 2
|
||||
#tmpfs /tmp tmpfs defaults,nosuid 0 0
|
||||
EOF
|
||||
|
||||
cat >./etc/config/fstab <<EOF
|
||||
config global
|
||||
option anon_swap '0'
|
||||
option anon_mount '1'
|
||||
option auto_swap '0'
|
||||
option auto_mount '1'
|
||||
option delay_root '5'
|
||||
option check_fs '0'
|
||||
|
||||
config mount
|
||||
option target '/rom'
|
||||
option uuid '${NEW_ROOT_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '1'
|
||||
option fstype 'btrfs'
|
||||
option options 'compress=zstd:${ZSTD_LEVEL}'
|
||||
|
||||
config mount
|
||||
option target '/boot'
|
||||
option uuid '${BOOT_UUID}'
|
||||
option enabled '1'
|
||||
option enabled_fsck '0'
|
||||
option fstype 'ext4'
|
||||
|
||||
EOF
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
sync
|
||||
|
||||
echo "create the first etc snapshot -> .snapshots/etc-000"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-000
|
||||
|
||||
[ -d ${SHFS}/docker ] || mkdir -p ${SHFS}/docker
|
||||
rm -rf opt/docker && ln -sf ${SHFS}/docker/ opt/docker
|
||||
|
||||
if [ -f /mnt/${NEW_ROOT_NAME}/etc/config/AdGuardHome ]; then
|
||||
[ -d ${SHFS}/AdGuardHome/data ] || mkdir -p ${SHFS}/AdGuardHome/data
|
||||
if [ ! -L /usr/bin/AdGuardHome ]; then
|
||||
[ -d /usr/bin/AdGuardHome ] &&
|
||||
cp -a /usr/bin/AdGuardHome/* ${SHFS}/AdGuardHome/
|
||||
fi
|
||||
ln -sf ${SHFS}/AdGuardHome /mnt/${NEW_ROOT_NAME}/usr/bin/AdGuardHome
|
||||
fi
|
||||
|
||||
sync
|
||||
echo "copy done"
|
||||
echo
|
||||
|
||||
BACKUP_LIST=$(${P2}/usr/sbin/openwrt-backup -p)
|
||||
if [ $BR_FLAG -eq 1 ]; then
|
||||
echo -n "Restore your old config files ... "
|
||||
(
|
||||
cd /
|
||||
eval tar czf ${NEW_ROOT_MP}/.reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
)
|
||||
tar xzf ${NEW_ROOT_MP}/.reserved/openwrt_config.tar.gz
|
||||
[ -f ./etc/config/dockerman ] && sed -e "s/option wan_mode 'false'/option wan_mode 'true'/" -i ./etc/config/dockerman 2>/dev/null
|
||||
[ -f ./etc/config/dockerd ] && sed -e "s/option wan_mode '0'/option wan_mode '1'/" -i ./etc/config/dockerd 2>/dev/null
|
||||
[ -f ./etc/config/verysync ] && sed -e 's/config setting/config verysync/' -i ./etc/config/verysync
|
||||
|
||||
# 还原 fstab
|
||||
cp -f .snapshots/etc-000/fstab ./etc/fstab
|
||||
cp -f .snapshots/etc-000/config/fstab ./etc/config/fstab
|
||||
# 还原 luci
|
||||
cp -f .snapshots/etc-000/config/luci ./etc/config/luci
|
||||
# 还原/etc/config/rpcd
|
||||
cp -f .snapshots/etc-000/config/rpcd ./etc/config/rpcd
|
||||
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
fi
|
||||
|
||||
cat >>./etc/crontabs/root <<EOF
|
||||
17 3 * * * /etc/coremark.sh
|
||||
EOF
|
||||
|
||||
sed -e 's/ttyAMA0/ttyS2/' -i ./etc/inittab
|
||||
|
||||
[ "${SOC}" != "h28k" ] && \
|
||||
[ "${SOC}" != "h29k" ] && \
|
||||
[ "${SOC}" != "ht2" ] && \
|
||||
[ "${SOC}" != "e20c" ] && \
|
||||
[ "${SOC}" != "e24c" ] && \
|
||||
sed -e 's/ttyS0/tty1/' -i ./etc/inittab
|
||||
|
||||
sss=$(date +%s)
|
||||
ddd=$((sss / 86400))
|
||||
sed -e "s/:0:0:99999:7:::/:${ddd}:0:99999:7:::/" -i ./etc/shadow
|
||||
# 修复amule每次升级后重复添加条目的问题
|
||||
sed -e "/amule:x:/d" -i ./etc/shadow
|
||||
# 修复dropbear每次升级后重复添加sshd条目的问题
|
||||
sed -e "/sshd:x:/d" -i ./etc/shadow
|
||||
if ! grep "sshd:x:22:sshd" ./etc/group >/dev/null; then
|
||||
echo "sshd:x:22:sshd" >>./etc/group
|
||||
fi
|
||||
if ! grep "sshd:x:22:22:sshd:" ./etc/passwd >/dev/null; then
|
||||
echo "sshd:x:22:22:sshd:/var/run/sshd:/bin/false" >>./etc/passwd
|
||||
fi
|
||||
if ! grep "sshd:x:" ./etc/shadow >/dev/null; then
|
||||
echo "sshd:x:${ddd}:0:99999:7:::" >>./etc/shadow
|
||||
fi
|
||||
|
||||
if [ $BR_FLAG -eq 1 ]; then
|
||||
if [ -x ./bin/bash ] && [ -f ./etc/profile.d/30-sysinfo.sh ]; then
|
||||
sed -e 's/\/bin\/ash/\/bin\/bash/' -i ./etc/passwd
|
||||
fi
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
fi
|
||||
sed -e "s/option hw_flow '1'/option hw_flow '0'/" -i ./etc/config/turboacc
|
||||
(
|
||||
cd etc/rc.d
|
||||
rm -f S??shortcut-fe
|
||||
if grep "sfe_flow '1'" ../config/turboacc >/dev/null; then
|
||||
if find ../../lib/modules -name 'shortcut-fe-cm.ko'; then
|
||||
ln -sf ../init.d/shortcut-fe S99shortcut-fe
|
||||
fi
|
||||
fi
|
||||
)
|
||||
|
||||
# move /etc/config/balance_irq to /etc/balance_irq
|
||||
[ -f "./etc/config/balance_irq" ] && mv ./etc/config/balance_irq ./etc/
|
||||
|
||||
rm -f "./etc/rc.local.orig" "./etc/first_run.sh" "./etc/part_size"
|
||||
|
||||
eval tar czf .reserved/openwrt_config.tar.gz "${BACKUP_LIST}" 2>/dev/null
|
||||
sync
|
||||
|
||||
mv ./etc/rc.local ./etc/rc.local.orig
|
||||
cat >"./etc/rc.local" <<EOF
|
||||
if ! ls /etc/rc.d/S??dockerd >/dev/null 2>&1;then
|
||||
/etc/init.d/dockerd enable
|
||||
/etc/init.d/dockerd start
|
||||
fi
|
||||
if ! ls /etc/rc.d/S??dockerman >/dev/null 2>&1 && [ -f /etc/init.d/dockerman ];then
|
||||
/etc/init.d/dockerman enable
|
||||
/etc/init.d/dockerman start
|
||||
fi
|
||||
mv /etc/rc.local.orig /etc/rc.local
|
||||
chmod 755 /etc/rc.local
|
||||
exec /etc/rc.local
|
||||
exit
|
||||
EOF
|
||||
chmod 755 ./etc/rc.local*
|
||||
|
||||
echo "create the second etc snapshot -> .snapshots/etc-001"
|
||||
btrfs subvolume snapshot -r etc .snapshots/etc-001
|
||||
|
||||
# 2021.04.01添加
|
||||
# 强制锁定fstab,防止用户擅自修改挂载点
|
||||
# 开启了快照功能之后,不再需要锁定fstab
|
||||
#chattr +ia ./etc/config/fstab
|
||||
|
||||
cd ${WORK_DIR}
|
||||
|
||||
echo "Start copy data from ${P2} to /boot ..."
|
||||
cd /boot
|
||||
echo -n "backup armbianEnv.txt ..."
|
||||
cp armbianEnv.txt /tmp/
|
||||
echo -n "backup current dtb file ..."
|
||||
cp -v dtb/rockchip/${CURRENT_FDTFILE} /tmp/
|
||||
echo -n "remove old boot files ..."
|
||||
rm -rf *
|
||||
echo "done"
|
||||
echo -n "copy new boot files ... "
|
||||
(cd ${P1} && tar cf - .) | tar xf -
|
||||
sync
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
echo -n "Update boot args ... "
|
||||
|
||||
if [ -f /tmp/armbianEnv.txt ]; then
|
||||
ORIG_OVERLAYS=$(grep '^overlays=' /tmp/armbianEnv.txt)
|
||||
ORIG_USER_OVERLAYS=$(grep '^user_overlays=' /tmp/armbianEnv.txt)
|
||||
sed -e '/user_overlays=/d' -i armbianEnv.txt
|
||||
sed -e '/overlays=/d' -i armbianEnv.txt
|
||||
echo "$ORIG_OVERLAYS" >>armbianEnv.txt
|
||||
echo "$ORIG_USER_OVERLAYS" >>armbianEnv.txt
|
||||
fi
|
||||
|
||||
sed -e '/fdtfile=/d' -i armbianEnv.txt
|
||||
sed -e '/rootdev=/d' -i armbianEnv.txt
|
||||
sed -e '/rootfstype=/d' -i armbianEnv.txt
|
||||
sed -e '/rootflags=/d' -i armbianEnv.txt
|
||||
case $SOC in
|
||||
l1pro | beikeyun)
|
||||
echo "fdtfile=/dtb/rockchip/${MYDTB_FDTFILE}" >>armbianEnv.txt
|
||||
;;
|
||||
*)
|
||||
echo "fdtfile=rockchip/${MYDTB_FDTFILE}" >>armbianEnv.txt
|
||||
;;
|
||||
esac
|
||||
cat >>armbianEnv.txt <<EOF
|
||||
rootdev=UUID=${NEW_ROOT_UUID}
|
||||
rootfstype=btrfs
|
||||
rootflags=compress=zstd:${ZSTD_LEVEL}
|
||||
EOF
|
||||
|
||||
# Replace the UUID for extlinux/extlinux.conf if it exists
|
||||
[[ -f "extlinux/extlinux.conf" ]] && {
|
||||
sed -i -E "s|UUID=[^ ]*|UUID=${NEW_ROOT_UUID}|" extlinux/extlinux.conf 2>/dev/null
|
||||
}
|
||||
|
||||
# 如果新的dtb文件不存在,则用旧的代替
|
||||
if [ ! -f "dtb/rockchip/${MYDTB_FDTFILE}" ]; then
|
||||
echo "The new dtb file does not exist, replace it with the old one."
|
||||
cp -v /tmp/${CURRENT_FDTFILE} dtb/rockchip/${MYDTB_FDTFILE}
|
||||
fi
|
||||
sync
|
||||
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
cd $WORK_DIR
|
||||
umount -f ${P1} ${P2} 2>/dev/null
|
||||
losetup -D 2>/dev/null
|
||||
rmdir ${P1} ${P2} 2>/dev/null
|
||||
rm -f ${IMG_NAME} 2>/dev/null
|
||||
rm -f sha256sums 2>/dev/null
|
||||
sync
|
||||
|
||||
echo "Successfully updated, automatic restarting..."
|
||||
sleep 3
|
||||
reboot
|
||||
exit 0
|
||||
@@ -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 };
|
||||