luci-mod-admin-full: switch to POST action for reboot
[project/luci.git] / modules / luci-mod-admin-full / luasrc / controller / admin / system.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
3 -- Licensed to the public under the Apache License 2.0.
4
5 module("luci.controller.admin.system", package.seeall)
6
7 function index()
8 local fs = require "nixio.fs"
9
10 entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
11 entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 1)
12 entry({"admin", "system", "clock_status"}, call("action_clock_status"))
13
14 entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)
15
16 if fs.access("/bin/opkg") then
17 entry({"admin", "system", "packages"}, call("action_packages"), _("Software"), 10)
18 entry({"admin", "system", "packages", "ipkg"}, form("admin_system/ipkg"))
19 end
20
21 entry({"admin", "system", "startup"}, form("admin_system/startup"), _("Startup"), 45)
22 entry({"admin", "system", "crontab"}, form("admin_system/crontab"), _("Scheduled Tasks"), 46)
23
24 if fs.access("/sbin/block") then
25 entry({"admin", "system", "fstab"}, cbi("admin_system/fstab"), _("Mount Points"), 50)
26 entry({"admin", "system", "fstab", "mount"}, cbi("admin_system/fstab/mount"), nil).leaf = true
27 entry({"admin", "system", "fstab", "swap"}, cbi("admin_system/fstab/swap"), nil).leaf = true
28 end
29
30 if fs.access("/sys/class/leds") then
31 entry({"admin", "system", "leds"}, cbi("admin_system/leds"), _("<abbr title=\"Light Emitting Diode\">LED</abbr> Configuration"), 60)
32 end
33
34 entry({"admin", "system", "flashops"}, call("action_flashops"), _("Backup / Flash Firmware"), 70)
35 entry({"admin", "system", "flashops", "backupfiles"}, form("admin_system/backupfiles"))
36
37 entry({"admin", "system", "reboot"}, template("admin_system/reboot"), _("Reboot"), 90)
38 entry({"admin", "system", "reboot", "call"}, post("action_reboot"))
39 end
40
41 function action_clock_status()
42 local set = tonumber(luci.http.formvalue("set"))
43 if set ~= nil and set > 0 then
44 local date = os.date("*t", set)
45 if date then
46 luci.sys.call("date -s '%04d-%02d-%02d %02d:%02d:%02d'" %{
47 date.year, date.month, date.day, date.hour, date.min, date.sec
48 })
49 end
50 end
51
52 luci.http.prepare_content("application/json")
53 luci.http.write_json({ timestring = os.date("%c") })
54 end
55
56 function action_packages()
57 local fs = require "nixio.fs"
58 local ipkg = require "luci.model.ipkg"
59 local submit = luci.http.formvalue("submit")
60 local changes = false
61 local install = { }
62 local remove = { }
63 local stdout = { "" }
64 local stderr = { "" }
65 local out, err
66
67 -- Display
68 local display = luci.http.formvalue("display") or "installed"
69
70 -- Letter
71 local letter = string.byte(luci.http.formvalue("letter") or "A", 1)
72 letter = (letter == 35 or (letter >= 65 and letter <= 90)) and letter or 65
73
74 -- Search query
75 local query = luci.http.formvalue("query")
76 query = (query ~= '') and query or nil
77
78
79 -- Packets to be installed
80 local ninst = submit and luci.http.formvalue("install")
81 local uinst = nil
82
83 -- Install from URL
84 local url = luci.http.formvalue("url")
85 if url and url ~= '' and submit then
86 uinst = url
87 end
88
89 -- Do install
90 if ninst then
91 install[ninst], out, err = ipkg.install(ninst)
92 stdout[#stdout+1] = out
93 stderr[#stderr+1] = err
94 changes = true
95 end
96
97 if uinst then
98 local pkg
99 for pkg in luci.util.imatch(uinst) do
100 install[uinst], out, err = ipkg.install(pkg)
101 stdout[#stdout+1] = out
102 stderr[#stderr+1] = err
103 changes = true
104 end
105 end
106
107 -- Remove packets
108 local rem = submit and luci.http.formvalue("remove")
109 if rem then
110 remove[rem], out, err = ipkg.remove(rem)
111 stdout[#stdout+1] = out
112 stderr[#stderr+1] = err
113 changes = true
114 end
115
116
117 -- Update all packets
118 local update = luci.http.formvalue("update")
119 if update then
120 update, out, err = ipkg.update()
121 stdout[#stdout+1] = out
122 stderr[#stderr+1] = err
123 end
124
125
126 -- Upgrade all packets
127 local upgrade = luci.http.formvalue("upgrade")
128 if upgrade then
129 upgrade, out, err = ipkg.upgrade()
130 stdout[#stdout+1] = out
131 stderr[#stderr+1] = err
132 end
133
134
135 -- List state
136 local no_lists = true
137 local old_lists = false
138 if fs.access("/var/opkg-lists/") then
139 local list
140 for list in fs.dir("/var/opkg-lists/") do
141 no_lists = false
142 if (fs.stat("/var/opkg-lists/"..list, "mtime") or 0) < (os.time() - (24 * 60 * 60)) then
143 old_lists = true
144 break
145 end
146 end
147 end
148
149
150 luci.template.render("admin_system/packages", {
151 display = display,
152 letter = letter,
153 query = query,
154 install = install,
155 remove = remove,
156 update = update,
157 upgrade = upgrade,
158 no_lists = no_lists,
159 old_lists = old_lists,
160 stdout = table.concat(stdout, ""),
161 stderr = table.concat(stderr, "")
162 })
163
164 -- Remove index cache
165 if changes then
166 fs.unlink("/tmp/luci-indexcache")
167 end
168 end
169
170 function action_flashops()
171 local sys = require "luci.sys"
172 local fs = require "nixio.fs"
173
174 local upgrade_avail = fs.access("/lib/upgrade/platform.sh")
175 local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0
176
177 local restore_cmd = "tar -xzC/ >/dev/null 2>&1"
178 local backup_cmd = "sysupgrade --create-backup - 2>/dev/null"
179 local image_tmp = "/tmp/firmware.img"
180
181 local function image_supported()
182 return (os.execute("sysupgrade -T %q >/dev/null" % image_tmp) == 0)
183 end
184
185 local function image_checksum()
186 return (luci.sys.exec("md5sum %q" % image_tmp):match("^([^%s]+)"))
187 end
188
189 local function storage_size()
190 local size = 0
191 if fs.access("/proc/mtd") then
192 for l in io.lines("/proc/mtd") do
193 local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
194 if n == "linux" or n == "firmware" then
195 size = tonumber(s, 16)
196 break
197 end
198 end
199 elseif fs.access("/proc/partitions") then
200 for l in io.lines("/proc/partitions") do
201 local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
202 if b and n and not n:match('[0-9]') then
203 size = tonumber(b) * 1024
204 break
205 end
206 end
207 end
208 return size
209 end
210
211
212 local fp
213 luci.http.setfilehandler(
214 function(meta, chunk, eof)
215 if not fp then
216 if meta and meta.name == "image" then
217 fp = io.open(image_tmp, "w")
218 else
219 fp = io.popen(restore_cmd, "w")
220 end
221 end
222 if chunk then
223 fp:write(chunk)
224 end
225 if eof then
226 fp:close()
227 end
228 end
229 )
230
231 if luci.http.formvalue("backup") then
232 --
233 -- Assemble file list, generate backup
234 --
235 local reader = ltn12_popen(backup_cmd)
236 luci.http.header('Content-Disposition', 'attachment; filename="backup-%s-%s.tar.gz"' % {
237 luci.sys.hostname(), os.date("%Y-%m-%d")})
238 luci.http.prepare_content("application/x-targz")
239 luci.ltn12.pump.all(reader, luci.http.write)
240 elseif luci.http.formvalue("restore") then
241 --
242 -- Unpack received .tar.gz
243 --
244 local upload = luci.http.formvalue("archive")
245 if upload and #upload > 0 then
246 luci.template.render("admin_system/applyreboot")
247 luci.sys.reboot()
248 end
249 elseif luci.http.formvalue("image") or luci.http.formvalue("step") then
250 --
251 -- Initiate firmware flash
252 --
253 local step = tonumber(luci.http.formvalue("step") or 1)
254 if step == 1 then
255 if image_supported() then
256 luci.template.render("admin_system/upgrade", {
257 checksum = image_checksum(),
258 storage = storage_size(),
259 size = (fs.stat(image_tmp, "size") or 0),
260 keep = (not not luci.http.formvalue("keep"))
261 })
262 else
263 fs.unlink(image_tmp)
264 luci.template.render("admin_system/flashops", {
265 reset_avail = reset_avail,
266 upgrade_avail = upgrade_avail,
267 image_invalid = true
268 })
269 end
270 --
271 -- Start sysupgrade flash
272 --
273 elseif step == 2 then
274 local keep = (luci.http.formvalue("keep") == "1") and "" or "-n"
275 luci.template.render("admin_system/applyreboot", {
276 title = luci.i18n.translate("Flashing..."),
277 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."),
278 addr = (#keep > 0) and "192.168.1.1" or nil
279 })
280 fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
281 end
282 elseif reset_avail and luci.http.formvalue("reset") then
283 --
284 -- Reset system
285 --
286 luci.template.render("admin_system/applyreboot", {
287 title = luci.i18n.translate("Erasing..."),
288 msg = luci.i18n.translate("The system is erasing the configuration partition now and will reboot itself when finished."),
289 addr = "192.168.1.1"
290 })
291 fork_exec("killall dropbear uhttpd; sleep 1; mtd -r erase rootfs_data")
292 else
293 --
294 -- Overview
295 --
296 luci.template.render("admin_system/flashops", {
297 reset_avail = reset_avail,
298 upgrade_avail = upgrade_avail
299 })
300 end
301 end
302
303 function action_passwd()
304 local p1 = luci.http.formvalue("pwd1")
305 local p2 = luci.http.formvalue("pwd2")
306 local stat = nil
307
308 if p1 or p2 then
309 if p1 == p2 then
310 stat = luci.sys.user.setpasswd("root", p1)
311 else
312 stat = 10
313 end
314 end
315
316 luci.template.render("admin_system/passwd", {stat=stat})
317 end
318
319 function action_reboot()
320 luci.sys.reboot()
321 end
322
323 function fork_exec(command)
324 local pid = nixio.fork()
325 if pid > 0 then
326 return
327 elseif pid == 0 then
328 -- change to root dir
329 nixio.chdir("/")
330
331 -- patch stdin, out, err to /dev/null
332 local null = nixio.open("/dev/null", "w+")
333 if null then
334 nixio.dup(null, nixio.stderr)
335 nixio.dup(null, nixio.stdout)
336 nixio.dup(null, nixio.stdin)
337 if null:fileno() > 2 then
338 null:close()
339 end
340 end
341
342 -- replace with target command
343 nixio.exec("/bin/sh", "-c", command)
344 end
345 end
346
347 function ltn12_popen(command)
348
349 local fdi, fdo = nixio.pipe()
350 local pid = nixio.fork()
351
352 if pid > 0 then
353 fdo:close()
354 local close
355 return function()
356 local buffer = fdi:read(2048)
357 local wpid, stat = nixio.waitpid(pid, "nohang")
358 if not close and wpid and stat == "exited" then
359 close = true
360 end
361
362 if buffer and #buffer > 0 then
363 return buffer
364 elseif close then
365 fdi:close()
366 return nil
367 end
368 end
369 elseif pid == 0 then
370 nixio.dup(fdo, nixio.stdout)
371 fdi:close()
372 fdo:close()
373 nixio.exec("/bin/sh", "-c", command)
374 end
375 end