system.lua 10.0 KB

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