update 2024-12-25 23:13:18
This commit is contained in:
parent
802fb67ddc
commit
569f523ce6
13
luci-app-ota/Makefile
Executable file
13
luci-app-ota/Makefile
Executable file
@ -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 <jjm2473@gmail.com>
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
175
luci-app-ota/luasrc/controller/admin/ota.lua
Normal file
175
luci-app-ota/luasrc/controller/admin/ota.lua
Normal file
@ -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.<br /> DO NOT POWER OFF THE DEVICE!<br /> 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
|
218
luci-app-ota/luasrc/view/admin_system/ota.htm
Normal file
218
luci-app-ota/luasrc/view/admin_system/ota.htm
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<%#
|
||||||
|
Licensed to the public under the Apache License 2.0.
|
||||||
|
-%>
|
||||||
|
|
||||||
|
<%+header%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
-%>
|
||||||
|
|
||||||
|
<h2 name="content"><%:OTA%></h2>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.state-ctl .state {
|
||||||
|
display: none;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-ctl.state-ctl-unchecked .state.state-unchecked,
|
||||||
|
.state-ctl.state-ctl-checked .state.state-checked,
|
||||||
|
.state-ctl.state-ctl-downloading .state.state-downloading,
|
||||||
|
.state-ctl.state-ctl-downloaded .state.state-downloaded
|
||||||
|
{
|
||||||
|
display: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<fieldset class="cbi-section">
|
||||||
|
<fieldset class="cbi-section state-ctl state-ctl-unchecked">
|
||||||
|
<legend><%:Upgrade firmware On the Air%></legend>
|
||||||
|
<div class="cbi-section-descr"><%:Check and upgrade firmware from the Internet%></div>
|
||||||
|
<% if image_invalid then %>
|
||||||
|
<div class="cbi-section-error"><%:The image file does not contain a supported format. Maybe try again later.%></div>
|
||||||
|
<% end %>
|
||||||
|
<div class="cbi-section-node">
|
||||||
|
<div class="state state-unchecked">
|
||||||
|
<form>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title" id="check_result"></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input class="cbi-button cbi-button-reload" type="button" name="check" value="<%:Check update%>" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="state state-checked">
|
||||||
|
<form>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title" id="download_result"><%:Found new firmware%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input class="cbi-button cbi-button-apply" type="button" name="download" value="<%:Download firmware%>" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="state state-downloading">
|
||||||
|
<form>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title" id="download_progress">0%</label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input class="cbi-button cbi-button-reset" type="button" name="cancel" value="<%:Cancel download%>" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="state state-downloaded">
|
||||||
|
<% if fs.access("/usr/lib/lua/luci/controller/admin/system.lua") then %>
|
||||||
|
<form method="post" action="<%=url('admin/system/flashops/sysupgrade')%>" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
|
<input type="hidden" name="step" value="1" />
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Firmware downloaded%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="submit" class="cbi-button cbi-input-apply" value="<%:Flash image...%>" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% else %>
|
||||||
|
<form method="post" action="<%=url('admin/system/ota')%>">
|
||||||
|
<input type="hidden" name="apply" value="1" />
|
||||||
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title" for="keep"><%:Keep settings and retain the current configuration%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="checkbox" name="keep" value="1" id="keep" checked="checked" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value cbi-value-last">
|
||||||
|
<label class="cbi-value-title"><%:Firmware downloaded%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="submit" class="cbi-button cbi-input-apply" value="<%:Flash image...%>" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="state state-checked state-downloading state-downloaded">
|
||||||
|
<div class="cbi-section-descr">
|
||||||
|
<h2><%:The latest firmware%>:</h2>
|
||||||
|
<div id="upgrade_log"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var csrfToken = "<%=token%>";
|
||||||
|
var check = document.querySelector('input[name="check"]');
|
||||||
|
var download = document.querySelector('input[name="download"]');
|
||||||
|
var cancel = document.querySelector('input[name="cancel"]');
|
||||||
|
var state_ctl = document.querySelector('.state-ctl');
|
||||||
|
var check_result = document.querySelector('#check_result');
|
||||||
|
var download_result = document.querySelector('#download_result');
|
||||||
|
var download_progress = document.querySelector('#download_progress');
|
||||||
|
var upgrade_log = document.querySelector('#upgrade_log');
|
||||||
|
|
||||||
|
var xhr_post = function(url, data, cb) {
|
||||||
|
data = data || {};
|
||||||
|
data.token = '<%=token%>';
|
||||||
|
new XHR().post(url, data, function(x, d){
|
||||||
|
cb && cb(x, x.status==200?JSON.parse(x.responseText):{code:500,msg:x.responseText});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var state_switch = function(from, to) {
|
||||||
|
state_ctl.classList.remove("state-ctl-" + from);
|
||||||
|
state_ctl.classList.add("state-ctl-" + to);
|
||||||
|
};
|
||||||
|
check.onclick = function(){
|
||||||
|
check.disabled = 'disabled';
|
||||||
|
check_result.innerText = "<%:Checking...%>";
|
||||||
|
xhr_post("<%=url('admin/system/ota/check')%>", {}, function(x, d){
|
||||||
|
check.disabled = undefined;
|
||||||
|
switch(d.code){
|
||||||
|
case 0:
|
||||||
|
check_result.innerText = "";
|
||||||
|
upgrade_log.innerHTML = d.msg;
|
||||||
|
state_switch("unchecked", "checked");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
check_result.innerText = "<%:Already the latest firmware%>";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
check_result.innerText = "<%:Check failed%>";
|
||||||
|
alert("<%:Check failed%>:\n"+d.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var refresh_download_progress_paused = false;
|
||||||
|
var refresh_download_progress_timer = null;
|
||||||
|
var refresh_download_progress = function() {
|
||||||
|
if (refresh_download_progress_paused) {
|
||||||
|
refresh_download_progress_timer = setTimeout(refresh_download_progress, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XHR.get("<%=url('admin/system/ota/progress')%>", {}, function(x, d){
|
||||||
|
refresh_download_progress_timer = null;
|
||||||
|
switch(d.code){
|
||||||
|
case 0:
|
||||||
|
download_progress.innerText = "";
|
||||||
|
state_switch("downloading", "downloaded");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// if (d.msg.startsWith('#')) {
|
||||||
|
if (/^[0-9]/.test(d.msg)) {
|
||||||
|
if (!refresh_download_progress_paused)
|
||||||
|
cancel.disabled = undefined;
|
||||||
|
download_progress.innerText = "<%:Downloading%>: "+d.msg;
|
||||||
|
} else {
|
||||||
|
if (!refresh_download_progress_paused)
|
||||||
|
cancel.disabled = 'disabled';
|
||||||
|
download_progress.innerText = d.msg;
|
||||||
|
}
|
||||||
|
refresh_download_progress_timer = setTimeout(refresh_download_progress, 500);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
download_result.innerText = "<%:Download canceled%>";
|
||||||
|
state_switch("downloading", "checked");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
download_result.innerText = "<%:Download failed%>";
|
||||||
|
state_switch("downloading", "checked");
|
||||||
|
alert("<%:Download failed%>:"+d.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
download.onclick = function(){
|
||||||
|
download.disabled = 'disabled';
|
||||||
|
xhr_post("<%=url('admin/system/ota/download')%>", {}, function(x, d){
|
||||||
|
download.disabled = undefined;
|
||||||
|
switch(d.code){
|
||||||
|
case 0:
|
||||||
|
download_progress.innerText = "<%:Downloading%>: ...";
|
||||||
|
cancel.disabled = 'disabled';
|
||||||
|
state_switch("checked", "downloading");
|
||||||
|
refresh_download_progress_timer = setTimeout(refresh_download_progress, 500);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
alert("<%:Download failed%>:"+d.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
cancel.onclick = function() {
|
||||||
|
cancel.disabled = 'disabled';
|
||||||
|
refresh_download_progress_paused = true;
|
||||||
|
|
||||||
|
xhr_post("<%=url('admin/system/ota/cancel')%>", {}, function(x, d){
|
||||||
|
refresh_download_progress_paused = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<%+footer%>
|
111
luci-app-ota/luasrc/view/admin_system/ota_flashing.htm
Normal file
111
luci-app-ota/luasrc/view/admin_system/ota_flashing.htm
Normal file
File diff suppressed because one or more lines are too long
1
luci-app-ota/po/zh-cn
Symbolic link
1
luci-app-ota/po/zh-cn
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
zh_Hans
|
53
luci-app-ota/po/zh_Hans/ota.po
Normal file
53
luci-app-ota/po/zh_Hans/ota.po
Normal file
@ -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 "镜像文件格式不支持或损坏。也许稍候再试试"
|
156
luci-app-ota/root/bin/ota
Executable file
156
luci-app-ota/root/bin/ota
Executable file
@ -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 "<h3>Model: <font color=\"green\">$(board_name)</font><br/>Current Version: <font color=\"green\">$PRETTY_NAME</font><br/>Build Date: <font color=\"Orange\">$(date "+%Y-%m-%d %H:%M:%S" -d "@$BUILD_DATE")</font></h3>"
|
||||||
|
echo "<h3>New Version: <font color=\"green\">OpenWrt $NEW_VERSION</font><br/>Build Date: <font color=\"green\">$(date "+%Y-%m-%d %H:%M:%S" -d "@$FW_BUILD_DATE")</font></h3>"
|
||||||
|
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
|
3
luci-app-ota/root/etc/config/ota
Normal file
3
luci-app-ota/root/etc/config/ota
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
config ota 'config'
|
||||||
|
option api_url ''
|
11
luci-app-ota/root/usr/share/rpcd/acl.d/luci-app-ota.json
Normal file
11
luci-app-ota/root/usr/share/rpcd/acl.d/luci-app-ota.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"luci-app-ota": {
|
||||||
|
"description": "Grant UCI access for luci-app-ota",
|
||||||
|
"read": {
|
||||||
|
"uci": [ "ota" ]
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"uci": [ "ota" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user