db4bda6605492381e85eb15d92d3d497fd843441
[project/luci.git] / modules / luci-mod-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                         ". /lib/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 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."),
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