From: Stan Grishin Date: Tue, 12 Aug 2025 08:31:10 +0000 (+0000) Subject: luci-app-advanced-reboot: update to 1.1.0-1 X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=71c21219388496f4d9fd49944ce45ca90be247c0;p=project%2Fluci.git luci-app-advanced-reboot: update to 1.1.0-1 Makefile: - update SPDX license identified and copyright - update documentation link README: - add short README with the link to full documentation System View javascript: - switch from single to double quotes - update documentation link - more readable translateTable - better code readability for - callPowerOff - handlePowerOff - handleReboot - handleTogglePartition - handleAlternativeReboot - parsePartitions - some visual elements RPCD Script: - more resilient to unexpected CLI output - add debugger function to ease development/debugging - better check if `/var/alt_rom` is mounted before calling umount - better handling of MTD indices Device Support: - add Linksys MR7350 which slipped thru the cracks Signed-off-by: Stan Grishin (cherry picked from commit a353d15b5592455e9efb95af290c036b780de31c) --- diff --git a/applications/luci-app-advanced-reboot/Makefile b/applications/luci-app-advanced-reboot/Makefile index 5166242a8d..dec193f77d 100644 --- a/applications/luci-app-advanced-reboot/Makefile +++ b/applications/luci-app-advanced-reboot/Makefile @@ -1,19 +1,19 @@ -# Copyright 2017-2024 MOSSDeF, Stan Grishin (stangri@melmac.ca). -# This is free software, licensed under AGPL-3.0-or-later. +# SPDX-License-Identifier: AGPL-3.0-or-later +# Copyright 2017-2025 MOSSDeF, Stan Grishin (stangri@melmac.ca). include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-advanced-reboot PKG_LICENSE:=AGPL-3.0-or-later PKG_MAINTAINER:=Stan Grishin -PKG_VERSION:=1.0.1 -PKG_RELEASE:=21 +PKG_VERSION:=1.1.0 +PKG_RELEASE:=1 LUCI_TITLE:=Advanced Linksys Reboot Web UI LUCI_URL:=https://github.com/stangri/luci-app-advanced-reboot/ 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\ - routers are listed at https://docs.openwrt.melmac.net/luci-app-advanced-reboot/ + routers are listed at https://docs.openwrt.melmac.ca/luci-app-advanced-reboot/ LUCI_DEPENDS:=+luci-base +jshn define Package/$(PKG_NAME)/config diff --git a/applications/luci-app-advanced-reboot/README.md b/applications/luci-app-advanced-reboot/README.md index 11df6a7380..c647595665 100644 --- a/applications/luci-app-advanced-reboot/README.md +++ b/applications/luci-app-advanced-reboot/README.md @@ -1,3 +1,28 @@ -# README +# luci-app-advanced-reboot -Documentation for this project is available at [https://docs.openwrt.melmac.net/luci-app-advanced-reboot/](https://docs.openwrt.melmac.net/luci-app-advanced-reboot/). +`luci-app-advanced-reboot` is a LuCI (web interface) application for OpenWrt that provides an easy way to reboot your router into an alternative firmware partition (for dual-partition devices) or perform other advanced reboot operations directly from the web UI. + +## Features + +- Detects supported dual-partition devices. +- Displays current and alternative firmware details. +- Allows rebooting into the alternative partition without using SSH. +- Supports switching between OpenWrt and vendor firmware (if present). + +## Installation + +You can install this package from the official OpenWrt package repositories or from the Melmac OpenWrt repository: + +```sh +opkg update +opkg install luci-app-advanced-reboot +``` + +## Documentation + +Full documentation is available at: +[https://docs.openwrt.melmac.ca/luci-app-advanced-reboot/](https://docs.openwrt.melmac.ca/luci-app-advanced-reboot/) + +## License + +This project is licensed under the terms of the GNU General Public License v3.0 (GPL-3.0). diff --git a/applications/luci-app-advanced-reboot/htdocs/luci-static/resources/view/system/advanced_reboot.js b/applications/luci-app-advanced-reboot/htdocs/luci-static/resources/view/system/advanced_reboot.js index 3fdf6c18ec..20c6fccac8 100644 --- a/applications/luci-app-advanced-reboot/htdocs/luci-static/resources/view/system/advanced_reboot.js +++ b/applications/luci-app-advanced-reboot/htdocs/luci-static/resources/view/system/advanced_reboot.js @@ -1,241 +1,380 @@ -'use strict'; -'require view'; -'require rpc'; -'require ui'; -'require uci'; -'require fs'; +"use strict"; +"require view"; +"require rpc"; +"require ui"; +"require uci"; +"require fs"; var pkg = { - get Name() { return 'luci-app-advanced-reboot'; }, - get URL() { return 'https://docs.openwrt.melmac.net/' + pkg.Name + '/'; } + get Name() { + return "luci-app-advanced-reboot"; + }, + get URL() { + return "https://docs.openwrt.melmac.ca/" + pkg.Name + "/"; + }, }; return view.extend({ translateTable: { - NO_BOARD_NAME : function(args) { return _('Unable to find Device Board Name.')}, - NO_DUAL_FLAG: function (args) { return _('Unable to find Dual Boot Flag Partition.') }, - NO_DUAL_FLAG_BLOCK: function (args) { return _('The Dual Boot Flag Partition: %s is not a block device.').format(args[0])}, - ERR_SET_DUAL_FLAG : function(args) { return _('Unable to set Dual Boot Flag Partition entry for partition: %s.').format(args[0])}, - NO_FIRM_ENV : function(args) { return _('Unable to obtain firmware environment variable: %s.').format(args[0])}, - ERR_SET_ENV : function(args) { return _('Unable to set firmware environment variable: %s to %s.').format(args[0],args[1])} + NO_BOARD_NAME: function (args) { + return _("Unable to find Device Board Name."); + }, + NO_DUAL_FLAG: function (args) { + return _("Unable to find Dual Boot Flag Partition."); + }, + NO_DUAL_FLAG_BLOCK: function (args) { + return _( + "The Dual Boot Flag Partition: %s is not a block device." + ).format(args[0]); + }, + ERR_SET_DUAL_FLAG: function (args) { + return _( + "Unable to set Dual Boot Flag Partition entry for partition: %s." + ).format(args[0]); + }, + NO_FIRM_ENV: function (args) { + return _("Unable to obtain firmware environment variable: %s.").format( + args[0] + ); + }, + ERR_SET_ENV: function (args) { + return _("Unable to set firmware environment variable: %s to %s.").format( + args[0], + args[1] + ); + }, }, callReboot: rpc.declare({ - object: 'system', - method: 'reboot', - expect: { result: 0 } + object: "system", + method: "reboot", + expect: { result: 0 }, }), callObtainDeviceInfo: rpc.declare({ - object: 'luci.advanced_reboot', - method: 'obtain_device_info', - expect: { } + object: "luci.advanced_reboot", + method: "obtain_device_info", + expect: {}, }), callTogglePartition: rpc.declare({ - object: 'luci.advanced_reboot', - method: 'toggle_boot_partition', - expect: { } + object: "luci.advanced_reboot", + method: "toggle_boot_partition", + expect: {}, }), - callPowerOff: function() { - return fs.exec('/sbin/poweroff').then(function() { - ui.showModal(_('Shutting down...'), [ - E('p', { 'class': 'spinning' }, _('The system is shutting down now.
DO NOT POWER OFF THE DEVICE!
It might be necessary to renew the address of your computer to reach the device again, depending on your settings.')) + callPowerOff: function () { + return fs.exec("/sbin/poweroff").then(function () { + ui.showModal(_("Shutting down..."), [ + E( + "p", + { class: "spinning" }, + _( + "The system is shutting down now.
DO NOT POWER OFF THE DEVICE!
It might be necessary to renew the address of your computer to reach the device again, depending on your settings." + ) + ), ]); - }) + }); }, - handlePowerOff: function() { - - ui.showModal(_('Power Off Device'), [ - E('p', _("WARNING: Power off might result in a reboot on a device which doesn't support power off.

