luci-app-advanced-reboot: rewrite rpcd in shell script + bugfixes

Signed-off-by: Stan Grishin <stangri@melmac.net>
This commit is contained in:
Stan Grishin 2021-01-06 16:56:10 +00:00
parent e7aeb9b153
commit 1ce1f86a2c
6 changed files with 380 additions and 432 deletions

View file

@ -9,10 +9,9 @@ PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
LUCI_TITLE:=Advanced Linksys Reboot Web UI LUCI_TITLE:=Advanced Linksys Reboot Web UI
LUCI_DESCRIPTION:=Provides Web UI (found under System/Advanced Reboot) to reboot supported Linksys and ZyXEL routers to\ LUCI_DESCRIPTION:=Provides Web UI (found under System/Advanced Reboot) to reboot supported Linksys and ZyXEL routers to\
an alternative partition. Also provides Web UI to shut down (power off) your device. Supported dual-partition\ an alternative partition. Also provides Web UI to shut down (power off) your device. Supported dual-partition\
routers are listed at https://github.com/openwrt/luci/blob/master/applications/luci-app-advanced-reboot/README.md routers are listed at https://docs.openwrt.melmac.net/luci-app-advanced-reboot/
LUCI_DEPENDS:=+luci-mod-admin-full LUCI_DEPENDS:=+luci-mod-admin-full +jshn
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_RELEASE:=55
include ../../luci.mk include ../../luci.mk

View file

