From 1dbb1da4d9a4ca24d92fe0fdc78e90ce0261e450 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 20 Sep 2012 02:16:51 +0000 Subject: [PATCH] modules: add failsafe module The failsafe module adds a minimal web-interface which is intended to run on a very compact initramfs-based image intended only for flashing firmware onto a device. This can be used as part of an installation method or reside in a small partition on the device flash which can be booted in case the checksum-test of the main kernel or rootfs fails. Signed-off-by: Daniel Golle --- contrib/package/luci/Makefile | 1 + modules/failsafe/Makefile | 2 + .../luasrc/controller/failsafe/failsafe.lua | 211 +++++++++++++++++++++ .../failsafe/luasrc/view/failsafe/applyreboot.htm | 50 +++++ modules/failsafe/luasrc/view/failsafe/flashops.htm | 43 +++++ modules/failsafe/luasrc/view/failsafe/reboot.htm | 26 +++ modules/failsafe/luasrc/view/failsafe/upgrade.htm | 62 ++++++ 7 files changed, 395 insertions(+) create mode 100644 modules/failsafe/Makefile create mode 100644 modules/failsafe/luasrc/controller/failsafe/failsafe.lua create mode 100644 modules/failsafe/luasrc/view/failsafe/applyreboot.htm create mode 100644 modules/failsafe/luasrc/view/failsafe/flashops.htm create mode 100644 modules/failsafe/luasrc/view/failsafe/reboot.htm create mode 100644 modules/failsafe/luasrc/view/failsafe/upgrade.htm diff --git a/contrib/package/luci/Makefile b/contrib/package/luci/Makefile index d3b0cc7b3..9c0ca745e 100644 --- a/contrib/package/luci/Makefile +++ b/contrib/package/luci/Makefile @@ -280,6 +280,7 @@ endef $(eval $(call module,admin-core,Web UI Core module,+luci-lib-web +luci-proto-core +luci-i18n-english)) $(eval $(call module,admin-mini,LuCI Essentials - stripped down and user-friendly,+luci-mod-admin-core @BROKEN)) $(eval $(call module,admin-full,LuCI Administration - full-featured for full control,+luci-mod-admin-core +luci-lib-ipkg)) +$(eval $(call module,failsafe,LuCI Fail-Safe - Fail-Safe sysupgrade module,+luci-mod-admin-core)) $(eval $(call module,rpc,LuCI RPC - JSON-RPC API,+luci-lib-json)) $(eval $(call module,freifunk,LuCI Freifunk module,+luci-mod-admin-full +luci-lib-json +PACKAGE_luci-mod-freifunk:freifunk-firewall +freifunk-common +community-profiles)) $(eval $(call module,niu,NIU - Next Generation Interface,+luci-mod-admin-core @BROKEN)) diff --git a/modules/failsafe/Makefile b/modules/failsafe/Makefile new file mode 100644 index 000000000..81a96f6a8 --- /dev/null +++ b/modules/failsafe/Makefile @@ -0,0 +1,2 @@ +include ../../build/config.mk +include ../../build/module.mk \ No newline at end of file diff --git a/modules/failsafe/luasrc/controller/failsafe/failsafe.lua b/modules/failsafe/luasrc/controller/failsafe/failsafe.lua new file mode 100644 index 000000000..c37fef597 --- /dev/null +++ b/modules/failsafe/luasrc/controller/failsafe/failsafe.lua @@ -0,0 +1,211 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth +Copyright 2008-2011 Jo-Philipp Wich +Copyright 2012 Daniel Golle + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.failsafe.failsafe", package.seeall) + +function index() + local root = node() + if not root.target then + root.target = alias("failsafe") + root.index = true + end + + page = node() + page.lock = true + page.target = alias("failsafe") + page.subindex = true + page.index = false + + page = node("failsafe") + page.title = _("Fail-safe") + page.target = alias("failsafe", "flashops") + page.order = 5 + page.setuser = "root" + page.setgroup = "root" + page.index = true + + entry({"failsafe", "flashops"}, call("action_flashops"), _("Flash Firmware"), 70).index = true + entry({"failsafe", "reboot"}, call("action_reboot"), _("Reboot"), 90) +end + +function action_flashops() + local sys = require "luci.sys" + local fs = require "luci.fs" + + local upgrade_avail = nixio.fs.access("/lib/upgrade/platform.sh") + local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0 + + local image_tmp = "/tmp/firmware.img" + + local function image_supported() + -- XXX: yay... + return ( 0 == os.execute( + ". /etc/functions.sh; " .. + "include /lib/upgrade; " .. + "platform_check_image %q >/dev/null" + % image_tmp + ) ) + end + + local function image_checksum() + return (luci.sys.exec("md5sum %q" % image_tmp):match("^([^%s]+)")) + end + + local function storage_size() + local size = 0 + if nixio.fs.access("/proc/mtd") then + for l in io.lines("/proc/mtd") do + local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"') + if n == "linux" or n == "firmware" then + size = tonumber(s, 16) + break + end + end + elseif nixio.fs.access("/proc/partitions") then + for l in io.lines("/proc/partitions") do + local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)') + if b and n and not n:match('[0-9]') then + size = tonumber(b) * 1024 + break + end + end + end + return size + end + + + local fp + luci.http.setfilehandler( + function(meta, chunk, eof) + if not fp then + if meta and meta.name == "image" then + fp = io.open(image_tmp, "w") + end + end + if chunk then + fp:write(chunk) + end + if eof then + fp:close() + end + end + ) + + if luci.http.formvalue("image") or luci.http.formvalue("step") then + -- + -- Initiate firmware flash + -- + local step = tonumber(luci.http.formvalue("step") or 1) + if step == 1 then + if image_supported() then + luci.template.render("failsafe/upgrade", { + checksum = image_checksum(), + storage = storage_size(), + size = nixio.fs.stat(image_tmp).size, + keep = false + }) + else + nixio.fs.unlink(image_tmp) + luci.template.render("failsafe/flashops", { + reset_avail = reset_avail, + upgrade_avail = upgrade_avail, + image_invalid = true + }) + end + -- + -- Start sysupgrade flash + -- + elseif step == 2 then + local keep = (luci.http.formvalue("keep") == "1") and "" or "-n" + luci.template.render("failsafe/applyreboot", { + title = luci.i18n.translate("Flashing..."), + msg = luci.i18n.translate("The system is flashing now.
DO NOT POWER OFF THE DEVICE!
Wait a few minutes until 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 "192.168.1.1" or nil + }) + fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp }) + end + else + -- + -- Overview + -- + luci.template.render("failsafe/flashops", { + reset_avail = reset_avail, + upgrade_avail = upgrade_avail + }) + end +end + +function action_reboot() + local reboot = luci.http.formvalue("reboot") + luci.template.render("failsafe/reboot", {reboot=reboot}) + if reboot then + luci.sys.reboot() + end +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 + +function ltn12_popen(command) + + local fdi, fdo = nixio.pipe() + local pid = nixio.fork() + + if pid > 0 then + fdo:close() + local close + return function() + local buffer = fdi:read(2048) + local wpid, stat = nixio.waitpid(pid, "nohang") + if not close and wpid and stat == "exited" then + close = true + end + + if buffer and #buffer > 0 then + return buffer + elseif close then + fdi:close() + return nil + end + end + elseif pid == 0 then + nixio.dup(fdo, nixio.stdout) + fdi:close() + fdo:close() + nixio.exec("/bin/sh", "-c", command) + end +end diff --git a/modules/failsafe/luasrc/view/failsafe/applyreboot.htm b/modules/failsafe/luasrc/view/failsafe/applyreboot.htm new file mode 100644 index 000000000..866d213f0 --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/applyreboot.htm @@ -0,0 +1,50 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> + + + + <%=luci.sys.hostname()%> - <% if title then %><%=title%><% else %><%:Rebooting...%><% end %> + + + + + +
+
+

