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