summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-advanced-reboot/luasrc/controller/advanced_reboot.lua
blob: f40eac50c35c71843df61bff857c0b98d6cff568 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
-- Copyright 2017-2018 Stan Grishin <stangri@melmac.net>
-- Licensed to the public under the Apache License 2.0.

module("luci.controller.advanced_reboot", package.seeall)

devices = {
  -- deviceName, boardName, partition1, partition2, offset, envVar1, envVar1Value1, envVar1Value2, envVar2, envVar2Value1, envVar2Value2
  {"Linksys EA3500", "linksys-audi", "mtd3", "mtd5", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys E4200v2/EA4500", "linksys-viper", "mtd3", "mtd5", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys EA6350v3", "linksys-ea6350v3", "mtd10", "mtd12", 192, "boot_part", 1, 2},
  {"Linksys EA8500", "ea8500", "mtd13", "mtd15", 32, "boot_part", 1, 2},
--  {"Linksys EA9500", "linksys-panamera", "mtd3", "mtd6", 28, "boot_part", 1, 2},
  {"Linksys WRT1200AC", "linksys-caiman", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys WRT1900AC", "linksys-mamba", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys WRT1900ACv2", "linksys-cobra", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys WRT1900ACS", "linksys-shelby", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys WRT3200ACM", "linksys-rango", "mtd5", "mtd7", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"Linksys WRT32X", "linksys-venom", "mtd5", "mtd7", nil, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
  {"ZyXEL NBG6817","nbg6817","mmcblk0p4","mmcblk0p7", 32, nil, 255, 1}
}

errorMessage = nil
rom_board_name = luci.util.trim(luci.sys.exec("cat /tmp/sysinfo/board_name"))
for i=1, #devices do
  device_board_name = devices[i][2]:gsub('%p','')
  if rom_board_name and rom_board_name:gsub('%p',''):match(device_board_name) then
    device_name = devices[i][1]
    partition_one_mtd = devices[i][3] or nil
    partition_two_mtd = devices[i][4] or nil
    partition_skip = devices[i][5] or nil
    boot_envvar1 = devices[i][6] or nil
    boot_envvar1_partition_one = tonumber(devices[i][7]) or nil
    boot_envvar1_partition_two = tonumber(devices[i][8]) or nil
    boot_envvar2 = devices[i][9] or nil
    boot_envvar2_partition_one = devices[i][10] or nil
    boot_envvar2_partition_two = devices[i][11] or nil
    if partition_one_mtd and partition_skip then
      partition_one_label = luci.util.trim(luci.sys.exec("dd if=/dev/" .. partition_one_mtd .. " bs=1 skip=" .. partition_skip .. " count=128" .. "  2>/dev/null"))
      n, partition_one_version = string.match(partition_one_label, '(Linux)-([%d|.]+)')
    end
    if partition_two_mtd and partition_skip then
      partition_two_label = luci.util.trim(luci.sys.exec("dd if=/dev/" .. partition_two_mtd .. " bs=1 skip=" .. partition_skip .. " count=128" .. "  2>/dev/null"))
      n, partition_two_version = string.match(partition_two_label, '(Linux)-([%d|.]+)')
    end
    if partition_one_label and string.find(partition_one_label, "LEDE") then partition_one_os = "LEDE" end
    if partition_one_label and string.find(partition_one_label, "OpenWrt") then partition_one_os = "OpenWrt" end
    if partition_one_label and string.find(partition_one_label, "Linksys") then partition_one_os = "Linksys" end
    if partition_two_label and string.find(partition_two_label, "LEDE") then partition_two_os = "LEDE" end
    if partition_two_label and string.find(partition_two_label, "OpenWrt") then partition_two_os = "OpenWrt" end
    if partition_two_label and string.find(partition_two_label, "Linksys") then partition_two_os = "Linksys" end
    if device_name and device_name == "ZyXEL NBG6817" then
      if not partition_one_os then partition_one_os = "ZyXEL" end
      if not partition_two_os then partition_two_os = "ZyXEL" end
    end
    if device_name and device_name == "Linksys WRT32X" then
      if not partition_one_os then partition_one_os = "Unknown/Compressed" end
      if not partition_two_os then partition_two_os = "Unknown/Compressed" end
    end
    if not partition_one_os then partition_one_os = "Unknown" end
    if not partition_two_os then partition_two_os = "Unknown" end
    if partition_one_os and partition_one_version then partition_one_os = partition_one_os .. " (Linux " .. partition_one_version .. ")" end
    if partition_two_os and partition_two_version then partition_two_os = partition_two_os .. " (Linux " .. partition_two_version .. ")" end

    if device_name and device_name == "ZyXEL NBG6817" then
      if not zyxelFlagPartition then zyxelFlagPartition = luci.util.trim(luci.sys.exec("source /lib/functions.sh; find_mtd_part 0:DUAL_FLAG")) end
      if not zyxelFlagPartition then
        errorMessage = errorMessage or "" .. luci.i18n.translate("Unable to find Dual Boot Flag Partition." .. " ")
        luci.util.perror(luci.i18n.translate("Unable to find Dual Boot Flag Partition."))
      else
        current_partition = tonumber(luci.sys.exec("dd if=" .. zyxelFlagPartition .. " bs=1 count=1 2>/dev/null | hexdump -n 1 -e '1/1 \"%d\"'"))
      end
    else
      if nixio.fs.access("/usr/sbin/fw_printenv") and nixio.fs.access("/usr/sbin/fw_setenv") then
        current_partition = tonumber(luci.util.trim(luci.sys.exec("/usr/sbin/fw_printenv -n " .. boot_envvar1)))
      end
    end
    other_partition = current_partition == boot_envvar1_partition_two and boot_envvar1_partition_one or boot_envvar1_partition_two
  end
end

function index()
  entry({"admin", "system", "advanced_reboot"}, template("advanced_reboot/advanced_reboot"), _("Advanced Reboot"), 90)
  entry({"admin", "system", "advanced_reboot", "reboot"}, post("action_reboot"))
  entry({"admin", "system", "advanced_reboot", "alternative_reboot"}, post("action_altreboot"))
  entry({"admin", "system", "advanced_reboot", "power_off"}, post("action_poweroff"))
end

function action_reboot()
  local uci = require "luci.model.uci".cursor()
  luci.template.render("admin_system/applyreboot", {
        title = luci.i18n.translate("Rebooting..."),
        msg   = luci.i18n.translate("The system is rebooting 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."),
        addr  = luci.ip.new(uci:get("network", "lan", "ipaddr")) or "192.168.1.1"
      })
  luci.sys.reboot()
end

function action_altreboot()
  local uci = require "luci.model.uci".cursor()
  local zyxelFlagPartition, zyxelBootFlag, zyxelNewBootFlag, errorCode, curEnvSetting, newEnvSetting
  errorMessage = nil
  errorCode = 0
  if luci.http.formvalue("cancel") then
    luci.http.redirect(luci.dispatcher.build_url('admin/system/advanced_reboot'))
    return
  end
  local step = tonumber(luci.http.formvalue("step") or 1)
  if step == 1 then
    if device_name and nixio.fs.access("/usr/sbin/fw_printenv") and nixio.fs.access("/usr/sbin/fw_setenv") then
      luci.template.render("advanced_reboot/alternative_reboot",{})
    else
      luci.template.render("advanced_reboot/advanced_reboot",{errorMessage = luci.i18n.translate("No access to fw_printenv or fw_printenv!")})
    end
  elseif step == 2 then
    if boot_envvar1 or boot_envvar2 then -- Linksys devices
      if boot_envvar1 then
        curEnvSetting = tonumber(luci.util.trim(luci.sys.exec("/usr/sbin/fw_printenv -n " .. boot_envvar1)))
        if not curEnvSetting then
          errorMessage = errorMessage .. luci.i18n.translate("Unable to obtain firmware environment variable") .. ": " .. boot_envvar1 .. ". "
          luci.util.perror(luci.i18n.translate("Unable to obtain firmware environment variable") .. ": " .. boot_envvar1 .. ".")
        else
          newEnvSetting = curEnvSetting == boot_envvar1_partition_one and boot_envvar1_partition_two or boot_envvar1_partition_one
          errorCode = luci.sys.call("/usr/sbin/fw_setenv " .. boot_envvar1 .. " " .. newEnvSetting)
            if errorCode ~= 0 then
              errorMessage = errorMessage or "" .. luci.i18n.translate("Unable to set firmware environment variable") .. ": " .. boot_envvar1 .. " " .. luci.i18n.translate("to") .. " " .. newEnvSetting .. ". "
              luci.util.perror(luci.i18n.translate("Unable to set firmware environment variable") .. ": " .. boot_envvar1 .. " " .. luci.i18n.translate("to") .. " " .. newEnvSetting .. ".")
            end
        end
      end
      if boot_envvar2 then
        curEnvSetting = luci.util.trim(luci.sys.exec("/usr/sbin/fw_printenv -n " .. boot_envvar2))
        if not curEnvSetting then
          errorMessage = errorMessage or "" .. luci.i18n.translate("Unable to obtain firmware environment variable") .. ": " .. boot_envvar2 .. ". "
          luci.util.perror(luci.i18n.translate("Unable to obtain firmware environment variable") .. ": " .. boot_envvar2 .. ".")
        else
          newEnvSetting = curEnvSetting == boot_envvar2_partition_one and boot_envvar2_partition_two or boot_envvar2_partition_one
          errorCode = luci.sys.call("/usr/sbin/fw_setenv " .. boot_envvar2 .. " '" .. newEnvSetting .. "'")
          if errorCode ~= 0 then
            errorMessage = errorMessage or "" .. luci.i18n.translate("Unable to set firmware environment variable") .. ": " .. boot_envvar2 .. " " .. luci.i18n.translate("to") .. " " .. newEnvSetting .. ". "
            luci.util.perror(luci.i18n.translate("Unable to set firmware environment variable") .. ": " .. boot_envvar2 .. " " .. luci.i18n.translate("to") .. " " .. newEnvSetting .. ".")
          end
        end
      end
    else -- NetGear device
      if not zyxelFlagPartition then zyxelFlagPartition = luci.util.trim(luci.sys.exec("source /lib/functions.sh; find_mtd_part 0:DUAL_FLAG")) end
      if not zyxelFlagPartition then
        errorMessage = errorMessage .. luci.i18n.translate("Unable to find Dual Boot Flag Partition." .. " ")
        luci.util.perror(luci.i18n.translate("Unable to find Dual Boot Flag Partition."))
      else
        zyxelBootFlag = tonumber(luci.sys.exec("dd if=" .. zyxelFlagPartition .. " bs=1 count=1 2>/dev/null | hexdump -n 1 -e '1/1 \"%d\"'"))
        zyxelNewBootFlag = zyxelBootFlag and zyxelBootFlag == 1 and "\\xff" or "\\x01"
        if zyxelNewBootFlag then
          errorCode = luci.sys.call("printf \"" .. zyxelNewBootFlag .. "\" >" .. zyxelFlagPartition )
          if errorCode ~= 0 then
            errorMessage = errorMessage or "" .. luci.i18n.translate("Unable to set Dual Boot Flag Partition entry for partition") .. ": " .. zyxelFlagPartition .. ". "
            luci.util.perror(luci.i18n.translate("Unable to set Dual Boot Flag Partition entry for partition") .. ": " .. zyxelFlagPartition .. ".")
          end
        end
      end
    end
    if not errorMessage then
      luci.template.render("admin_system/applyreboot", {
            title = luci.i18n.translate("Rebooting..."),
            msg   = luci.i18n.translate("The system is rebooting to an alternative partition 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."),
            addr  = luci.ip.new(uci:get("network", "lan", "ipaddr")) or "192.168.1.1"
          })
      luci.sys.reboot()
    else
      luci.template.render("advanced_reboot/advanced_reboot",{
        rom_board_name=rom_board_name,
        device_name=device_name,
        boot_envvar1_partition_one=boot_envvar1_partition_one,
        partition_one_os=partition_one_os,
        boot_envvar1_partition_two=boot_envvar1_partition_two,
        partition_two_os=partition_two_os,
        current_partition=current_partition,
        errorMessage = errorMessage})
    end
  end
end

function action_poweroff()
  local uci = require "luci.model.uci".cursor()
  if luci.http.formvalue("cancel") then
    luci.http.redirect(luci.dispatcher.build_url('admin/system/advanced_reboot'))
    return
  end
  local step = tonumber(luci.http.formvalue("step") or 1)
  if step == 1 then
    if nixio.fs.access("/sbin/poweroff") then
      luci.template.render("advanced_reboot/power_off",{})
    else
      luci.template.render("advanced_reboot/advanced_reboot",{})
    end
  elseif step == 2 then
    luci.template.render("admin_system/applyreboot", {
          title = luci.i18n.translate("Shutting down..."),
          msg   = luci.i18n.translate("The system is shutting down now.<br /> DO NOT POWER OFF THE DEVICE!<br /> It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
          addr  = luci.ip.new(uci:get("network", "lan", "ipaddr")) or "192.168.1.1"
        })
    luci.sys.call("/sbin/poweroff")
  end
end