<%:System%> - <% if title then %><%=title%><% else %><%:Rebooting...%><% end %>

+
+

+ <% if msg then %><%=msg%><% else %><%:Changes applied.%><% end %> +

+

+ <%:Loading%> + <%:Waiting for changes to be applied...%> +

+
+
+
+ + diff --git a/modules/failsafe/luasrc/view/failsafe/flashops.htm b/modules/failsafe/luasrc/view/failsafe/flashops.htm new file mode 100644 index 000000000..accbba04b --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/flashops.htm @@ -0,0 +1,43 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich +Copyright 2012 Daniel Golle + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> + +<%+header%> + +

<%:Flash operations%>

+
+ <%:Flash new firmware image%> + <% if upgrade_avail then %> +
+
<%:Upload a sysupgrade-compatible image. %>
+
+
+ +
+ + +
+
+
+ <% if image_invalid then %> +
<%:The uploaded image file does not contain a supported format. Make sure that you choose the generic image format for your platform. %>
+ <% end %> +
+ <% else %> +
<%:Sorry, there is no sysupgrade support present, a new firmware image must be flashed manually. Please refer to the OpenWrt wiki for device specific install instructions.%>
+ <% end %> +
+ +<%+footer%> diff --git a/modules/failsafe/luasrc/view/failsafe/reboot.htm b/modules/failsafe/luasrc/view/failsafe/reboot.htm new file mode 100644 index 000000000..e2e4db035 --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/reboot.htm @@ -0,0 +1,26 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich +Copyright 2012 Daniel Golle + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> +<%+header%> +

<%:System%>

+

<%:Reboot%>

+

<%:Reboots the operating system of your device%>

+<%- if not reboot then -%> +

<%:Perform reboot%>

+<%- else -%> +

<%:Please wait: Device rebooting...%>

+ +<%- end -%> +<%+footer%> diff --git a/modules/failsafe/luasrc/view/failsafe/upgrade.htm b/modules/failsafe/luasrc/view/failsafe/upgrade.htm new file mode 100644 index 000000000..66f645762 --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/upgrade.htm @@ -0,0 +1,62 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008-2009 Jo-Philipp Wich +Copyright 2012 Daniel Golle + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> + +<%+header%> + +

<%:Flash Firmware%> - <%:Verify%>

+

+ <%_ The flash image was uploaded. + Below is the checksum and file size listed, + compare them with the original file to ensure data integrity.
+ Click "Proceed" below to start the flash procedure. %> + + <% if storage > 0 and size > storage then %> +

+

<%:It appears that you try to + flash an image that does not fit into the flash memory, please verify + the image file! %>
+ <% end %> + +

+ +
+
    +
  • <%:Checksum%>: <%=checksum%>
  • +
  • <%:Size%>: <% + local w = require "luci.tools.webadmin" + write(w.byte_format(size)) + + if storage > 0 then + write(luci.i18n.translatef( + " (%s available)", + w.byte_format(storage) + )) + end + %>
  • +
+
+ +
+
+ +
+
+ + +
+
+ +<%+footer%> -- 2.11.0