Merge pull request #464 from remakeelectric/pulls/list-operations
[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 fp then
88                                 if chunk then
89                                         fp:write(chunk)
90                                 end
91                                 if eof then
92                                         fp:close()
93                                 end
94                         end
95                 end
96         )
97
98         if luci.http.formvalue("image") or luci.http.formvalue("step") then
99                 --
100                 -- Initiate firmware flash
101                 --
102                 local step = tonumber(luci.http.formvalue("step") or 1)
103                 if step == 1 then
104                         if image_supported() then
105                                 luci.template.render("failsafe/upgrade", {
106                                         checksum = image_checksum(),
107                                         storage  = storage_size(),
108                                         size     = (fs.stat(image_tmp, "size") or 0),
109                                         keep     = false
110                                 })
111                         else
112                                 fs.unlink(image_tmp)
113                                 luci.template.render("failsafe/flashops", {
114                                         reset_avail   = reset_avail,
115                                         upgrade_avail = upgrade_avail,
116                                         image_invalid = true
117                                 })
118                         end
119                 --
120                 -- Start sysupgrade flash
121                 --
122                 elseif step == 2 then
123                         local keep = (luci.http.formvalue("keep") == "1") and "" or "-n"
124                         luci.template.render("failsafe/applyreboot", {
125                                 title = luci.i18n.translate("Flashing..."),
126                                 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."),
127                                 addr  = (#keep > 0) and "192.168.1.1" or nil
128                         })
129                         fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
130                 end
131         else
132                 --
133                 -- Overview
134                 --
135                 luci.template.render("failsafe/flashops", {
136                         reset_avail   = reset_avail,
137                         upgrade_avail = upgrade_avail
138                 })
139         end
140 end
141
142 function action_reboot()
143         local reboot = luci.http.formvalue("reboot")
144         luci.template.render("failsafe/reboot", {reboot=reboot})
145         if reboot then
146                 luci.sys.reboot()
147         end
148 end
149
150 function fork_exec(command)
151         local pid = nixio.fork()
152         if pid > 0 then
153                 return
154         elseif pid == 0 then
155                 -- change to root dir
156                 nixio.chdir("/")
157
158                 -- patch stdin, out, err to /dev/null
159                 local null = nixio.open("/dev/null", "w+")
160                 if null then
161                         nixio.dup(null, nixio.stderr)
162                         nixio.dup(null, nixio.stdout)
163                         nixio.dup(null, nixio.stdin)
164                         if null:fileno() > 2 then
165                                 null:close()
166                         end
167                 end
168
169                 -- replace with target command
170                 nixio.exec("/bin/sh", "-c", command)
171         end
172 end
173
174 function ltn12_popen(command)
175
176         local fdi, fdo = nixio.pipe()
177         local pid = nixio.fork()
178
179         if pid > 0 then
180                 fdo:close()
181                 local close
182                 return function()
183                         local buffer = fdi:read(2048)
184                         local wpid, stat = nixio.waitpid(pid, "nohang")
185                         if not close and wpid and stat == "exited" then
186                                 close = true
187                         end
188
189                         if buffer and #buffer > 0 then
190                                 return buffer
191                         elseif close then
192                                 fdi:close()
193                                 return nil
194                         end
195                 end
196         elseif pid == 0 then
197                 nixio.dup(fdo, nixio.stdout)
198                 fdi:close()
199                 fdo:close()
200                 nixio.exec("/bin/sh", "-c", command)
201         end
202 end