From 569f523ce6ed350cfff6897fdedf2f86e977e73c Mon Sep 17 00:00:00 2001 From: actions-user Date: Wed, 25 Dec 2024 23:13:18 +0800 Subject: [PATCH] update 2024-12-25 23:13:18 --- luci-app-ota/Makefile | 13 ++ luci-app-ota/luasrc/controller/admin/ota.lua | 175 ++++++++++++++ luci-app-ota/luasrc/view/admin_system/ota.htm | 218 ++++++++++++++++++ .../luasrc/view/admin_system/ota_flashing.htm | 111 +++++++++ luci-app-ota/po/zh-cn | 1 + luci-app-ota/po/zh_Hans/ota.po | 53 +++++ luci-app-ota/root/bin/ota | 156 +++++++++++++ luci-app-ota/root/etc/config/ota | 3 + .../usr/share/rpcd/acl.d/luci-app-ota.json | 11 + 9 files changed, 741 insertions(+) create mode 100755 luci-app-ota/Makefile create mode 100644 luci-app-ota/luasrc/controller/admin/ota.lua create mode 100644 luci-app-ota/luasrc/view/admin_system/ota.htm create mode 100644 luci-app-ota/luasrc/view/admin_system/ota_flashing.htm create mode 120000 luci-app-ota/po/zh-cn create mode 100644 luci-app-ota/po/zh_Hans/ota.po create mode 100755 luci-app-ota/root/bin/ota create mode 100644 luci-app-ota/root/etc/config/ota create mode 100644 luci-app-ota/root/usr/share/rpcd/acl.d/luci-app-ota.json diff --git a/luci-app-ota/Makefile b/luci-app-ota/Makefile new file mode 100755 index 00000000..aa797b66 --- /dev/null +++ b/luci-app-ota/Makefile @@ -0,0 +1,13 @@ + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI for OTA upgrade +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+fw_download_tool +jsonfilter +PKG_VERSION:=1.2 +PKG_RELEASE:=1 +PKG_MAINTAINER:=jjm2473 + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-ota/luasrc/controller/admin/ota.lua b/luci-app-ota/luasrc/controller/admin/ota.lua new file mode 100644 index 00000000..01e2e030 --- /dev/null +++ b/luci-app-ota/luasrc/controller/admin/ota.lua @@ -0,0 +1,175 @@ +--[[ +LuCI - Lua Configuration Interface +Copyright 2021 jjm2473 +]]-- + +require "luci.util" +module("luci.controller.admin.ota",package.seeall) + +function index() + if nixio.fs.access("/rom/bin/ota") then + entry({"admin", "system", "ota"}, call("action_ota"), _("OTA"), 69) + entry({"admin", "system", "ota", "check"}, post("action_check")) + entry({"admin", "system", "ota", "download"}, post("action_download")) + entry({"admin", "system", "ota", "progress"}, call("action_progress")) + entry({"admin", "system", "ota", "cancel"}, post("action_cancel")) + end +end + +local function ota_exec(cmd) + local nixio = require "nixio" + local os = require "os" + local fs = require "nixio.fs" + local rshift = nixio.bit.rshift + + local oflags = nixio.open_flags("wronly", "creat") + local lock, code, msg = nixio.open("/var/lock/ota_api.lock", oflags) + if not lock then + return 255, "", "Open stdio lock failed: " .. msg + end + + -- Acquire lock + local stat, code, msg = lock:lock("tlock") + if not stat then + lock:close() + return 255, "", "Lock stdio failed: " .. msg + end + + local r = os.execute(cmd .. " >/var/log/ota.stdout 2>/var/log/ota.stderr") + local e = fs.readfile("/var/log/ota.stderr") + local o = fs.readfile("/var/log/ota.stdout") + + fs.unlink("/var/log/ota.stderr") + fs.unlink("/var/log/ota.stdout") + + lock:lock("ulock") + lock:close() + + e = e or "" + if r == 256 and e == "" then + e = "os.execute failed, is /var/log full or not existed?" + end + return rshift(r, 8), o or "", e or "" +end + +local function image_supported(image) + return (os.execute("sysupgrade -T %q >/dev/null" % image) == 0) +end + +function action_ota() + local image_tmp = "/tmp/firmware.img" + local http = require "luci.http" + if http.formvalue("apply") == "1" then + if not image_supported(image_tmp) then + luci.template.render("admin_system/ota", {image_invalid = true}) + return + end + local keep = (http.formvalue("keep") == "1") and "" or "-n" + luci.template.render("admin_system/ota_flashing", { + title = luci.i18n.translate("Flashing…"), + msg = luci.i18n.translate("The system is flashing now.
DO NOT POWER OFF THE DEVICE!
Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings."), + addr = (#keep > 0) and "10.0.0.1" or nil + }) + fork_exec("sleep 1; killall dropbear uhttpd nginx; sleep 1; sync; /sbin/sysupgrade %s %q" %{ keep, image_tmp }) + else + luci.template.render("admin_system/ota") + end +end + +function action_check() + local r,o,e = ota_exec("ota check") + local ret = { + code = 500, + msg = "Unknown" + } + if r == 0 then + ret.code = 0 + ret.msg = o + elseif r == 1 then + ret.code = 1 + ret.msg = "Already the latest firmware" + else + ret.code = 500 + ret.msg = e + end + luci.http.prepare_content("application/json") + luci.http.write_json(ret) +end + +function action_download() + local r,o,e = ota_exec("ota download") + local ret = { + code = 500, + msg = "Unknown" + } + if r == 0 then + ret.code = 0 + ret.msg = "" + else + ret.code = 500 + ret.msg = e + end + luci.http.prepare_content("application/json") + luci.http.write_json(ret) +end + +function action_progress() + local r,o,e = ota_exec("ota progress") + local ret = { + code = 500, + msg = "Unknown" + } + if r == 0 then + ret.code = 0 + ret.msg = "done" + elseif r == 1 or r == 2 then + ret.code = r + ret.msg = o + else + ret.code = 500 + ret.msg = e + end + luci.http.prepare_content("application/json") + luci.http.write_json(ret) +end + +function action_cancel() + local r,o,e = ota_exec("ota cancel") + local ret = { + code = 500, + msg = "Unknown" + } + if r == 0 then + ret.code = 0 + ret.msg = "ok" + else + ret.code = 500 + ret.msg = e + end + luci.http.prepare_content("application/json") + luci.http.write_json(ret) +end + +function fork_exec(command) + local pid = nixio.fork() + if pid > 0 then + return + elseif pid == 0 then + -- change to root dir + nixio.chdir("/") + + -- patch stdin, out, err to /dev/null + local null = nixio.open("/dev/null", "w+") + if null then + nixio.dup(null, nixio.stderr) + nixio.dup(null, nixio.stdout) + nixio.dup(null, nixio.stdin) + if null:fileno() > 2 then + null:close() + end + end + + -- replace with target command + nixio.exec("/bin/sh", "-c", command) + end +end diff --git a/luci-app-ota/luasrc/view/admin_system/ota.htm b/luci-app-ota/luasrc/view/admin_system/ota.htm new file mode 100644 index 00000000..ee7dd730 --- /dev/null +++ b/luci-app-ota/luasrc/view/admin_system/ota.htm @@ -0,0 +1,218 @@ +<%# + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> + +<% + local fs = require "nixio.fs" +-%> + +

<%:OTA%>

+ + +
+
+ <%:Upgrade firmware On the Air%> +
<%:Check and upgrade firmware from the Internet%>
+ <% if image_invalid then %> +
<%:The image file does not contain a supported format. Maybe try again later.%>
+ <% end %> +
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ <% if fs.access("/usr/lib/lua/luci/controller/admin/system.lua") then %> +
+ + +
+ +
+ +
+
+
+ <% else %> +
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ <% end %> +
+
+
+
+

<%:The latest firmware%>:

+
+
+
+
+ + + +
+ +<%+footer%> diff --git a/luci-app-ota/luasrc/view/admin_system/ota_flashing.htm b/luci-app-ota/luasrc/view/admin_system/ota_flashing.htm new file mode 100644 index 00000000..cf516dfb --- /dev/null +++ b/luci-app-ota/luasrc/view/admin_system/ota_flashing.htm @@ -0,0 +1,111 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + + + + <%=luci.sys.hostname()%> - <%= title or translate("Rebooting...") %> + + + + + +
+
+ +
+

<%:System%> - <%= title or translate("Rebooting...") %>

+

+
+

<%= msg or translate("Changes applied.") %>

+
+
+ <%:Loading%> + <%:Waiting for device...%> +
+
+
+ + diff --git a/luci-app-ota/po/zh-cn b/luci-app-ota/po/zh-cn new file mode 120000 index 00000000..8d69574d --- /dev/null +++ b/luci-app-ota/po/zh-cn @@ -0,0 +1 @@ +zh_Hans \ No newline at end of file diff --git a/luci-app-ota/po/zh_Hans/ota.po b/luci-app-ota/po/zh_Hans/ota.po new file mode 100644 index 00000000..c330bca1 --- /dev/null +++ b/luci-app-ota/po/zh_Hans/ota.po @@ -0,0 +1,53 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "OTA" +msgstr "在线升级" + +msgid "Already the latest firmware" +msgstr "已经是最新固件" + +msgid "Check update" +msgstr "检查更新" + +msgid "Found new firmware" +msgstr "发现新固件" + +msgid "Download firmware" +msgstr "下载固件" + +msgid "Cancel download" +msgstr "取消下载" + +msgid "Firmware downloaded" +msgstr "固件已下载" + +msgid "Flash image..." +msgstr "刷写固件..." + +msgid "Checking..." +msgstr "检查中..." + +msgid "Check failed" +msgstr "检查失败" + +msgid "Downloading" +msgstr "下载中" + +msgid "Download canceled" +msgstr "下载已取消" + +msgid "Download failed" +msgstr "下载失败" + +msgid "Upgrade firmware On the Air" +msgstr "在线升级固件" + +msgid "Check and upgrade firmware from the Internet" +msgstr "通过网络检查和更新固件" + +msgid "The latest firmware" +msgstr "最新固件" + +msgid "The image file does not contain a supported format. Maybe try again later." +msgstr "镜像文件格式不支持或损坏。也许稍候再试试" diff --git a/luci-app-ota/root/bin/ota b/luci-app-ota/root/bin/ota new file mode 100755 index 00000000..c682ddc2 --- /dev/null +++ b/luci-app-ota/root/bin/ota @@ -0,0 +1,156 @@ +#!/bin/sh + +. /etc/os-release +. /lib/functions/uci-defaults.sh + +API=$(uci -q get ota.config.api_url) +WRLOCK=/var/lock/ota_background.lock + +# armsr/armv8 +[ "$OPENWRT_BOARD" = "armsr/armv8" ] && alias board_name="echo armsr,armv8" + +# x86_64 +[ $(uname -m) = "x86_64" ] && alias board_name="echo x86_64" + +action=${1} +shift + +sha256() { + sha256sum $1 | cut -d' ' -f1 +} + +download() { + read_json + if [ -f /tmp/firmware.img ]; then + echo "Checking existed firmware.img..." >> /tmp/firmware.img.progress + if [ "`sha256 /tmp/firmware.img`" = "$FW_SHA256SUM" ]; then + return 0; + else + echo "Check failed, redownload" >> /tmp/firmware.img.progress + rm -f /tmp/firmware.img + fi + fi + touch /tmp/firmware.img.progress + fw_download_tool "$FW_URL" -o /tmp/firmware.img.part -k -L -4 > /tmp/firmware.img.progress 2>&1 & + echo "$! $PPID" > /var/run/ota/download.pid + while true; do + progress=$(grep -c "100.00%" /tmp/firmware.img.progress) + if [ "$progress" -ge "1" ]; then + echo "Checking new firmware.img.part..." > /tmp/firmware.img.progress + break + fi + sleep 1 + done + if [ "`sha256 /tmp/firmware.img.part`" = "$FW_SHA256SUM" ]; then + mv /tmp/firmware.img.part /tmp/firmware.img && echo $FW_SHA256SUM > /tmp/firmware.img.sha256sum + rm -f /tmp/firmware.img.progress + return 0 + else + echo "Checksum failed!" >>/tmp/firmware.img.progress + sleep 1 + rm -rf /tmp/firmware.img.part + return 1 + fi +} + +lock_download() { + local lock="$WRLOCK" + exec 200>$lock + flock -n 200 || return + download + flock -u 200 +} + +# 0: found newer fw, 1: already newest fw, *: err +do_check() { + url_check=$(curl -I -o /dev/null -s -w %{http_code} "$API") + [ $url_check -ne 200 ] && exit 255 + curl -s "$API" > /var/run/ota/fw.json + read_json + NEW_VERSION=$(basename $FW_URL | awk -F- '{if (NF >= 3 && $3 ~ /^rc/) {print $2 "-" $3} else {print $2}}') + if [ "$BUILD_DATE" -lt "$FW_BUILD_DATE" ]; then + echo "

Model:  $(board_name)
Current Version:  $PRETTY_NAME
Build Date:  $(date "+%Y-%m-%d %H:%M:%S" -d "@$BUILD_DATE")

" + echo "

New Version:  OpenWrt $NEW_VERSION
Build Date:  $(date "+%Y-%m-%d %H:%M:%S" -d "@$FW_BUILD_DATE")

" + return 0 + elif [ "$BUILD_DATE" -ge "$FW_BUILD_DATE" ]; then + return 1 + else + return 255 + fi +} + +# async download +do_download(){ + [ ! -f "/var/run/ota/fw.json" ] && { echo "do check first" >&2 ; return 254; } + lock_download & + return 0 +} + +# 0: done, 1: downloading, 2: failed, *: err +do_progress() { + read_json + [ -f /tmp/firmware.img.sha256sum ] && [ "`cat /tmp/firmware.img.sha256sum`" = "$FW_SHA256SUM" ] && return 0 + [ -f /tmp/firmware.img.progress ] || { echo "download not in progress" >&2 ; return 254; } + [ -f /tmp/firmware.img.part ] && { cat /tmp/firmware.img.progress | tr '\r' '\n' | tail -n1; return 1; } + tail -1 /tmp/firmware.img.progress | grep -Fq 'Canceled!' && { echo "Canceled"; return 2; } + tail -1 /tmp/firmware.img.progress | grep -Fq 'Checksum failed!' && { echo "Checksum failed!"; return 254; } + grep -v '\r' /tmp/firmware.img.progress >&2 + return 1 +} + +do_cancel() { + if [ -f /var/run/ota/download.pid ]; then + local pid=`cat /var/run/ota/download.pid` + if [ -n "$pid" ]; then + kill -TERM $pid; + while kill -9 $pid >/dev/null 2>&1; do + if ! sleep 1; then + break + fi + done + rm -rf /tmp/firmware.img* /var/lock/ota_background.lock /var/lock/ota_api.lock /var/run/ota/download.pid + echo "" >> /tmp/firmware.img.progress + echo "Canceled!" >> /tmp/firmware.img.progress + fi + fi + return 0 +} + +read_json(){ + FW_BUILD_DATE=$(jsonfilter -i /var/run/ota/fw.json -e "@['$(board_name)'][0]['build_date']") + FW_SHA256SUM=$(jsonfilter -i /var/run/ota/fw.json -e "@['$(board_name)'][0]['sha256sum']") + FW_URL=$(jsonfilter -i /var/run/ota/fw.json -e "@['$(board_name)'][0]['url']") +} + +ota_init(){ + mkdir -p /var/run/ota >/dev/null 2>&1 || true +} + +usage() { + echo "usage: ota sub-command" + echo "where sub-command is one of:" + echo " check Check firmware upgrade" + echo " download Download latest firmware" + echo " progress Download progress" + echo " cancel Cancel download" +} + +ota_init || exit 255 + +case $action in + "check") + do_check + ;; + "download") + do_download + ;; + "progress") + do_progress + ;; + "cancel") + do_cancel + ;; + *) + usage + ;; +esac diff --git a/luci-app-ota/root/etc/config/ota b/luci-app-ota/root/etc/config/ota new file mode 100644 index 00000000..cbbfb866 --- /dev/null +++ b/luci-app-ota/root/etc/config/ota @@ -0,0 +1,3 @@ + +config ota 'config' + option api_url '' diff --git a/luci-app-ota/root/usr/share/rpcd/acl.d/luci-app-ota.json b/luci-app-ota/root/usr/share/rpcd/acl.d/luci-app-ota.json new file mode 100644 index 00000000..2c396543 --- /dev/null +++ b/luci-app-ota/root/usr/share/rpcd/acl.d/luci-app-ota.json @@ -0,0 +1,11 @@ +{ + "luci-app-ota": { + "description": "Grant UCI access for luci-app-ota", + "read": { + "uci": [ "ota" ] + }, + "write": { + "uci": [ "ota" ] + } + } +}