c37fef597c9c8fb2a873e011521d2e1b254d0aab
[project/luci.git] / modules / failsafe / luasrc / controller / failsafe / failsafe.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008-2011 Jo-Philipp Wich <xm@subsignal.org>
6 Copyright 2012 Daniel Golle <dgolle@allnet.de>
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12 http://www.apache.org/licenses/LICENSE-2.0
13
14 $Id$
15 ]]--
16
17 module("luci.controller.failsafe.failsafe", package.seeall)
18
19 function index()
20 local root = node()
21 if not root.target then
22 root.target = alias("failsafe")
23 root.index = true
24 end
25
26 page = node()
27 page.lock = true
28 page.target = alias("failsafe")
29 page.subindex = true
30 page.index = false
31
32 page = node("failsafe")
33 page.title = _("Fail-safe")
34 page.target = alias("failsafe", "flashops")
35 page.order = 5
36 page.setuser = "root"
37 page.setgroup = "root"
38 page.index = true
39
40 entry({"failsafe", "flashops"}, call("action_flashops"), _("Flash Firmware"), 70).index = true
41 entry({"failsafe", "reboot"}, call("action_reboot"), _("Reboot"), 90)
42 end
43
44 function action_flashops()
45 local sys = require "luci.sys"
46 local fs = require "luci.fs"
47
48 local upgrade_avail = nixio.fs.access("/lib/upgrade/platform.sh")
49 local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0
50
51 local image_tmp = "/tmp/firmware.img"
52
53 local function image_supported()
54 -- XXX: yay...
55 return ( 0 == os.execute(
56 ". /etc/functions.sh; " ..
57 "include /lib/upgrade; " ..
58 "platform_check_image %q >/dev/null"
59 % image_tmp
60 ) )
61 end
62
63 local function image_checksum()
64 return (luci.sys.exec("md5sum %q" % image_tmp):match("^([^%s]+)"))
65 end
66
67 local function storage_size()
68 local size = 0
69 if nixio.fs.access("/proc/mtd") then
70 for l in io.lines("/proc/mtd") do
71 local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
72 if n == "linux" or n == "firmware" then
73 size = tonumber(s, 16)
74 break
75 end
76 end
77 elseif nixio.fs.access("/proc/partitions") then
78 for l in io.lines("/proc/partitions") do
79 local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
80 if b and n and not n:match('[0-9]') then
81 size = tonumber(b) * 1024
82 break
83 end
84 end
85 end
86 return size
87 end
88
89
90 local fp
91 luci.http.setfilehandler(
92 function(meta, chunk, eof)
93 if not fp then
94 if meta and meta.name == "image" then
95 fp = io.open(image_tmp, "w")
96 end
97 end
98 if chunk then
99 fp:write(chunk)
100 end
101 if eof then
102 fp:close()
103 end
104 end
105 )
106
107 if luci.http.formvalue("image") or luci.http.formvalue("step") then
108 --
109 -- Initiate firmware flash
110 --
111 local step = tonumber(luci.http.formvalue("step") or 1)
112 if step == 1 then
113 if image_supported() then
114 luci.template.render("failsafe/upgrade", {
115 checksum = image_checksum(),
116 storage = storage_size(),
117 size = nixio.fs.stat(image_tmp).size,
118 keep = false
119 })
120 else
121 nixio.fs.unlink(image_tmp)
122 luci.template.render("failsafe/flashops", {
123 reset_avail = reset_avail,
124 upgrade_avail = upgrade_avail,
125 image_invalid = true
126 })
127 end
128 --
129 -- Start sysupgrade flash
130 --
131 elseif step == 2 then
132 local keep = (luci.http.formvalue("keep") == "1") and "" or "-n"
133 luci.template.render("failsafe/applyreboot", {
134 title = luci.i18n.translate("Flashing..."),
135 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."),
136 addr = (#keep > 0) and "192.168.1.1" or nil
137 })
138 fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
139 end
140 else
141 --
142 -- Overview
143 --
144 luci.template.render("failsafe/flashops", {
145 reset_avail = reset_avail,
146 upgrade_avail = upgrade_avail
147 })
148 end
149 end
150
151 function action_reboot()
152 local reboot = luci.http.formvalue("reboot")
153 luci.template.render("failsafe/reboot", {reboot=reboot})
154 if reboot then
155 luci.sys.reboot()
156 end
157 end
158
159 function fork_exec(command)
160 local pid = nixio.fork()
161 if pid > 0 then
162 return
163 elseif pid == 0 then
164 -- change to root dir
165 nixio.chdir("/")
166
167 -- patch stdin, out, err to /dev/null
168 local null = nixio.open("/dev/null", "w+")
169 if null then
170 nixio.dup(null, nixio.stderr)
171 nixio.dup(null, nixio.stdout)
172 nixio.dup(null, nixio.stdin)
173 if null:fileno() > 2 then
174 null:close()
175 end
176 end
177
178 -- replace with target command
179 nixio.exec("/bin/sh", "-c", command)
180 end
181 end
182
183 function ltn12_popen(command)
184
185 local fdi, fdo = nixio.pipe()
186 local pid = nixio.fork()
187
188 if pid > 0 then
189 fdo:close()
190 local close
191 return function()
192 local buffer = fdi:read(2048)
193 local wpid, stat = nixio.waitpid(pid, "nohang")
194 if not close and wpid and stat == "exited" then
195 close = true
196 end
197
198 if buffer and #buffer > 0 then
199 return buffer
200 elseif close then
201 fdi:close()
202 return nil
203 end
204 end
205 elseif pid == 0 then
206 nixio.dup(fdo, nixio.stdout)
207 fdi:close()
208 fdo:close()
209 nixio.exec("/bin/sh", "-c", command)
210 end
211 end