75ceb0d963
This implements the same behavior as it is used in the autoupdater [1].
This is for example required to allow the manual installation of
firmware upgrades via the config mode on devices which where migrated
from swconfig to DSA. Otherwise the image will always be invalid.
[1] b804281664
143 lines
3.8 KiB
Lua
143 lines
3.8 KiB
Lua
--[[
|
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
|
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
]]--
|
|
|
|
package 'gluon-web-admin'
|
|
|
|
|
|
local util = require 'gluon.util'
|
|
local unistd = require 'posix.unistd'
|
|
|
|
local file
|
|
local tmpfile = "/tmp/firmware.img"
|
|
local compat_option = "--ignore-minor-compat-version"
|
|
|
|
|
|
local function filehandler(_, chunk, eof)
|
|
if not unistd.access(tmpfile) and not file and chunk and #chunk > 0 then
|
|
file = io.open(tmpfile, "w")
|
|
end
|
|
if file and chunk then
|
|
file:write(chunk)
|
|
end
|
|
if file and eof then
|
|
file:close()
|
|
end
|
|
end
|
|
|
|
local function action_upgrade(http, renderer)
|
|
local fcntl = require 'posix.fcntl'
|
|
local stat = require 'posix.sys.stat'
|
|
|
|
local function fork_exec(argv)
|
|
local pid = unistd.fork()
|
|
if pid > 0 then
|
|
return
|
|
elseif pid == 0 then
|
|
-- change to root dir
|
|
unistd.chdir('/')
|
|
|
|
-- patch stdin, out, err to /dev/null
|
|
local null = fcntl.open('/dev/null', fcntl.O_RDWR)
|
|
if null then
|
|
unistd.dup2(null, unistd.STDIN_FILENO)
|
|
unistd.dup2(null, unistd.STDOUT_FILENO)
|
|
unistd.dup2(null, unistd.STDERR_FILENO)
|
|
if null > 2 then
|
|
unistd.close(null)
|
|
end
|
|
end
|
|
|
|
-- Sleep a little so the browser can fetch everything required to
|
|
-- display the reboot page, then reboot the device.
|
|
unistd.sleep(1)
|
|
|
|
-- replace with target command
|
|
unistd.exec(argv[0], argv)
|
|
end
|
|
end
|
|
|
|
local function image_supported(supported_tmpfile)
|
|
return (os.execute(string.format("exec /sbin/sysupgrade -T %s %q >/dev/null", compat_option, supported_tmpfile)) == 0)
|
|
end
|
|
|
|
local function storage_size()
|
|
local size = 0
|
|
if unistd.access("/proc/mtd") then
|
|
for l in io.lines("/proc/mtd") do
|
|
local s, n = l:match('^[^%s]+%s+([^%s]+)%s+[^%s]+%s+"([^%s]+)"')
|
|
if n == "firmware" then
|
|
size = tonumber(s, 16)
|
|
break
|
|
end
|
|
end
|
|
elseif unistd.access("/proc/partitions") then
|
|
for l in io.lines("/proc/partitions") do
|
|
local b, n = l:match('^%s*%d+%s+%d+%s+([^%s]+)%s+([^%s]+)')
|
|
if b and n and not n:match('[0-9]') then
|
|
size = tonumber(b) * 1024
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return size
|
|
end
|
|
|
|
local function image_checksum(checksum_tmpfile)
|
|
return (util.exec(string.format("exec sha256sum %q", checksum_tmpfile)):match("^([^%s]+)"))
|
|
end
|
|
|
|
|
|
-- Determine state
|
|
local step = tonumber(http:getenv("REQUEST_METHOD") == "POST" and http:formvalue("step")) or 1
|
|
|
|
local has_image = unistd.access(tmpfile)
|
|
local has_support = has_image and image_supported(tmpfile)
|
|
|
|
-- Step 1: file upload, error on unsupported image format
|
|
if step == 1 or not has_support then
|
|
-- If there is an image but user has requested step 1
|
|
-- or type is not supported, then remove it.
|
|
if has_image then
|
|
unistd.unlink(tmpfile)
|
|
end
|
|
|
|
renderer.render_layout('admin/upgrade', {
|
|
bad_image = has_image and not has_support,
|
|
}, 'gluon-web-admin')
|
|
|
|
-- Step 2: present uploaded file, show checksum, confirmation
|
|
elseif step == 2 then
|
|
renderer.render_layout('admin/upgrade_confirm', {
|
|
checksum = image_checksum(tmpfile),
|
|
filesize = stat.stat(tmpfile).st_size,
|
|
flashsize = storage_size(),
|
|
keepconfig = (http:formvalue("keepcfg") == "1"),
|
|
}, 'gluon-web-admin')
|
|
|
|
elseif step == 3 then
|
|
local cmd = {[0] = '/sbin/sysupgrade', compat_option, tmpfile}
|
|
if http:formvalue('keepcfg') ~= '1' then
|
|
table.insert(cmd, 1, '-n')
|
|
end
|
|
fork_exec(cmd)
|
|
renderer.render_layout('admin/upgrade_reboot', nil, 'gluon-web-admin', {
|
|
hidenav = true,
|
|
})
|
|
end
|
|
end
|
|
|
|
|
|
local has_platform = unistd.access("/lib/upgrade/platform.sh")
|
|
if has_platform then
|
|
local upgrade = entry({"admin", "upgrade"}, call(action_upgrade), _("Upgrade firmware"), 90)
|
|
upgrade.filehandler = filehandler
|
|
end
|