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