mirror of
				https://github.com/openwrt/luci.git
				synced 2025-10-31 13:18:04 +00:00 
			
		
		
		
	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 <stangri@melmac.ca>
		
			
				
	
	
		
			387 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "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.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]
 | |
| 			);
 | |
| 		},
 | |
| 	},
 | |
| 
 | |
| 	callReboot: rpc.declare({
 | |
| 		object: "system",
 | |
| 		method: "reboot",
 | |
| 		expect: { result: 0 },
 | |
| 	}),
 | |
| 
 | |
| 	callObtainDeviceInfo: rpc.declare({
 | |
| 		object: "luci.advanced_reboot",
 | |
| 		method: "obtain_device_info",
 | |
| 		expect: {},
 | |
| 	}),
 | |
| 
 | |
| 	callTogglePartition: rpc.declare({
 | |
| 		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.<br /> DO NOT POWER OFF THE DEVICE!<br /> 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.<br /><br />\
 | |
| 			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) {
 | |
| 						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.<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."
 | |
| 								)
 | |
| 							),
 | |
| 						]);
 | |
| 
 | |
| 						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) {
 | |
| 		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.<br /><br />\
 | |
| 				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.<br /><br />\
 | |
| 				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.<br /><br />\
 | |
| 				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) {
 | |
| 		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)
 | |
| 		);
 | |
| 
 | |
| 		return res;
 | |
| 	},
 | |
| 
 | |
| 	load: function () {
 | |
| 		return Promise.all([
 | |
| 			uci.changes(),
 | |
| 			L.resolveDefault(fs.stat("/sbin/poweroff"), null),
 | |
| 			this.callObtainDeviceInfo(),
 | |
| 		]);
 | |
| 	},
 | |
| 
 | |
| 	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"))]);
 | |
| 
 | |
| 		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("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")]),
 | |
| 				]),
 | |
| 			]);
 | |
| 
 | |
| 			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,
 | |
| 								"<br /><br />",
 | |
| 								"<br />",
 | |
| 								'<a href="' +
 | |
| 									pkg.URL +
 | |
| 									'#how-to-add-a-new-device" target="_blank">',
 | |
| 								"</a>"
 | |
| 						  )
 | |
| 						: _("Warning: Unable to obtain device information!")
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		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!")
 | |
| 				  )
 | |
| 		);
 | |
| 
 | |
| 		return body;
 | |
| 	},
 | |
| 
 | |
| 	handleSaveApply: null,
 | |
| 	handleSave: null,
 | |
| 	handleReset: null,
 | |
| });
 |