modules/admin-full: fixes for upgrade pages:
[project/luci.git] / modules / admin-full / luasrc / controller / admin / system.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008-2009 Jo-Philipp Wich <xm@subsignal.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14 ]]--
15 module("luci.controller.admin.system", package.seeall)
16
17 function index()
18 luci.i18n.loadc("admin-core")
19 local i18n = luci.i18n.translate
20
21 entry({"admin", "system"}, alias("admin", "system", "system"), i18n("system"), 30).index = true
22 entry({"admin", "system", "system"}, cbi("admin_system/system"), i18n("system"), 1)
23 entry({"admin", "system", "packages"}, call("action_packages"), i18n("a_s_packages"), 10)
24 entry({"admin", "system", "packages", "ipkg"}, form("admin_system/ipkg"))
25 entry({"admin", "system", "passwd"}, form("admin_system/passwd"), i18n("a_s_changepw"), 20)
26 entry({"admin", "system", "sshkeys"}, form("admin_system/sshkeys"), i18n("a_s_sshkeys"), 30)
27 entry({"admin", "system", "processes"}, form("admin_system/processes"), i18n("process_head"), 45)
28 entry({"admin", "system", "fstab"}, cbi("admin_system/fstab"), i18n("a_s_fstab"), 50)
29
30 if luci.fs.isdirectory("/sys/class/leds") then
31 entry({"admin", "system", "leds"}, cbi("admin_system/leds"), i18n("leds", "LEDs"), 60)
32 end
33
34 entry({"admin", "system", "backup"}, call("action_backup"), i18n("a_s_backup"), 70)
35 entry({"admin", "system", "upgrade"}, call("action_upgrade"), i18n("a_s_flash"), 80)
36 entry({"admin", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 90)
37 end
38
39 function action_packages()
40 local ipkg = require("luci.model.ipkg")
41 local void = nil
42 local submit = luci.http.formvalue("submit")
43
44
45 -- Search query
46 local query = luci.http.formvalue("query")
47 query = (query ~= '') and query or nil
48
49
50 -- Packets to be installed
51 local install = submit and luci.http.formvaluetable("install")
52
53 -- Install from URL
54 local url = luci.http.formvalue("url")
55 if url and url ~= '' and submit then
56 if not install then
57 install = {}
58 end
59 install[url] = 1
60 end
61
62 -- Do install
63 if install then
64 for k, v in pairs(install) do
65 void, install[k] = ipkg.install(k)
66 end
67 end
68
69
70 -- Remove packets
71 local remove = submit and luci.http.formvaluetable("remove")
72 if remove then
73 for k, v in pairs(remove) do
74 void, remove[k] = ipkg.remove(k)
75 end
76 end
77
78
79 -- Update all packets
80 local update = luci.http.formvalue("update")
81 if update then
82 void, update = ipkg.update()
83 end
84
85
86 -- Upgrade all packets
87 local upgrade = luci.http.formvalue("upgrade")
88 if upgrade then
89 void, upgrade = ipkg.upgrade()
90 end
91
92
93 -- Package info
94 local info = luci.model.ipkg.info(query and "*"..query.."*")
95 info = info or {}
96 local pkgs = {}
97
98 -- Sort after status and name
99 for k, v in pairs(info) do
100 local x = 0
101 for i, j in pairs(pkgs) do
102 local vins = (v.Status and v.Status.installed)
103 local jins = (j.Status and j.Status.installed)
104 if vins ~= jins then
105 if vins then
106 break
107 end
108 else
109 if j.Package > v.Package then
110 break
111 end
112 end
113 x = i
114 end
115 table.insert(pkgs, x+1, v)
116 end
117
118 luci.template.render("admin_system/packages", {pkgs=pkgs, query=query,
119 install=install, remove=remove, update=update, upgrade=upgrade})
120 end
121
122 function action_backup()
123 local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0
124 local restore_cmd = "gunzip | tar -xC/ >/dev/null 2>&1"
125 local backup_cmd = "tar -c %s | gzip 2>/dev/null"
126
127 local restore_fpi
128 luci.http.setfilehandler(
129 function(meta, chunk, eof)
130 if not restore_fpi then
131 restore_fpi = io.popen(restore_cmd, "w")
132 end
133 if chunk then
134 restore_fpi:write(chunk)
135 end
136 if eof then
137 restore_fpi:close()
138 end
139 end
140 )
141
142 local upload = luci.http.formvalue("archive")
143 local backup = luci.http.formvalue("backup")
144 local reset = reset_avail and luci.http.formvalue("reset")
145
146 if upload and #upload > 0 then
147 luci.template.render("admin_system/applyreboot")
148 luci.sys.reboot()
149 elseif backup then
150 luci.util.perror(backup_cmd:format(_keep_pattern()))
151 local backup_fpi = io.popen(backup_cmd:format(_keep_pattern()), "r")
152 luci.http.header('Content-Disposition', 'attachment; filename="backup-%s-%s.tar.gz"' % {
153 luci.sys.hostname(), os.date("%Y-%m-%d")})
154 luci.http.prepare_content("application/x-targz")
155 luci.ltn12.pump.all(luci.ltn12.source.file(backup_fpi), luci.http.write)
156 elseif reset then
157 luci.template.render("admin_system/applyreboot")
158 luci.util.exec("mtd -r erase rootfs_data")
159 else
160 luci.template.render("admin_system/backup", {reset_avail = reset_avail})
161 end
162 end
163
164 function action_passwd()
165 local p1 = luci.http.formvalue("pwd1")
166 local p2 = luci.http.formvalue("pwd2")
167 local stat = nil
168
169 if p1 or p2 then
170 if p1 == p2 then
171 stat = luci.sys.user.setpasswd("root", p1)
172 else
173 stat = 10
174 end
175 end
176
177 luci.template.render("admin_system/passwd", {stat=stat})
178 end
179
180 function action_reboot()
181 local reboot = luci.http.formvalue("reboot")
182 luci.template.render("admin_system/reboot", {reboot=reboot})
183 if reboot then
184 luci.sys.reboot()
185 end
186 end
187
188 function action_upgrade()
189 require("luci.model.uci")
190
191 local tmpfile = "/tmp/firmware.img"
192
193 local function image_supported()
194 -- XXX: yay...
195 return ( 0 == os.execute(
196 ". /etc/functions.sh; " ..
197 "include /lib/upgrade; " ..
198 "platform_check_image %q >/dev/null"
199 % tmpfile
200 ) )
201 end
202
203 local function image_checksum()
204 return (luci.sys.exec("md5sum %q" % tmpfile):match("^([^%s]+)"))
205 end
206
207 local function storage_size()
208 local size = 0
209 if luci.fs.access("/proc/mtd") then
210 for l in io.lines("/proc/mtd") do
211 local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
212 if n == "linux" then
213 size = tonumber(s, 16)
214 break
215 end
216 end
217 end
218 return size
219 end
220
221
222 -- Install upload handler
223 local file
224 luci.http.setfilehandler(
225 function(meta, chunk, eof)
226 if not luci.fs.access(tmpfile) and not file and chunk and #chunk > 0 then
227 file = io.open(tmpfile, "w")
228 end
229 if file and chunk then
230 file:write(chunk)
231 end
232 if file and eof then
233 file:close()
234 end
235 end
236 )
237
238
239 -- Determine state
240 local keep_avail = true
241 local step = tonumber(luci.http.formvalue("step") or 1)
242 local has_image = luci.fs.access(tmpfile)
243 local has_support = image_supported()
244 local has_platform = luci.fs.access("/lib/upgrade/platform.sh")
245 local has_upload = luci.http.formvalue("image")
246
247 -- This does the actual flashing which is invoked inside an iframe
248 -- so don't produce meaningful errors here because the the
249 -- previous pages should arrange the stuff as required.
250 if step == 4 then
251 if has_platform and has_image and has_support then
252 -- Next line is to bypass luci.http layer
253 luci.http.context.eoh = true
254
255 -- Now invoke sysupgrade
256 local keepcfg = keep_avail and luci.http.formvalue("keepcfg") == "1"
257 os.execute("/sbin/luci-flash %s %q" %{
258 keepcfg and "-k %q" % _keep_pattern() or "", tmpfile
259 })
260
261 -- Make sure the device is rebooted
262 luci.sys.reboot()
263 end
264
265
266 --
267 -- This is step 1-3, which does the user interaction and
268 -- image upload.
269 --
270
271 -- Step 1: file upload, error on unsupported image format
272 elseif not has_image or not has_support or step == 1 then
273 -- If there is an image but user has requested step 1
274 -- or type is not supported, then remove it.
275 if has_image then
276 luci.fs.unlink(tmpfile)
277 end
278
279 luci.template.render("admin_system/upgrade", {
280 step=1,
281 bad_image=(has_image and not has_support or false),
282 keepavail=keep_avail,
283 supported=has_platform
284 } )
285
286 -- Step 2: present uploaded file, show checksum, confirmation
287 elseif step == 2 then
288 luci.template.render("admin_system/upgrade", {
289 step=2,
290 checksum=image_checksum(),
291 filesize=luci.fs.stat(tmpfile).size,
292 flashsize=storage_size(),
293 keepconfig=(keep_avail and luci.http.formvalue("keepcfg") == "1")
294 } )
295
296 -- Step 3: load iframe which calls the actual flash procedure
297 elseif step == 3 then
298 luci.template.render("admin_system/upgrade", {
299 step=3,
300 keepconfig=(keep_avail and luci.http.formvalue("keepcfg") == "1")
301 } )
302 end
303 end
304
305 function _keep_pattern()
306 local kpattern = ""
307 local files = luci.model.uci.cursor():get_all("luci", "flash_keep")
308 if files then
309 kpattern = ""
310 for k, v in pairs(files) do
311 if k:sub(1,1) ~= "." and luci.fs.glob(v) then
312 kpattern = kpattern .. " " .. v
313 end
314 end
315 end
316 return kpattern
317 end