failsafe.lua 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. -- Copyright 2008 Steven Barth <steven@midlink.org>
  2. -- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
  3. -- Copyright 2012 Daniel Golle <dgolle@allnet.de>
  4. -- Licensed to the public under the Apache License 2.0.
  5. module("luci.controller.failsafe.failsafe", package.seeall)
  6. function index()
  7. local root = node()
  8. if not root.target then
  9. root.target = alias("failsafe")
  10. root.index = true
  11. end
  12. page = node()
  13. page.lock = true
  14. page.target = alias("failsafe")
  15. page.subindex = true
  16. page.index = false
  17. page = node("failsafe")
  18. page.title = _("Fail-safe")
  19. page.target = alias("failsafe", "flashops")
  20. page.order = 5
  21. page.setuser = "root"
  22. page.setgroup = "root"
  23. page.index = true
  24. entry({"failsafe", "flashops"}, call("action_flashops"), _("Flash Firmware"), 70).index = true
  25. entry({"failsafe", "reboot"}, call("action_reboot"), _("Reboot"), 90)
  26. end
  27. function action_flashops()
  28. local sys = require "luci.sys"
  29. local fs = require "nixio.fs"
  30. local upgrade_avail = fs.access("/lib/upgrade/platform.sh")
  31. local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0
  32. local image_tmp = "/tmp/firmware.img"
  33. local function image_supported()
  34. -- XXX: yay...
  35. return ( 0 == os.execute(
  36. ". /lib/functions.sh; " ..
  37. "include /lib/upgrade; " ..
  38. "platform_check_image %q >/dev/null"
  39. % image_tmp
  40. ) )
  41. end
  42. local function image_checksum()
  43. return (luci.sys.exec("md5sum %q" % image_tmp):match("^([^%s]+)"))
  44. end
  45. local function storage_size()
  46. local size = 0
  47. if fs.access("/proc/mtd") then
  48. for l in io.lines("/proc/mtd") do
  49. local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
  50. if n == "linux" or n == "firmware" then
  51. size = tonumber(s, 16)
  52. break
  53. end
  54. end
  55. elseif fs.access("/proc/partitions") then
  56. for l in io.lines("/proc/partitions") do
  57. local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
  58. if b and n and not n:match('[0-9]') then
  59. size = tonumber(b) * 1024
  60. break
  61. end
  62. end
  63. end
  64. return size
  65. end
  66. local fp
  67. luci.http.setfilehandler(
  68. function(meta, chunk, eof)
  69. if not fp then
  70. if meta and meta.name == "image" then
  71. fp = io.open(image_tmp, "w")
  72. end
  73. end
  74. if chunk then
  75. fp:write(chunk)
  76. end
  77. if eof then
  78. fp:close()
  79. end
  80. end
  81. )
  82. if luci.http.formvalue("image") or luci.http.formvalue("step") then
  83. --
  84. -- Initiate firmware flash
  85. --
  86. local step = tonumber(luci.http.formvalue("step") or 1)
  87. if step == 1 then
  88. if image_supported() then
  89. luci.template.render("failsafe/upgrade", {
  90. checksum = image_checksum(),
  91. storage = storage_size(),
  92. size = (fs.stat(image_tmp, "size") or 0),
  93. keep = false
  94. })
  95. else
  96. fs.unlink(image_tmp)
  97. luci.template.render("failsafe/flashops", {
  98. reset_avail = reset_avail,
  99. upgrade_avail = upgrade_avail,
  100. image_invalid = true
  101. })
  102. end
  103. --
  104. -- Start sysupgrade flash
  105. --
  106. elseif step == 2 then
  107. local keep = (luci.http.formvalue("keep") == "1") and "" or "-n"
  108. luci.template.render("failsafe/applyreboot", {
  109. title = luci.i18n.translate("Flashing..."),
  110. 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."),
  111. addr = (#keep > 0) and "192.168.1.1" or nil
  112. })
  113. fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
  114. end
  115. else
  116. --
  117. -- Overview
  118. --
  119. luci.template.render("failsafe/flashops", {
  120. reset_avail = reset_avail,
  121. upgrade_avail = upgrade_avail
  122. })
  123. end
  124. end
  125. function action_reboot()
  126. local reboot = luci.http.formvalue("reboot")
  127. luci.template.render("failsafe/reboot", {reboot=reboot})
  128. if reboot then
  129. luci.sys.reboot()
  130. end
  131. end
  132. function fork_exec(command)
  133. local pid = nixio.fork()
  134. if pid > 0 then
  135. return
  136. elseif pid == 0 then
  137. -- change to root dir
  138. nixio.chdir("/")
  139. -- patch stdin, out, err to /dev/null
  140. local null = nixio.open("/dev/null", "w+")
  141. if null then
  142. nixio.dup(null, nixio.stderr)
  143. nixio.dup(null, nixio.stdout)
  144. nixio.dup(null, nixio.stdin)
  145. if null:fileno() > 2 then
  146. null:close()
  147. end
  148. end
  149. -- replace with target command
  150. nixio.exec("/bin/sh", "-c", command)
  151. end
  152. end
  153. function ltn12_popen(command)
  154. local fdi, fdo = nixio.pipe()
  155. local pid = nixio.fork()
  156. if pid > 0 then
  157. fdo:close()
  158. local close
  159. return function()
  160. local buffer = fdi:read(2048)
  161. local wpid, stat = nixio.waitpid(pid, "nohang")
  162. if not close and wpid and stat == "exited" then
  163. close = true
  164. end
  165. if buffer and #buffer > 0 then
  166. return buffer
  167. elseif close then
  168. fdi:close()
  169. return nil
  170. end
  171. end
  172. elseif pid == 0 then
  173. nixio.dup(fdo, nixio.stdout)
  174. fdi:close()
  175. fdo:close()
  176. nixio.exec("/bin/sh", "-c", command)
  177. end
  178. end