system.lua 11 KB

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