@ -1,83 +1,3 @@
# Advanced Reboot Web UI (luci-app-advanced-reboot) # Advanced Reboot Web UI (luci-app-advanced-reboot)
[![HitCount](http://hits.dwyl.com/stangri/openwrt/luci-app-advanced-reboot.svg)](http://hits.dwyl.com/stangri/openwrt/luci-app-advanced-reboot) README has been moved to [https://docs.openwrt.melmac.net/luci-app-advanced-reboot/](<https://docs.openwrt.melmac.net/luci-app-advanced-reboot/>).
## Description
This package allows you to reboot to an alternative partition on the supported (dual-partition) routers and to power off (power down) your OpenWrt device.
## Supported Devices
Currently supported dual-partition devices include:
- Linksys EA3500
- Linksys E4200v2
- Linksys EA4500
- Linksys EA6350v3
- Linksys EA7300v2
- Linksys EA8300
- Linksys MR8300
- Linksys EA8500
- Linksys WRT1200AC
- Linksys WRT1900AC
- Linksys WRT1900ACv2
- Linksys WRT1900ACS
- Linksys WRT3200ACM
- Linksys WRT32X
- ZyXEL NBG6817
If your device is not in the list above, however it is a [dual-firmware device](https://openwrt.org/tag/dual_firmware?do=showtag&tag=dual_firmware) and you're interested in having your device supported, please post in [OpenWrt Forum Support Thread](https://forum.openwrt.org/t/web-ui-to-reboot-to-another-partition-dual-partition-routers/3423).
## Screenshot (luci-app-advanced-reboot)
![screenshot](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/screenshots/luci-app-advanced-reboot/screenshot02.png "screenshot")
## How to install
Install ```luci-app-advanced-reboot``` from Web UI or connect to your router via ssh and run the following commands:
```sh
opkg update
opkg install luci-app-advanced-reboot
```
If the ```luci-app-advanced-reboot``` package is not found in the official feed/repo for your version of OpenWrt/LEDE Project, you will need to add a custom repo to your router following instructions on [GitHub](https://github.com/stangri/openwrt_packages/blob/master/README.md#on-your-router)/[jsDelivr](https://cdn.jsdelivr.net/gh/stangri/openwrt_packages@master/README.md#on-your-router) first.
## Notes/Known Issues
- When you reboot to a different partition, your current settings (WiFi SSID/password, etc.) will not apply to a different partition. Different partitions might have completely different settings and even firmware.
- If you reboot to a partition which doesn't allow you to switch boot partitions (like stock vendor firmware), you might not be able to boot back to OpenWrt unless you reflash it, losing all the settings.
- Some devices allow you to trigger reboot to an alternative partition by interrupting boot 3 times in a row (by resetting/switching off the device or pulling power). As these methods might be different for different devices, do your own homework.
- Newer versions of this package try to mount alternative partition on compatible NAND routers in order to retrieve detailed firmware information. When that happens, it is normal to have messages similar to the below in the system log:
```sh
Tue Nov 19 15:45:03 2019 user.notice luci-app-advanced-reboot: attempting to mount alternative partition (mtd6)
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.673826] ubi2: attaching mtd6
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.876698] ubi2: scanning is finished
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.885267] ubi2: attached mtd6 (name "rootfs1", size 74 MiB)
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.891063] ubi2: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.898011] ubi2: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.904878] ubi2: VID header offset: 2048 (aligned 2048), data offset: 4096
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.911928] ubi2: good PEBs: 592, bad PEBs: 0, corrupted PEBs: 0
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.917962] ubi2: user volume: 2, internal volumes: 1, max. volumes count: 128
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.925252] ubi2: max/mean erase counter: 48/32, WL threshold: 4096, image sequence number: 1659081076
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.934623] ubi2: available PEBs: 0, total reserved PEBs: 592, PEBs reserved for bad PEB handling: 40
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.944346] ubi2: background thread "ubi_bgt2d" started, PID 26780
Tue Nov 19 15:45:03 2019 kern.info kernel: [30392.952596] block ubiblock2_0: created from ubi2:0 (rootfs)
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30392.964083] UBIFS (ubi2:1): background thread "ubifs_bgt2_1" started, PID 26787
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.009298] UBIFS (ubi2:1): UBIFS: mounted UBI device 2, volume 1, name "rootfs_data"
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.017185] UBIFS (ubi2:1): LEB size: 126976 bytes (124 KiB), min./max. I/O unit sizes: 2048 bytes/2048 bytes
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.027213] UBIFS (ubi2:1): FS size: 61075456 bytes (58 MiB, 481 LEBs), journal size 3047424 bytes (2 MiB, 24 LEBs)
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.037733] UBIFS (ubi2:1): reserved for root: 2884744 bytes (2817 KiB)
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.044389] UBIFS (ubi2:1): media format: w4/r0 (latest is w5/r0), UUID 76F0C52C-6197-4E00-B306-747262B06545, small LPT model
Tue Nov 19 15:45:03 2019 user.notice luci-app-advanced-reboot: attempting to unmount alternative partition (mtd6)
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.132743] UBIFS (ubi2:1): un-mount UBI device 2
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.137481] UBIFS (ubi2:1): background thread "ubifs_bgt2_1" stops
Tue Nov 19 15:45:03 2019 kern.info kernel: [30393.390961] block ubiblock2_0: released
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.396576] ubi2: detaching mtd6
Tue Nov 19 15:45:03 2019 kern.notice kernel: [30393.400117] ubi2: mtd6 is detached
```
## Thanks
I'd like to thank everyone who helped create, test and troubleshoot this package. Without help from [@hnyman](https://github.com/hnyman), [@jpstyves](https://github.com/jpstyves), [@imi2003](https://github.com/imi2003), [@jeffsf](https://github.com/jeffsf) and many contributions from [@slh](https://github.com/pkgadd) it wouldn't have been possible.

View file

@ -157,7 +157,7 @@ return view.extend({
} }
res.push([ res.push([
(partition.number).toString(16).toUpperCase(), (partition.number+0x100).toString(16).substr(-2).toUpperCase(),
_(partition.state), _(partition.state),
partition.os.replace("Unknown", _("Unknown")).replace("Compressed", _("Compressed")), partition.os.replace("Unknown", _("Unknown")).replace("Compressed", _("Compressed")),
E('button', { E('button', {

View file

@ -1,362 +1,390 @@
#!/usr/bin/env lua #!/bin/sh
# Copyright 2017-2020 Stan Grishin (stangri@melmac.net)
# shellcheck disable=SC2039,SC1091
local json = require "luci.jsonc" readonly devices_dir="/usr/share/advanced-reboot/devices/"
local nixio = require "nixio"
local fs = require "nixio.fs"
local sys = require "luci.sys"
local util = require "luci.util"
local devices_dir = "/usr/share/advanced-reboot/devices/" . /lib/functions.sh
. /usr/share/libubox/jshn.sh
local function logger(t) logger() { /usr/bin/logger -t advanced-reboot "$1"; }
util.exec("logger -t advanced-reboot '" .. tostring(t) .. "'")
end
local function is_alt_mountable(p1_mtd, p2_mtd) is_alt_mountable() {
if p1_mtd:sub(1,3) == "mtd" and local p1_mtd="$1" p2_mtd="$2"
p2_mtd:sub(1,3) == "mtd" and if [ "${p1_mtd:0:3}" = "mtd" ] && [ "${p2_mtd:0:3}" = "mtd" ] && \
fs.access("/usr/sbin/ubiattach") and [ -x "/usr/sbin/ubiattach" ] && \
fs.access("/usr/sbin/ubiblock") and [ -x "/usr/sbin/ubiblock" ] && \
fs.access("/bin/mount") then [ -x "/bin/mount" ]; then
return true return 0
else else
return false return 1
end fi
end
local function alt_partition_mount(op_ubi)
local ubi_dev
util.exec('for i in alt_rom alt_overlay firmware; do [ ! -d "$i" ] && mkdir -p "/alt/${i}"; done')
ubi_dev = tostring(util.exec("ubiattach -m " .. tostring(op_ubi)))
_, _, ubi_dev = ubi_dev:find("UBI device number (%d+)")
if not ubi_dev then
util.exec("ubidetach -m " .. tostring(op_ubi))
return
end
util.exec("ubiblock --create /dev/ubi" .. ubi_dev .. "_0")
util.exec("mount -t squashfs -o ro /dev/ubiblock" .. ubi_dev .. "_0 /alt/alt_rom")
util.exec("mount -t ubifs /dev/ubi" .. ubi_dev .. "_1 /alt/alt_overlay")
-- util.exec("mount -t overlay overlay -o noatime,lowerdir=/alt/rom,upperdir=/alt/overlay/upper,workdir=/alt/overlay/work /alt/firmware")
end
local function alt_partition_unmount(op_ubi)
local i
local mtdCount = tonumber(util.exec("ubinfo | grep 'Present UBI devices' | grep -c ','"))
mtdCount = mtdCount and mtdCount + 1 or 10
-- util.exec("[ -d /alt/firmware ] && umount /alt/firmware")
util.exec("[ -d /alt/alt_overlay ] && umount /alt/alt_overlay")
util.exec("[ -d /alt/alt_rom ] && umount /alt/alt_rom")
for i = 0, mtdCount do
if not fs.access("/sys/devices/virtual/ubi/ubi" .. tostring(i) .. "/mtd_num") then break end
ubi_mtd = tonumber(util.trim(util.exec("cat /sys/devices/virtual/ubi/ubi" .. i .. "/mtd_num")))
if ubi_mtd and ubi_mtd == op_ubi then
util.exec("ubiblock --remove /dev/ubi" .. tostring(i) .. "_0")
util.exec("ubidetach -m " .. tostring(op_ubi))
util.exec('rm -rf /alt')
end
end
end
local function get_partition_os_info(op_ubi)
local cp_info, op_info
if fs.access("/etc/os-release") then
cp_info = util.trim(util.exec('. /etc/os-release && echo "$PRETTY_NAME"'))
if cp_info:find("SNAPSHOT") then
cp_info = util.trim(util.exec('. /etc/os-release && echo "$OPENWRT_RELEASE"'))
end
end
logger(string.format("attempting to mount alternative partition (mtd%s)", tostring(op_ubi)))
alt_partition_unmount(op_ubi)
alt_partition_mount(op_ubi)
if fs.access("/alt/alt_rom/etc/os-release") then
op_info = util.trim(util.exec('. /alt/alt_rom/etc/os-release && echo "$PRETTY_NAME"'))
if op_info:find("SNAPSHOT") then
op_info = util.trim(util.exec('. /alt/alt_rom/etc/os-release && echo "$OPENWRT_RELEASE"'))
end
end
logger(string.format("attempting to unmount alternative partition (mtd%s)", tostring(op_ubi)))
alt_partition_unmount(op_ubi)
return cp_info, op_info
end
local function find_device_data(romBoardName)
local filename
for filename in fs.dir(devices_dir) do
local filedata = fs.readfile(devices_dir .. filename)
local p = json.parse(filedata or {})
if p then
local boardName
if p.boardName then
boardName = p.boardName:gsub('%p','')
if romBoardName:match(boardName) then return p end
end
if p.boardNames then
for i, v in pairs(p.boardNames) do
boardName = v:gsub('%p','')
if romBoardName:match(boardName) then return p end
end
end
end
end
return nil
end
local methods = {
obtain_device_info = {
call = function()
local ret = {}
local romBoardName = fs.readfile('/tmp/sysinfo/board_name')
if not romBoardName then
ret.error = 'NO_BOARD_NAME'
return ret
end
romBoardName = romBoardName:gsub('\n','')
ret.rom_board_name = romBoardName
romBoardName = romBoardName:gsub('%p','')
local p, boardName, n, p1_label, p1_version, p2_label, p2_version, p1_os, p2_os
local current_partition
local op_ubi, cp_info, op_info, zyxelFlagPartition
p = find_device_data(romBoardName)
if p then
if p.labelOffset then
if p.partition1MTD then
p1_label = util.trim(util.exec("dd if=/dev/" .. p.partition1MTD .. " bs=1 skip=" .. p.labelOffset .. " count=128" .. " 2>/dev/null"))
n, p1_version = p1_label:match('(Linux)-([%d|.]+)')
end
if p1_label then
if p1_label:find("LEDE") then p1_os = "LEDE" end
if p1_label:find("OpenWrt") then p1_os = "OpenWrt" end
if p.vendorName and p1_label:find(p.vendorName) then p1_os = p.vendorName end
end
if not p1_os then
p1_os = (p.vendorName and p.vendorName or 'Unknown') .. "/" .. "Unknown"
end
if p1_os and p1_version then p1_os = p1_os .. " (Linux " .. p1_version .. ")" end
if p.partition2MTD then
p2_label = util.trim(util.exec("dd if=/dev/" .. p.partition2MTD .. " bs=1 skip=" .. p.labelOffset .. " count=128" .. " 2>/dev/null"))
n, p2_version = p2_label:match('(Linux)-([%d|.]+)')
end
if p2_label then
if p2_label:find("LEDE") then p2_os = "LEDE" end
if p2_label:find("OpenWrt") then p2_os = "OpenWrt" end
if p.vendorName and p2_label:find(p.vendorName) then p2_os = p.vendorName end
end
if not p2_os then
p2_os = (p.vendorName and p.vendorName or 'Unknown') .. "/" .. "Unknown"
end
if p2_os and p2_version then p2_os = p2_os .. " (Linux " .. p2_version .. ")" end
else
p1_os = p.vendorName .. "/" .. "Unknown" .. " (" .. "Compressed" .. ")"
p2_os = p.vendorName .. "/" .. "Unknown" .. " (" .. "Compressed" .. ")"
end
if p.bootEnv1 then
if fs.access("/usr/sbin/fw_printenv") and fs.access("/usr/sbin/fw_setenv") then
current_partition = tonumber(util.trim(util.exec("fw_printenv -n " .. p.bootEnv1)))
end
else
if not zyxelFlagPartition then zyxelFlagPartition = util.trim(util.exec(". /lib/functions.sh; find_mtd_part 0:DUAL_FLAG")) end
if zyxelFlagPartition then
current_partition = tonumber(util.exec("dd if=" .. zyxelFlagPartition .. " bs=1 count=1 2>/dev/null | hexdump -n 1 -e '1/1 \"%d\"'"))
else
ret.error = 'NO_DUAL_FLAG'
logger("Unable to find Dual Boot Flag Partition.")
return ret
end
end
ret.current_partition = current_partition
if is_alt_mountable(p.partition1MTD, p.partition2MTD) then
if current_partition == p.bootEnv1Partition1Value then
op_ubi = tonumber(p.partition2MTD:sub(4)) + 1
else
op_ubi = tonumber(p.partition1MTD:sub(4)) + 1
end
local cp_info, op_info = get_partition_os_info(op_ubi)
if current_partition == p.bootEnv1Partition1Value then
p1_os = cp_info or p1_os
p2_os = op_info or p2_os
else
p1_os = op_info or p1_os
p2_os = cp_info or p2_os
end
end
ret.device_name = (p.vendorName and p.vendorName or "") .. " " .. p.deviceName
ret.partitions = {
{
os = p1_os,
state = p.bootEnv1Partition1Value == current_partition and 'Current' or 'Alternative',
number = p.bootEnv1Partition1Value
},
{
os = p2_os,
state = p.bootEnv1Partition2Value == current_partition and 'Current' or 'Alternative',
number = p.bootEnv1Partition2Value
}
}
end
return ret
end
},
toggle_boot_partition = {
call = function()
local ret = {}
local zyxelFlagPartition, zyxelBootFlag, zyxelNewBootFlag, errorCode, curEnvSetting, newEnvSetting
local romBoardName = fs.readfile('/tmp/sysinfo/board_name')
if not romBoardName then
ret.error = 'NO_BOARD_NAME'
return ret
end
romBoardName = romBoardName:gsub('\n',''):gsub('%p','')
p = find_device_data(romBoardName)
local bev1, bev2 = p.bootEnv1, p.bootEnv2
if bev1 or bev2 then -- Linksys devices
if bev1 then
curEnvSetting = tonumber(util.trim(util.exec("fw_printenv -n " .. bev1)))
if not curEnvSetting then
logger(string.format("Unable to obtain firmware environment variable: %s.", bev1))
ret.error = 'NO_FIRM_ENV'
ret.args = { bev1 }
return ret
else
local bev1p1, bev1p2 = p.bootEnv1Partition1Value, p.bootEnv1Partition2Value
newEnvSetting = curEnvSetting == bev1p1 and bev1p2 or bev1p1
errorCode = sys.call("fw_setenv " .. bev1 .. " " .. newEnvSetting)
if errorCode ~= 0 then
logger(string.format("Unable to set firmware environment variable: %s to %s.", bev1, newEnvSetting))
ret.error = 'ERR_SET_ENV'
ret.args = { bev1, newEnvSetting }
return ret
end
end
end
if bev2 then
curEnvSetting = util.trim(util.exec("fw_printenv -n " .. bev2))
if not curEnvSetting then
logger(string.format("Unable to obtain firmware environment variable: %s.", bev2))
ret.error = 'NO_FIRM_ENV'
ret.args = { bev2 }
return ret
else
local bev2p1, bev2p2 = p.bootEnv2Partition1Value, p.bootEnv2Partition1Value
newEnvSetting = curEnvSetting == bev2p1 and bev2p2 or bev2p1
errorCode = sys.call("fw_setenv " .. bev2 .. " '" .. newEnvSetting .. "'")
if errorCode ~= 0 then
logger(string.format("Unable to set firmware environment variable: %s to %s.", bev2, newEnvSetting))
ret.error = 'ERR_SET_ENV'
ret.args = { bev2, newEnvSetting }
return ret
end
end
end
else -- NetGear device
if not zyxelFlagPartition then zyxelFlagPartition = util.trim(util.exec(". /lib/functions.sh; find_mtd_part 0:DUAL_FLAG")) end
if not zyxelFlagPartition then
logger("Unable to find Dual Boot Flag Partition.")
ret.error = 'NO_DUAL_FLAG'
else
zyxelBootFlag = tonumber(util.exec("dd if=" .. zyxelFlagPartition .. " bs=1 count=1 2>/dev/null | hexdump -n 1 -e '1/1 \"%d\"'"))
zyxelNewBootFlag = zyxelBootFlag and zyxelBootFlag == 1 and "\\xff" or "\\x01"
if zyxelNewBootFlag then
errorCode = sys.call("printf \"" .. zyxelNewBootFlag .. "\" >" .. zyxelFlagPartition )
if errorCode ~= 0 then
logger(string.format("Unable to set Dual Boot Flag Partition entry for partition: %s.", zyxelFlagPartition))
ret.error = 'ERR_SET_DUAL_FLAG'
ret.args = { zyxelFlagPartition }
return ret
end
end
end
end
return ret
end
}
} }
local function parseInput() alt_partition_mount() {
local parse = json.new() local ubi_dev op_ubi="$1"
local done, err for i in alt_rom alt_overlay firmware; do [ ! -d "$i" ] && mkdir -p "/alt/${i}"; done
ubi_dev="$(ubiattach -m "$op_ubi")"
ubi_dev="$(echo "$ubi_dev" | sed -n "s/^UBI device number\s*\(\d*\),.*$/\1/p")"
if [ -z "$ubi_dev" ]; then
ubidetach -m "$op_ubi"
return 1
fi
ubiblock --create "/dev/ubi${ubi_dev}_0"
mount -t squashfs -o ro "/dev/ubiblock${ubi_dev}_0" /alt/alt_rom
mount -t ubifs "/dev/ubi${ubi_dev}_0" /alt/alt_overlay
# mount -t overlay overlay -o noatime,lowerdir=/alt/rom,upperdir=/alt/overlay/upper,workdir=/alt/overlay/work /alt/firmware
}
while true do alt_partition_unmount() {
local chunk = io.read(4096) local mtdCount i=0 op_ubi="$1"
if not chunk then mtdCount="$(ubinfo | grep 'Present UBI devices' | tr ',' '\n' | grep -c 'ubi')"
[ -z "$mtdCount" ] && mtdCount=10
# [ -d /alt/firmware ] && umount /alt/firmware
[ -d /alt/alt_overlay ] && umount /alt/alt_overlay
[ -d /alt/alt_rom ] && umount /alt/alt_rom
while [ "$i" -le "$mtdCount" ]; do
if [ ! -e "/sys/devices/virtual/ubi/ubi${i}/mtd_num" ]; then
break break
elseif not done and not err then fi
done, err = parse:parse(chunk) ubi_mtd="$(cat /sys/devices/virtual/ubi/ubi${i}/mtd_num)"
end if [ -n "$ubi_mtd" ] && [ "$ubi_mtd" = "$op_ubi" ]; then
end ubiblock --remove /dev/ubi${i}_0
ubidetach -m "$op_ubi"
rm -rf /alt
fi
i=$((i + 1))
done
}
if not done then get_main_partition_os_info(){
print(json.stringify({ error = err or "Incomplete input" })) local cp_info
os.exit(1) if [ -s "/etc/os-release" ]; then
end cp_info="$(. /etc/os-release && echo "$PRETTY_NAME")"
if [ "${cp_info//SNAPSHOT}" != "$cp_info" ]; then
cp_info="$(. /etc/os-release && echo "${OPENWRT_RELEASE%%-*}")"
fi
fi
echo "$cp_info"
}
return parse:get() get_alt_partition_os_info(){
end local op_info op_ubi="$1"
logger "attempting to mount alternative partition (mtd${op_ubi})"
alt_partition_unmount "$op_ubi"
alt_partition_mount "$op_ubi"
if [ -s "/alt/alt_rom/etc/os-release" ]; then
op_info="$(. /alt/alt_rom/etc/os-release && echo "$PRETTY_NAME")"
if [ "${op_info//SNAPSHOT}" != "$op_info" ]; then
op_info="$(. /alt/alt_rom/etc/os-release && echo "${OPENWRT_RELEASE%%-*}")"
fi
fi
logger "attempting to unmount alternative partition (mtd${op_ubi})"
alt_partition_unmount "$op_ubi"
echo "$op_info"
}
local function validateArgs(func, uargs) find_device_data(){
local method = methods[func] local boardNames filename i romBoardName="$1"
if not method then for filename in "${devices_dir}"*.json; do
print(json.stringify({ error = "Method not found" })) [ "$filename" = "${devices_dir}*.json" ] && return
os.exit(1) json_load_file "$filename"
end json_get_values boardNames 'boardNames'
json_cleanup
for i in $boardNames; do
if [ "$i" = "$romBoardName" ]; then
echo "$filename"
return
fi
done
done
}
if type(uargs) ~= "table" then print_json() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
print(json.stringify({ error = "Invalid arguments" }))
os.exit(1)
end
uargs.ubus_rpc_session = nil obtain_device_info(){
local romBoardName p zyxelFlagPartition
local vendorName deviceName partition1MTD partition2MTD labelOffset
local bootEnv1 bootEnv1Partition1Value bootEnv1Partition2Value
local bootEnv2 bootEnv2Partition1Value bootEnv2Partition2Value
local p1_label p1_version p2_label p2_version p1_os p2_os
local current_partition op_ubi cp_info op_info
local k, v romBoardName="$(cat /tmp/sysinfo/board_name)"
local margs = method.args or {} if [ -z "$romBoardName" ]; then
for k, v in pairs(uargs) do print_json 'error' 'NO_BOARD_NAME'
if margs[k] == nil or return
(v ~= nil and type(v) ~= type(margs[k])) fi
then
print(json.stringify({ error = "Invalid arguments" }))
os.exit(1)
end
end
return method p="$(find_device_data "$romBoardName")"
end if [ -z "$p" ] || [ ! -s "$p" ]; then
print_json 'rom_board_name' "$romBoardName"
return
fi
if arg[1] == "list" then json_load_file "$p"
local _, method, rv = nil, nil, {} for i in vendorName deviceName partition1MTD partition2MTD labelOffset \
for _, method in pairs(methods) do rv[_] = method.args or {} end bootEnv1 bootEnv1Partition1Value bootEnv1Partition2Value \
print((json.stringify(rv):gsub(":%[%]", ":{}"))) bootEnv2 bootEnv2Partition1Value bootEnv2Partition2Value; do
elseif arg[1] == "call" then json_get_var $i "$i"
local args = parseInput() done
local method = validateArgs(arg[2], args) json_cleanup
local result, code = method.call(args)
print((json.stringify(result):gsub("^%[%]$", "{}"))) if [ -n "$labelOffset" ]; then
os.exit(code or 0) if [ -n "$partition1MTD" ]; then
end p1_label="$(dd if="/dev/${partition1MTD}" bs=1 skip="${labelOffset}" count=64 2>/dev/null)"
if [ -n "$p1_label" ]; then
p1_version="$(echo "$p1_label" | sed -n "s/\(.*\)Linux-\([0-9.]\+\).*$/\2/p")"
if [ "${p1_label//LEDE}" != "$p1_label" ]; then p1_os="LEDE"; fi
if [ "${p1_label//OpenWrt}" != "$p1_label" ]; then p1_os="OpenWrt"; fi
if [ -n "$vendorName" ] && [ "${p1_label//$vendorName}" != "$p1_label" ]; then
p1_os="$vendorName"
fi
fi
if [ -z "$p1_os" ]; then
p1_os="${vendorName:-Unknown}/Unknown"
fi
fi
if [ -n "$partition2MTD" ]; then
p2_label="$(dd if="/dev/${partition2MTD}" bs=1 skip="${labelOffset}" count=64 2>/dev/null)"
if [ -n "$p2_label" ]; then
p2_version="$(echo "$p2_label" | sed -n "s/\(.*\)Linux-\([0-9.]\+\).*$/\2/p")"
if [ "${p2_label//LEDE}" != "$p2_label" ]; then p2_os="LEDE"; fi
if [ "${p2_label//OpenWrt}" != "$p2_label" ]; then p2_os="OpenWrt"; fi
if [ -n "$vendorName" ] && [ "${p2_label//$vendorName}" != "$p2_label" ]; then
p2_os="$vendorName"
fi
fi
if [ -z "$p2_os" ]; then
p2_os="${vendorName:-Unknown}/Unknown"
fi
fi
else
p1_os="${vendorName}/Unknown (Compressed)"
p2_os="${vendorName}/Unknown (Compressed)"
fi
if [ -n "$bootEnv1" ]; then
if [ -x "/usr/sbin/fw_printenv" ] && [ -x "/usr/sbin/fw_setenv" ]; then
current_partition="$(/usr/sbin/fw_printenv -n "${bootEnv1}")"
fi
else
if [ -z "$zyxelFlagPartition" ]; then
zyxelFlagPartition="$(find_mtd_part 0:DUAL_FLAG 2>/dev/null)"
fi
if [ -n "$zyxelFlagPartition" ]; then
current_partition="$(dd if="${zyxelFlagPartition}" bs=1 count=1 2>/dev/null | hexdump -n 1 -e '1/1 "%d"')"
else
print_json 'error' 'NO_DUAL_FLAG'
logger "Unable to find Dual Boot Environment or Dual Boot Flag Partition."
return
fi
fi
if is_alt_mountable "$partition1MTD" "$partition2MTD"; then
if [ "$current_partition" = "$bootEnv1Partition1Value" ]; then
op_ubi=$(( ${partition2MTD:3:3} + 1 ))
else
op_ubi=$(( ${partition1MTD:3:3} + 1 ))
fi
cp_info="$(get_main_partition_os_info $op_ubi)"
op_info="$(get_alt_partition_os_info $op_ubi)"
if [ "$current_partition" = "$bootEnv1Partition1Value" ]; then
p1_os="${cp_info:-$p1_os}"
p2_os="${op_info:-$p2_os}"
else
p1_os="${op_info:-$p1_os}"
p2_os="${cp_info:-$p2_os}"
fi
fi
if [ -n "$p1_os" ] && [ -n "$p1_version" ]; then
p1_os="$p1_os (Linux ${p1_version})"
fi
if [ -n "$p2_os" ] && [ -n "$p2_version" ]; then
p2_os="$p2_os (Linux ${p2_version})"
fi
json_init
json_add_int 'current_partition' "$current_partition"
json_add_string 'device_name' "$vendorName $deviceName"
json_add_array 'partitions'
json_add_object
if [ "$bootEnv1Partition1Value" = "$current_partition" ]; then
json_add_string 'state' "Current"
else
json_add_string 'state' "Alternative"
fi
json_add_string 'os' "$p1_os"
json_add_int 'number' "$bootEnv1Partition1Value"
json_close_object
json_add_object
if [ "$bootEnv1Partition2Value" = "$current_partition" ]; then
json_add_string 'state' "Current"
else
json_add_string 'state' "Alternative"
fi
json_add_string 'os' "$p2_os"
json_add_int 'number' "$bootEnv1Partition2Value"
json_close_object
json_close_array
json_add_string 'rom_board_name' "$romBoardName"
json_dump; json_cleanup;
}
toggle_boot_partition(){
local zyxelFlagPartition zyxelBootFlag zyxelNewBootFlag curEnvSetting newEnvSetting
local romBoardName p
local bev1 bev2 bev1p1 bev1p2 bev2p1 bev2p2
local vendorName deviceName partition1MTD partition2MTD labelOffset
local bootEnv1 bootEnv1Partition1Value bootEnv1Partition2Value
local bootEnv2 bootEnv2Partition1Value bootEnv2Partition2Value
romBoardName="$(cat /tmp/sysinfo/board_name)"
if [ -z "$romBoardName" ]; then
print_json 'error' 'NO_BOARD_NAME'
return
fi
p="$(find_device_data "$romBoardName")"
if [ -z "$p" ] || [ ! -s "$p" ]; then
print_json 'rom_board_name' "$romBoardName"
return
fi
json_load_file "$p"
for i in vendorName deviceName partition1MTD partition2MTD labelOffset \
bootEnv1 bootEnv1Partition1Value bootEnv1Partition2Value \
bootEnv2 bootEnv2Partition1Value bootEnv2Partition2Value; do
json_get_var $i "$i"
done
json_cleanup
bev1="$bootEnv1"
bev2="$bootEnv2"
if [ -n "${bev1}${bev2}" ]; then # Linksys devices
if [ -n "$bev1" ]; then
curEnvSetting="$(fw_printenv -n "${bev1}")"
if [ -z "$curEnvSetting" ]; then
logger "$(printf "Unable to obtain firmware environment variable: %s." "$bev1")"
json_init
json_add_string 'error' 'NO_FIRM_ENV'
json_add_array 'args'
json_add_string "$bev1"
json_close_array
json_add_string 'rom_board_name' "$romBoardName"
json_dump; json_cleanup;
return
else
bev1p1="$bootEnv1Partition1Value"
bev1p2="$bootEnv1Partition2Value"
if [ "$curEnvSetting" = "$bev1p1" ]; then
newEnvSetting="$bev1p2"
else
newEnvSetting="$bev1p1"
fi
if ! fw_setenv "$bev1" "$newEnvSetting"; then
logger "$(printf "Unable to set firmware environment variable: %s to %s." "$bev1" "$newEnvSetting")"
json_init
json_add_string 'error' 'ERR_SET_ENV'
json_add_array 'args'
json_add_string "$bev1"
json_add_string "$newEnvSetting"
json_close_array
json_add_string 'rom_board_name' "$romBoardName"
json_dump; json_cleanup;
return
fi
fi
fi
if [ -n "$bev2" ]; then
curEnvSetting="$(fw_printenv -n "${bev2}")"
if [ -z "$curEnvSetting" ]; then
logger "$(printf "Unable to obtain firmware environment variable: %s." "$bev2")"
json_init
json_add_string 'error' 'NO_FIRM_ENV'
json_add_array 'args'
json_add_string "$bev2"
json_close_array
json_add_string 'rom_board_name' "$romBoardName"
json_dump; json_cleanup;
return
else
bev2p1="$bootEnv2Partition1Value"
bev2p2="$bootEnv2Partition2Value"
if [ "$curEnvSetting" = "$bev2p1" ]; then
newEnvSetting="$bev2p2"
else
newEnvSetting="$bev2p1"
fi
if ! fw_setenv "$bev2" "$newEnvSetting"; then
logger "$(printf "Unable to set firmware environment variable: %s to %s." "$bev2" "$newEnvSetting")"
json_init
json_add_string 'error' 'ERR_SET_ENV'
json_add_array 'args'
json_add_string "$bev2"
json_add_string "$newEnvSetting"
json_close_array
json_add_string 'rom_board_name' "$romBoardName"
json_dump; json_cleanup;
return
fi
fi
fi
json_init
json_dump; json_cleanup;
else # NetGear device
if [ -z "$zyxelFlagPartition" ]; then
zyxelFlagPartition="$(find_mtd_part 0:DUAL_FLAG 2>/dev/null)"
fi
if [ -z "$zyxelFlagPartition" ]; then
logger "Unable to find Dual Boot Flag Partition."
print_json 'error' 'NO_DUAL_FLAG'
return
else
zyxelBootFlag="$(dd if="${zyxelFlagPartition}" bs=1 count=1 2>/dev/null | hexdump -n 1 -e '1/1 "%d"')"
if [ "$zyxelBootFlag" = "1" ]; then
zyxelNewBootFlag="\\xff"
else
zyxelNewBootFlag="\\x01"
fi
if [ -n "$zyxelNewBootFlag" ]; then
if ! printf "%b" "$zyxelNewBootFlag" > "$zyxelFlagPartition"; then
logger "$(printf "Unable to set Dual Boot Flag Partition entry for partition: %s." "$zyxelFlagPartition")"
json_init
json_add_string 'error' 'ERR_SET_DUAL_FLAG'
json_add_array 'args'
json_add_string "$zyxelFlagPartition"
json_close_array
json_add_string 'rom_board_name' "$romBoardName"
json_dump; json_cleanup;
return
fi
fi
fi
json_init
json_dump; json_cleanup;
fi
}
case "$1" in
list)
json_init
json_add_object "obtain_device_info"
json_close_object
json_add_object "toggle_boot_partition"
json_close_object
json_dump
json_cleanup
;;
call)
case "$2" in
obtain_device_info)
obtain_device_info;;
toggle_boot_partition)
toggle_boot_partition;;
esac
;;
esac

View file

@ -1,7 +1,7 @@
{ {
"vendorName": "ZyXEL", "vendorName": "ZyXEL",
"deviceName": "NBG6817", "deviceName": "NBG6817",
"boardNames": [ "nbg6817" ], "boardNames": [ "nbg6817", "zyxel,nbg6817" ],
"partition1MTD": "mmcblk0p4", "partition1MTD": "mmcblk0p4",
"partition2MTD": "mmcblk0p7", "partition2MTD": "mmcblk0p7",
"labelOffset": 32, "labelOffset": 32,

View file

@ -3,7 +3,8 @@
"description": "Grant UCI and file access for luci-app-advanced-reboot", "description": "Grant UCI and file access for luci-app-advanced-reboot",
"read": { "read": {
"ubus": { "ubus": {
"luci.advanced_reboot": [ "obtain_device_info", "toggle_boot_partition" ] "luci.advanced_reboot": [ "obtain_device_info", "toggle_boot_partition" ],
"system": [ "reboot" ]
}, },
"file": { "file": {
"/usr/sbin/fw_printenv": [ "list" ], "/usr/sbin/fw_printenv": [ "list" ],