diff options
author | Daniel Golle <daniel@makrotopia.org> | 2012-09-20 02:16:51 +0000 |
---|---|---|
committer | Daniel Golle <daniel@makrotopia.org> | 2012-09-20 02:16:51 +0000 |
commit | 1dbb1da4d9a4ca24d92fe0fdc78e90ce0261e450 (patch) | |
tree | 5c0afcc8e66675c76378e57c392d35bf352cda9a /modules | |
parent | 45d812dbb7b6374cd4188925661b74723cef91a7 (diff) |
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 <dgolle@allnet.de>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/failsafe/Makefile | 2 | ||||
-rw-r--r-- | modules/failsafe/luasrc/controller/failsafe/failsafe.lua | 211 | ||||
-rw-r--r-- | modules/failsafe/luasrc/view/failsafe/applyreboot.htm | 50 | ||||
-rw-r--r-- | modules/failsafe/luasrc/view/failsafe/flashops.htm | 43 | ||||
-rw-r--r-- | modules/failsafe/luasrc/view/failsafe/reboot.htm | 26 | ||||
-rw-r--r-- | modules/failsafe/luasrc/view/failsafe/upgrade.htm | 62 |
6 files changed, 394 insertions, 0 deletions
diff --git a/modules/failsafe/Makefile b/modules/failsafe/Makefile new file mode 100644 index 0000000000..81a96f6a83 --- /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 0000000000..c37fef597c --- /dev/null +++ b/modules/failsafe/luasrc/controller/failsafe/failsafe.lua @@ -0,0 +1,211 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth <steven@midlink.org> +Copyright 2008-2011 Jo-Philipp Wich <xm@subsignal.org> +Copyright 2012 Daniel Golle <dgolle@allnet.de> + +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.<br /> DO NOT POWER OFF THE DEVICE!<br /> 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 0000000000..866d213f05 --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/applyreboot.htm @@ -0,0 +1,50 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth <steven@midlink.org> +Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> + +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$ + +-%> + +<html> + <head> + <title><%=luci.sys.hostname()%> - <% if title then %><%=title%><% else %><%:Rebooting...%><% end %></title> + <link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" /> + <script type="text/javascript" src="<%=resource%>/xhr.js"></script> + <script type="text/javascript">//<![CDATA[ + var interval = window.setInterval(function() { + var img = new Image(); + + img.onload = function() { + window.clearInterval(interval); + location.href = 'http://<%=addr or luci.http.getenv("SERVER_NAME")%>/'; + }; + + img.src = 'http://<%=addr or luci.http.getenv("SERVER_NAME")%><%=resource%>/icons/loading.gif?' + Math.random(); + }, 5000); + //]]></script> + </head> + <body> + <div id="maincontainer"> + <div id="maincontent"> + <h2><a id="content" name="content"><%:System%> - <% if title then %><%=title%><% else %><%:Rebooting...%><% end %></a></h2> + <fieldset class="cbi-section"> + <p> + <% if msg then %><%=msg%><% else %><%:Changes applied.%><% end %> + </p> + <p> + <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> + <%:Waiting for changes to be applied...%> + </p> + </fieldset> + </div> + </div> + </body> +</html> diff --git a/modules/failsafe/luasrc/view/failsafe/flashops.htm b/modules/failsafe/luasrc/view/failsafe/flashops.htm new file mode 100644 index 0000000000..accbba04ba --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/flashops.htm @@ -0,0 +1,43 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth <steven@midlink.org> +Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> +Copyright 2012 Daniel Golle <dgolle@allnet.de> + +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%> + +<h2><a id="content" name="content"><%:Flash operations%></a></h2> +<fieldset class="cbi-section"> + <legend><%:Flash new firmware image%></legend> + <% if upgrade_avail then %> + <form method="post" action="<%=REQUEST_URI%>" enctype="multipart/form-data"> + <div class="cbi-section-descr"><%:Upload a sysupgrade-compatible image. %></div> + <div class="cbi-section-node"> + <div class="cbi-value cbi-value-last<% if image_invalid then %> cbi-value-error<% end %>"> + <label class="cbi-value-title" for="image"><%:Image%>:</label> + <div class="cbi-value-field"> + <input type="file" name="image" id="image" /> + <input type="submit" class="cbi-button cbi-input-apply" value="<%:Flash image...%>" /> + </div> + </div> + </div> + <% if image_invalid then %> + <div class="cbi-section-error"><%:The uploaded image file does not contain a supported format. Make sure that you choose the generic image format for your platform. %></div> + <% end %> + </form> + <% else %> + <div class="cbi-section-descr"><%: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.%></div> + <% end %> +</fieldset> + +<%+footer%> diff --git a/modules/failsafe/luasrc/view/failsafe/reboot.htm b/modules/failsafe/luasrc/view/failsafe/reboot.htm new file mode 100644 index 0000000000..e2e4db035b --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/reboot.htm @@ -0,0 +1,26 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth <steven@midlink.org> +Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> +Copyright 2012 Daniel Golle <dgolle@allnet.de> + +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%> +<h2><a id="content" name="content"><%:System%></a></h2> +<h3><%:Reboot%></h3> +<p><%:Reboots the operating system of your device%></p> +<%- if not reboot then -%> +<p><a href="<%=controller%>/failsafe/reboot?reboot=1"><%:Perform reboot%></a></p> +<%- else -%> +<p><%:Please wait: Device rebooting...%></p> +<script type="text/javascript">setTimeout("location='<%=controller%>'", 60000)</script> +<%- 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 0000000000..66f6457626 --- /dev/null +++ b/modules/failsafe/luasrc/view/failsafe/upgrade.htm @@ -0,0 +1,62 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth <steven@midlink.org> +Copyright 2008-2009 Jo-Philipp Wich <xm@subsignal.org> +Copyright 2012 Daniel Golle <dgolle@allnet.de> + +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%> + +<h2><a id="content" name="content"><%:Flash Firmware%> - <%:Verify%></a></h2> +<p> + <%_ The flash image was uploaded. + Below is the checksum and file size listed, + compare them with the original file to ensure data integrity.<br /> + Click "Proceed" below to start the flash procedure. %> + + <% if storage > 0 and size > storage then %> + <br /><br /> + <div class="error"><%:It appears that you try to + flash an image that does not fit into the flash memory, please verify + the image file! %></div> + <% end %> + +</p> + +<fieldset class="cbi-section"> + <ul> + <li><%:Checksum%>: <code><%=checksum%></code></li> + <li><%: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 + %></li> + </ul> +</fieldset> + +<div class="cbi-page-actions right"> + <form style="display:inline" action="<%=REQUEST_URI%>" method="post"> + <input class="cbi-button cbi-button-reset" type="submit" value="<%:Cancel%>" /> + </form> + <form style="display:inline" action="<%=REQUEST_URI%>" method="post"> + <input type="hidden" name="step" value="2" /> + <input class="cbi-button cbi-button-apply" type="submit" value="<%:Proceed%>" /> + </form> +</div> + +<%+footer%> |