system.lua 11 KB

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