\ - Click \"Proceed\" below to power off your device.")), - E('div', { 'class': 'right' }, [ - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Cancel')), ' ', - E('button', { - 'class': 'btn cbi-button cbi-button-positive important', - 'click': L.bind(this.callPowerOff, this) - }, _('Proceed')) - ]) + handlePowerOff: function () { + ui.showModal(_("Power Off Device"), [ + E( + "p", + _( + 'WARNING: Power off might result in a reboot on a device which doesn\'t support power off.

\ + Click "Proceed" below to power off your device.' + ) + ), + E("div", { class: "right" }, [ + E( + "button", + { + class: "btn", + click: ui.hideModal, + }, + _("Cancel") + ), + " ", + E( + "button", + { + class: "btn cbi-button cbi-button-positive important", + click: L.bind(this.callPowerOff, this), + }, + _("Proceed") + ), + ]), ]); - }, - handleReboot: function(ev) { - return this.callReboot().then(function(res) { - if (res != 0) { - ui.addNotification(null, E('p', _('The reboot command failed with code %d').format(res))); - L.raise('Error', 'Reboot failed'); - } - - ui.showModal(_('Rebooting…'), [ - E('p', { 'class': 'spinning' }, _('Waiting for device...')) - ]); - - window.setTimeout(function() { - ui.showModal(_('Rebooting…'), [ - E('p', { 'class': 'spinning alert-message warning' }, - _('Device unreachable! Still waiting for device...')) - ]); - }, 150000); - - ui.awaitReconnect(); - }) - .catch(function(e) { ui.addNotification(null, E('p', e.message)) }); - }, - - handleTogglePartition: function(ev) { - return this.callTogglePartition().then(L.bind(function(res) { - if (res.error) { - ui.hideModal() - return ui.addNotification(null, E('p', this.translateTable[res.error](res.args))); - } - - return this.callReboot().then(function(res) { + handleReboot: function (ev) { + return this.callReboot() + .then(function (res) { if (res != 0) { - ui.addNotification(null, E('p', _('The reboot command failed with code %d').format(res))); - L.raise('Error', 'Reboot failed'); + ui.addNotification( + null, + E("p", _("The reboot command failed with code %d").format(res)) + ); + L.raise("Error", "Reboot failed"); } - ui.showModal(_('Rebooting…'), [ - E('p', { 'class': 'spinning' }, _('The system is rebooting to an alternative partition 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.')) + ui.showModal(_("Rebooting…"), [ + E("p", { class: "spinning" }, _("Waiting for device...")), ]); - window.setTimeout(function() { - ui.showModal(_('Rebooting…'), [ - E('p', { 'class': 'spinning alert-message warning' }, - _('Device unreachable! Still waiting for device...')) + window.setTimeout(function () { + ui.showModal(_("Rebooting…"), [ + E( + "p", + { class: "spinning alert-message warning" }, + _("Device unreachable! Still waiting for device...") + ), ]); }, 150000); ui.awaitReconnect(); }) - .catch(function(e) { ui.addNotification(null, E('p', e.message)) }); - }, this)); + .catch(function (e) { + ui.addNotification(null, E("p", e.message)); + }); + }, + + handleTogglePartition: function (ev) { + return this.callTogglePartition().then( + L.bind(function (res) { + if (res.error) { + ui.hideModal(); + return ui.addNotification( + null, + E("p", this.translateTable[res.error](res.args)) + ); + } + + return this.callReboot() + .then(function (res) { + if (res != 0) { + ui.addNotification( + null, + E("p", _("The reboot command failed with code %d").format(res)) + ); + L.raise("Error", "Reboot failed"); + } + + ui.showModal(_("Rebooting…"), [ + E( + "p", + { class: "spinning" }, + _( + "The system is rebooting to an alternative partition 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." + ) + ), + ]); + + window.setTimeout(function () { + ui.showModal(_("Rebooting…"), [ + E( + "p", + { class: "spinning alert-message warning" }, + _("Device unreachable! Still waiting for device...") + ), + ]); + }, 150000); + + ui.awaitReconnect(); + }) + .catch(function (e) { + ui.addNotification(null, E("p", e.message)); + }); + }, this) + ); }, - handleAlternativeReboot: function(ev) { + handleAlternativeReboot: function (ev) { return Promise.all([ - L.resolveDefault(fs.stat('/usr/sbin/fw_printenv'), null), - L.resolveDefault(fs.stat('/usr/sbin/fw_setenv'), null), - ]).then(L.bind(function (data) { - if (!data[0] || !data[1]) { - return ui.addNotification(null, E('p', _('No access to fw_printenv or fw_printenv!'))); - } - - ui.showModal(_('Reboot Device to an Alternative Partition') + " - " + _("Confirm"), [ - E('p', _("WARNING: An alternative partition might have its own settings and completely different firmware.

\ + L.resolveDefault(fs.stat("/usr/sbin/fw_printenv"), null), + L.resolveDefault(fs.stat("/usr/sbin/fw_setenv"), null), + ]).then( + L.bind(function (data) { + if (!data[0] || !data[1]) { + return ui.addNotification( + null, + E("p", _("No access to fw_printenv or fw_printenv!")) + ); + } + + ui.showModal( + _("Reboot Device to an Alternative Partition") + " - " + _("Confirm"), + [ + E( + "p", + _( + 'WARNING: An alternative partition might have its own settings and completely different firmware.

\ As your network configuration and WiFi SSID/password on alternative partition might be different,\ you might have to adjust your computer settings to be able to access your device once it reboots.

\ Please also be aware that alternative partition firmware might not provide an easy way to switch active partition\ and boot back to the currently active partition.

\ - Click \"Proceed\" below to reboot device to an alternative partition.")), - E('div', { 'class': 'right' }, [ - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Cancel')), ' ', - E('button', { - 'class': 'btn cbi-button cbi-button-positive important', - 'click': L.bind(this.handleTogglePartition, this) - }, _('Proceed')) - ]) - ]); - }, this)) + Click "Proceed" below to reboot device to an alternative partition.' + ) + ), + E("div", { class: "right" }, [ + E( + "button", + { + class: "btn", + click: ui.hideModal, + }, + _("Cancel") + ), + " ", + E( + "button", + { + class: "btn cbi-button cbi-button-positive important", + click: L.bind(this.handleTogglePartition, this), + }, + _("Proceed") + ), + ]), + ] + ); + }, this) + ); }, - parsePartitions: function(partitions) { + parsePartitions: function (partitions) { var res = []; - partitions.forEach(L.bind(function(partition) { - var func, text; - - if (partition.state == 'Current') { - func = 'handleReboot'; - text = _('Reboot to current partition'); - } else { - func = 'handleAlternativeReboot'; - text = _('Reboot to alternative partition...'); - } - - res.push([ - (partition.number+0x100).toString(16).substr(-2).toUpperCase(), - _(partition.state), - partition.os.replace("Unknown", _("Unknown")).replace("Compressed", _("Compressed")), - E('button', { - 'class': 'btn cbi-button cbi-button-apply important', - 'click': ui.createHandlerFn(this, func) - }, text) - ]) - }, this)); + partitions.forEach( + L.bind(function (partition) { + var func, text; + + if (partition.state == "Current") { + func = "handleReboot"; + text = _("Reboot to current partition"); + } else { + func = "handleAlternativeReboot"; + text = _("Reboot to alternative partition..."); + } + + res.push([ + (partition.number + 0x100).toString(16).substr(-2).toUpperCase(), + _(partition.state), + partition.os + .replace("Unknown", _("Unknown")) + .replace("Compressed", _("Compressed")), + E( + "button", + { + class: "btn cbi-button cbi-button-apply important", + click: ui.createHandlerFn(this, func), + }, + text + ), + ]); + }, this) + ); return res; }, - load: function() { + load: function () { return Promise.all([ uci.changes(), - L.resolveDefault(fs.stat('/sbin/poweroff'), null), - this.callObtainDeviceInfo() + L.resolveDefault(fs.stat("/sbin/poweroff"), null), + this.callObtainDeviceInfo(), ]); }, - render: function(data) { + render: function (data) { var changes = data[0], poweroff_supported = data[1] != null ? true : false, device_info = data[2]; - var body = E([ - E('h2', _('Advanced Reboot')) - ]); + var body = E([E("h2", _("Advanced Reboot"))]); - for (var config in (changes || {})) { - body.appendChild(E('p', { 'class': 'alert-message warning' }, - _('Warning: There are unsaved changes that will get lost on reboot!'))); + for (var config in changes || {}) { + body.appendChild( + E( + "p", + { class: "alert-message warning" }, + _("Warning: There are unsaved changes that will get lost on reboot!") + ) + ); break; } if (device_info.error) - body.appendChild(E('p', { 'class' : 'alert-message warning'}, _("ERROR: ") + this.translateTable[device_info.error]())); + body.appendChild( + E( + "p", + { class: "alert-message warning" }, + _("ERROR: ") + this.translateTable[device_info.error]() + ) + ); - body.appendChild(E('h3', (device_info.device_name || '') + _(' Partitions'))); + body.appendChild( + E("h3", (device_info.device_name || "") + _(" Partitions")) + ); if (device_info.device_name) { - var partitions_table = E('table', { 'class': 'table' }, [ - E('tr', { 'class': 'tr table-titles' }, [ - E('th', { 'class': 'th' }, [ _('Partition') ]), - E('th', { 'class': 'th' }, [ _('Status') ]), - E('th', { 'class': 'th' }, [ _('Firmware') ]), - E('th', { 'class': 'th' }, [ _('Reboot') ]) - ]) + var partitions_table = E("table", { class: "table" }, [ + E("tr", { class: "tr table-titles" }, [ + E("th", { class: "th" }, [_("Partition")]), + E("th", { class: "th" }, [_("Status")]), + E("th", { class: "th" }, [_("Firmware")]), + E("th", { class: "th" }, [_("Reboot")]), + ]), ]); - cbi_update_table(partitions_table, this.parsePartitions(device_info.partitions)); + cbi_update_table( + partitions_table, + this.parsePartitions(device_info.partitions) + ); body.appendChild(partitions_table); } else { - body.appendChild(E('p', { 'class' : 'alert-message warning'}, - device_info.rom_board_name ? _("Warning: Device (%s) is unknown or isn't a dual-firmware device!" + "%s" + - "If you are seeing this on an OpenWrt dual-firmware supported device," + "%s" + "please refer to " + - "%sHow to add a new device section of the README%s.").format(device_info.rom_board_name, "

", "
", - "", "" ) - : _('Warning: Unable to obtain device information!') - )); - + body.appendChild( + E( + "p", + { class: "alert-message warning" }, + device_info.rom_board_name + ? _( + "Warning: Device (%s) is unknown or isn't a dual-firmware device!" + + "%s" + + "If you are seeing this on an OpenWrt dual-firmware supported device," + + "%s" + + "please refer to " + + "%sHow to add a new device section of the README%s." + ).format( + device_info.rom_board_name, + "

", + "
", + '', + "" + ) + : _("Warning: Unable to obtain device information!") + ) + ); } - body.appendChild(E('hr')); + body.appendChild(E("hr")); body.appendChild( - poweroff_supported ? E('button', { - 'class': 'btn cbi-button cbi-button-apply important', - 'click': ui.createHandlerFn(this, 'handlePowerOff') - }, _('Perform power off...')) - - : E('p', { 'class' : 'alert-message warning'}, - _('Warning: This system does not support powering off!')) + poweroff_supported + ? E( + "button", + { + class: "btn cbi-button cbi-button-apply important", + click: ui.createHandlerFn(this, "handlePowerOff"), + }, + _("Perform power off...") + ) + : E( + "p", + { class: "alert-message warning" }, + _("Warning: This system does not support powering off!") + ) ); return body; @@ -243,5 +382,5 @@ return view.extend({ handleSaveApply: null, handleSave: null, - handleReset: null + handleReset: null, }); diff --git a/applications/luci-app-advanced-reboot/root/usr/libexec/rpcd/luci.advanced_reboot b/applications/luci-app-advanced-reboot/root/usr/libexec/rpcd/luci.advanced_reboot index c79ed77474..af9dd817e3 100755 --- a/applications/luci-app-advanced-reboot/root/usr/libexec/rpcd/luci.advanced_reboot +++ b/applications/luci-app-advanced-reboot/root/usr/libexec/rpcd/luci.advanced_reboot @@ -1,13 +1,23 @@ #!/bin/sh -# Copyright 2017-2020 Stan Grishin (stangri@melmac.ca) +# Copyright 2017-2025 Stan Grishin (stangri@melmac.ca) # shellcheck disable=SC2039,SC1091,SC3043,SC3057,SC3060 + +# TechRef: https://openwrt.org/docs/techref/rpcd +# TESTS +# ubus -v list luci.advanced_reboot +# ubus -S call luci.advanced_reboot obtain_device_info '{"name": "advanced-reboot" }' +# ubus -S call luci.advanced_reboot toggle_boot_partition '{"name": "advanced-reboot" }' + readonly devices_dir="/usr/share/advanced-reboot/devices/" . /lib/functions.sh . /usr/share/libubox/jshn.sh -logger() { /usr/bin/logger -t advanced-reboot "$1"; } +packageName='advanced-reboot' +debug() { local i j; for i in "$@"; do eval "j=\$$i"; logger "${packageName:+-t $packageName}" "${i}: ${j} "; done; } + +logger() { /usr/bin/logger -t advanced-reboot "$*"; } is_present() { command -v "$1" >/dev/null 2>&1; } is_alt_mountable() { @@ -25,29 +35,29 @@ is_alt_mountable() { alt_partition_mount() { local ubi_dev op_ubi="$1" ubi_vol="${2:-0}" mkdir -p /var/alt_rom - ubi_dev="$(ubiattach -m "$op_ubi")" + ubi_dev="$(ubiattach -m "$op_ubi" 2>/dev/null)" ubi_dev="$(echo "$ubi_dev" | sed -n "s/^UBI device number\s*\(\d*\),.*$/\1/p")" if [ -z "$ubi_dev" ]; then - ubidetach -m "$op_ubi" + ubidetach -m "$op_ubi" >/dev/null 2>&1 return 1 fi - ubiblock --create "/dev/ubi${ubi_dev}_${ubi_vol}" && \ - mount -t squashfs -r "/dev/ubiblock${ubi_dev}_${ubi_vol}" /var/alt_rom + ubiblock --create "/dev/ubi${ubi_dev}_${ubi_vol}" >/dev/null 2>&1 && \ + mount -t squashfs -r "/dev/ubiblock${ubi_dev}_${ubi_vol}" /var/alt_rom >/dev/null 2>&1 } alt_partition_unmount() { local mtdCount i=0 op_ubi="$1" ubi_vol="${2:-0}" mtdCount="$(ubinfo | grep 'Present UBI devices' | tr ',' '\n' | grep -c 'ubi')" [ -z "$mtdCount" ] && mtdCount=10 - [ -d /var/alt_rom ] && umount /var/alt_rom + grep -qs '/var/alt_rom ' /proc/mounts && umount /var/alt_rom while [ "$i" -le "$mtdCount" ]; do if [ ! -e "/sys/devices/virtual/ubi/ubi${i}/mtd_num" ]; then break fi ubi_mtd="$(cat /sys/devices/virtual/ubi/ubi${i}/mtd_num)" if [ -n "$ubi_mtd" ] && [ "$ubi_mtd" = "$op_ubi" ]; then - ubiblock --remove /dev/ubi${i}_${ubi_vol} - ubidetach -m "$op_ubi" + ubiblock --remove "/dev/ubi${i}_${ubi_vol}" >/dev/null 2>&1 + ubidetach -m "$op_ubi" >/dev/null 2>&1 rm -rf /var/alt_rom fi i=$((i + 1)) @@ -71,6 +81,7 @@ get_alt_partition_os_info(){ alt_partition_unmount "$op_ubi" "$ubi_vol" alt_partition_mount "$op_ubi" "$ubi_vol" if [ -s "/var/alt_rom/etc/os-release" ]; then +# shellcheck disable=SC2031 op_info="$(. /var/alt_rom/etc/os-release && echo "$PRETTY_NAME")" if [ "${op_info//SNAPSHOT}" != "$op_info" ]; then op_info="$(. /var/alt_rom/etc/os-release && echo "${OPENWRT_RELEASE%%-*}")" @@ -134,7 +145,7 @@ obtain_device_info(){ if [ -n "$labelOffset" ]; then if [ -n "$partition1MTD" ]; then - p1_label="$(dd if="/dev/${partition1MTD}" bs=1 skip="${labelOffset}" count=64 2>/dev/null)" + p1_label="$(dd if="/dev/${partition1MTD}" bs=1 skip="${labelOffset}" count=64 2>/dev/null | tr -d '\0')" 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 @@ -149,7 +160,7 @@ obtain_device_info(){ fi if [ -n "$partition2MTD" ]; then - p2_label="$(dd if="/dev/${partition2MTD}" bs=1 skip="${labelOffset}" count=64 2>/dev/null)" + p2_label="$(dd if="/dev/${partition2MTD}" bs=1 skip="${labelOffset}" count=64 2>/dev/null | tr -d '\0')" 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 @@ -192,13 +203,19 @@ obtain_device_info(){ if is_alt_mountable "$partition1MTD" "$partition2MTD"; then opOffset="${opOffset:-1}" ubiVolume="${ubiVolume:-0}" + # Robustly extract numeric MTD indices (handles mtd2, mtd22, mtd22ro, etc.) + local p1num p2num + p1num="${partition1MTD#mtd}"; p1num="${p1num%%[^0-9]*}" + p2num="${partition2MTD#mtd}"; p2num="${p2num%%[^0-9]*}" + [ -n "$p1num" ] || p1num=0 + [ -n "$p2num" ] || p2num=0 if [ "$current_partition" = "$bootEnv1Partition1Value" ]; then - op_ubi=$(( ${partition2MTD:3:3} + $opOffset )) + op_ubi=$(( p2num + opOffset )) else - op_ubi=$(( ${partition1MTD:3:3} + $opOffset )) + op_ubi=$(( p1num + opOffset )) fi - cp_info="$(get_main_partition_os_info $op_ubi)" - op_info="$(get_alt_partition_os_info $op_ubi $vendorName $ubiVolume)" + cp_info="$(get_main_partition_os_info "$op_ubi")" + op_info="$(get_alt_partition_os_info "$op_ubi" "$vendorName" "$ubiVolume")" if [ "$current_partition" = "$bootEnv1Partition1Value" ]; then p1_os="${cp_info:-$p1_os}" p2_os="${op_info:-$p2_os}" @@ -336,7 +353,7 @@ toggle_boot_partition(){ json_add_string "$newEnvSetting" json_close_array json_add_string 'rom_board_name' "$romBoardName" - json_dump; json_cleanup; + json_dump >&4; json_cleanup; return fi fi @@ -378,7 +395,7 @@ toggle_boot_partition(){ fi fi json_init - json_dump; json_cleanup; + json_dump >&4; json_cleanup; fi } diff --git a/applications/luci-app-advanced-reboot/root/usr/share/advanced-reboot/devices/linksys-mr7350.json b/applications/luci-app-advanced-reboot/root/usr/share/advanced-reboot/devices/linksys-mr7350.json new file mode 100644 index 0000000000..1d1fc862b1 --- /dev/null +++ b/applications/luci-app-advanced-reboot/root/usr/share/advanced-reboot/devices/linksys-mr7350.json @@ -0,0 +1,14 @@ +{ + "vendorName": "Linksys", + "deviceName": "MR7350", + "boardNames": [ "linksys,mr7350" ], + "partition1MTD": "mtd14", + "partition2MTD": "mtd16", + "labelOffset": 192, + "bootEnv1": "boot_part", + "bootEnv1Partition1Value": 1, + "bootEnv1Partition2Value": 2, + "bootEnv2": null, + "bootEnv2Partition1Value": null, + "bootEnv2Partition2Value": null +} \ No newline at end of file