autoupdater: convert spaces to tabs
This commit is contained in:
parent
463f872844
commit
ac55ad56b5
|
@ -8,126 +8,126 @@ module('autoupdater.util', package.seeall)
|
|||
|
||||
-- Executes a command in the background, without parsing the command through a shell (in contrast to os.execute)
|
||||
function exec(timeout, ...)
|
||||
local pid, errno, error = nixio.fork()
|
||||
if pid == 0 then
|
||||
nixio.execp(...)
|
||||
os.exit(127)
|
||||
elseif pid > 0 then
|
||||
if timeout then
|
||||
local starttime = os.time()
|
||||
while true do
|
||||
if os.difftime(os.time(), starttime) > timeout then
|
||||
nixio.kill(pid, nixio.const.SIGTERM)
|
||||
end
|
||||
local pid, errno, error = nixio.fork()
|
||||
if pid == 0 then
|
||||
nixio.execp(...)
|
||||
os.exit(127)
|
||||
elseif pid > 0 then
|
||||
if timeout then
|
||||
local starttime = os.time()
|
||||
while true do
|
||||
if os.difftime(os.time(), starttime) > timeout then
|
||||
nixio.kill(pid, nixio.const.SIGTERM)
|
||||
end
|
||||
|
||||
local wpid, status, code = nixio.waitpid(pid, 'nohang')
|
||||
if wpid then
|
||||
return wpid and status == 'exited' and code
|
||||
end
|
||||
nixio.nanosleep(1)
|
||||
end
|
||||
else
|
||||
local wpid, status, code = nixio.waitpid(pid)
|
||||
return wpid and status == 'exited' and code
|
||||
end
|
||||
else
|
||||
return pid, errno, error
|
||||
end
|
||||
local wpid, status, code = nixio.waitpid(pid, 'nohang')
|
||||
if wpid then
|
||||
return wpid and status == 'exited' and code
|
||||
end
|
||||
nixio.nanosleep(1)
|
||||
end
|
||||
else
|
||||
local wpid, status, code = nixio.waitpid(pid)
|
||||
return wpid and status == 'exited' and code
|
||||
end
|
||||
else
|
||||
return pid, errno, error
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Executes a command in the background, returning its PID and a pipe connected to the command's standard input
|
||||
function popen(write, ...)
|
||||
local inr, inw = nixio.pipe()
|
||||
local pid = nixio.fork()
|
||||
local inr, inw = nixio.pipe()
|
||||
local pid = nixio.fork()
|
||||
|
||||
if pid > 0 then
|
||||
if write then
|
||||
inr:close()
|
||||
return pid, inw
|
||||
else
|
||||
inw:close()
|
||||
return pid, inr
|
||||
end
|
||||
elseif pid == 0 then
|
||||
if write then
|
||||
nixio.dup(inr, nixio.stdin)
|
||||
else
|
||||
nixio.dup(inw, nixio.stdout)
|
||||
end
|
||||
if pid > 0 then
|
||||
if write then
|
||||
inr:close()
|
||||
return pid, inw
|
||||
else
|
||||
inw:close()
|
||||
return pid, inr
|
||||
end
|
||||
elseif pid == 0 then
|
||||
if write then
|
||||
nixio.dup(inr, nixio.stdin)
|
||||
else
|
||||
nixio.dup(inw, nixio.stdout)
|
||||
end
|
||||
|
||||
inr:close()
|
||||
inw:close()
|
||||
inr:close()
|
||||
inw:close()
|
||||
|
||||
nixio.execp(...)
|
||||
os.exit(127)
|
||||
end
|
||||
nixio.execp(...)
|
||||
os.exit(127)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Executes all executable files in a directory
|
||||
function run_dir(dir)
|
||||
local function is_ok(entry)
|
||||
if entry:sub(1, 1) == '.' then
|
||||
return false
|
||||
end
|
||||
local function is_ok(entry)
|
||||
if entry:sub(1, 1) == '.' then
|
||||
return false
|
||||
end
|
||||
|
||||
local file = dir .. '/' .. entry
|
||||
if fs.stat(file, 'type') ~= 'reg' then
|
||||
return false
|
||||
end
|
||||
if not fs.access(file, 'x') then
|
||||
return false
|
||||
end
|
||||
local file = dir .. '/' .. entry
|
||||
if fs.stat(file, 'type') ~= 'reg' then
|
||||
return false
|
||||
end
|
||||
if not fs.access(file, 'x') then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local files = util.consume(fs.dir(dir))
|
||||
if not files then
|
||||
return
|
||||
end
|
||||
local files = util.consume(fs.dir(dir))
|
||||
if not files then
|
||||
return
|
||||
end
|
||||
|
||||
table.sort(files)
|
||||
table.sort(files)
|
||||
|
||||
for _, entry in ipairs(files) do
|
||||
if is_ok(entry) then
|
||||
exec(nil, dir .. '/' .. entry)
|
||||
end
|
||||
end
|
||||
for _, entry in ipairs(files) do
|
||||
if is_ok(entry) then
|
||||
exec(nil, dir .. '/' .. entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Seeds Lua's random generator from /dev/urandom
|
||||
function randomseed()
|
||||
local f = io.open('/dev/urandom', 'r')
|
||||
local b1, b2, b3, b4 = f:read(4):byte(1, 4)
|
||||
f:close()
|
||||
local f = io.open('/dev/urandom', 'r')
|
||||
local b1, b2, b3, b4 = f:read(4):byte(1, 4)
|
||||
f:close()
|
||||
|
||||
-- The and is necessary as Lua on OpenWrt doesn't like integers over 2^31-1
|
||||
math.randomseed(nixio.bit.band(b1*0x1000000 + b2*0x10000 + b3*0x100 + b4, 0x7fffffff))
|
||||
-- The and is necessary as Lua on OpenWrt doesn't like integers over 2^31-1
|
||||
math.randomseed(nixio.bit.band(b1*0x1000000 + b2*0x10000 + b3*0x100 + b4, 0x7fffffff))
|
||||
end
|
||||
|
||||
|
||||
-- Takes a date and time in RFC3339 format and returns a Unix timestamp
|
||||
function parse_date(date)
|
||||
local year, month, day, hour, minute, second, tzs, tzh, tzm = date:match('^(%d%d%d%d)%-(%d%d)%-(%d%d) (%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$')
|
||||
if not year then
|
||||
return nil
|
||||
end
|
||||
local year, month, day, hour, minute, second, tzs, tzh, tzm = date:match('^(%d%d%d%d)%-(%d%d)%-(%d%d) (%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$')
|
||||
if not year then
|
||||
return nil
|
||||
end
|
||||
|
||||
local a = math.floor((14 - month)/12)
|
||||
local y = year - a
|
||||
local m = month + 12*a - 3
|
||||
local a = math.floor((14 - month)/12)
|
||||
local y = year - a
|
||||
local m = month + 12*a - 3
|
||||
|
||||
-- Based on a well-known formula for Julian dates
|
||||
local days = day + math.floor((153*m + 2)/5) + 365*y + math.floor(y/4) - math.floor(y/100) + math.floor(y/400) - 719469
|
||||
local time = hour*3600 + minute*60 + second
|
||||
local tz = tzh*3600 + tzm*60
|
||||
-- Based on a well-known formula for Julian dates
|
||||
local days = day + math.floor((153*m + 2)/5) + 365*y + math.floor(y/4) - math.floor(y/100) + math.floor(y/400) - 719469
|
||||
local time = hour*3600 + minute*60 + second
|
||||
local tz = tzh*3600 + tzm*60
|
||||
|
||||
if tzs == '-' then
|
||||
tz = -tz
|
||||
end
|
||||
if tzs == '-' then
|
||||
tz = -tz
|
||||
end
|
||||
|
||||
return days * 86400 + time - tz
|
||||
return days * 86400 + time - tz
|
||||
end
|
||||
|
|
|
@ -3,77 +3,77 @@ module 'autoupdater.version'
|
|||
|
||||
-- version comparison is based on dpkg code
|
||||
local function isdigit(s, i)
|
||||
local c = s:sub(i, i)
|
||||
return c and c:match('^%d$')
|
||||
local c = s:sub(i, i)
|
||||
return c and c:match('^%d$')
|
||||
end
|
||||
|
||||
local function char_value(s, i)
|
||||
return s:byte(i, i) or 0
|
||||
return s:byte(i, i) or 0
|
||||
end
|
||||
|
||||
local function char_order(s, i)
|
||||
local c = s:sub(i, i)
|
||||
local c = s:sub(i, i)
|
||||
|
||||
if c == '' or c:match('^%d$') then
|
||||
return 0
|
||||
elseif c:match('^%a$') then
|
||||
return c:byte()
|
||||
elseif c == '~' then
|
||||
return -1
|
||||
else
|
||||
return c:byte() + 256
|
||||
end
|
||||
if c == '' or c:match('^%d$') then
|
||||
return 0
|
||||
elseif c:match('^%a$') then
|
||||
return c:byte()
|
||||
elseif c == '~' then
|
||||
return -1
|
||||
else
|
||||
return c:byte() + 256
|
||||
end
|
||||
end
|
||||
|
||||
-- returns true when a is a higher version number than b
|
||||
function newer_than(a, b)
|
||||
local apos = 1
|
||||
local bpos = 1
|
||||
local apos = 1
|
||||
local bpos = 1
|
||||
|
||||
while apos <= a:len() or bpos <= b:len() do
|
||||
local first_diff = 0
|
||||
while apos <= a:len() or bpos <= b:len() do
|
||||
local first_diff = 0
|
||||
|
||||
while (apos <= a:len() and not isdigit(a, apos)) or (bpos <= b:len() and not isdigit(b, bpos)) do
|
||||
local ac = char_order(a, apos)
|
||||
local bc = char_order(b, bpos)
|
||||
while (apos <= a:len() and not isdigit(a, apos)) or (bpos <= b:len() and not isdigit(b, bpos)) do
|
||||
local ac = char_order(a, apos)
|
||||
local bc = char_order(b, bpos)
|
||||
|
||||
if ac ~= bc then
|
||||
if ac ~= bc then
|
||||
return ac > bc
|
||||
end
|
||||
end
|
||||
|
||||
apos = apos + 1
|
||||
bpos = bpos + 1
|
||||
end
|
||||
apos = apos + 1
|
||||
bpos = bpos + 1
|
||||
end
|
||||
|
||||
while a:sub(apos, apos) == '0' do
|
||||
apos = apos + 1
|
||||
end
|
||||
while a:sub(apos, apos) == '0' do
|
||||
apos = apos + 1
|
||||
end
|
||||
|
||||
while b:sub(bpos, bpos) == '0' do
|
||||
bpos = bpos + 1
|
||||
end
|
||||
while b:sub(bpos, bpos) == '0' do
|
||||
bpos = bpos + 1
|
||||
end
|
||||
|
||||
while isdigit(a, apos) and isdigit(b, bpos) do
|
||||
if first_diff == 0 then
|
||||
while isdigit(a, apos) and isdigit(b, bpos) do
|
||||
if first_diff == 0 then
|
||||
first_diff = char_value(a, apos) - char_value(b, bpos)
|
||||
end
|
||||
end
|
||||
|
||||
apos = apos + 1
|
||||
bpos = bpos + 1
|
||||
end
|
||||
apos = apos + 1
|
||||
bpos = bpos + 1
|
||||
end
|
||||
|
||||
if isdigit(a, apos) then
|
||||
return true
|
||||
end
|
||||
if isdigit(a, apos) then
|
||||
return true
|
||||
end
|
||||
|
||||
if isdigit(b, bpos) then
|
||||
return false
|
||||
end
|
||||
if isdigit(b, bpos) then
|
||||
return false
|
||||
end
|
||||
|
||||
if first_diff ~= 0 then
|
||||
return first_diff > 0
|
||||
end
|
||||
end
|
||||
if first_diff ~= 0 then
|
||||
return first_diff > 0
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -11,8 +11,8 @@ local autoupdater_version = require('autoupdater.version')
|
|||
|
||||
|
||||
if not platform_info.get_image_name() then
|
||||
io.stderr:write("The autoupdater doesn't support this hardware model.\n")
|
||||
os.exit(1)
|
||||
io.stderr:write("The autoupdater doesn't support this hardware model.\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
|
||||
|
@ -38,30 +38,30 @@ local fallback = false
|
|||
local mirrors = {}
|
||||
|
||||
local function parse_args()
|
||||
local i = 1
|
||||
while arg[i] do
|
||||
if arg[i] == '-f' then
|
||||
force = true
|
||||
elseif arg[i] == '--fallback' then
|
||||
fallback = true
|
||||
elseif arg[i] == '-b' then
|
||||
i = i+1
|
||||
local i = 1
|
||||
while arg[i] do
|
||||
if arg[i] == '-f' then
|
||||
force = true
|
||||
elseif arg[i] == '--fallback' then
|
||||
fallback = true
|
||||
elseif arg[i] == '-b' then
|
||||
i = i+1
|
||||
|
||||
if not arg[i] then
|
||||
io.stderr:write("Error parsing command line: expected branch name\n")
|
||||
os.exit(1)
|
||||
end
|
||||
if not arg[i] then
|
||||
io.stderr:write("Error parsing command line: expected branch name\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
branch_name = arg[i]
|
||||
elseif arg[i]:sub(0, 1) == '-' then
|
||||
io.stderr:write("Error parsing command line: unexpected argument '" .. arg[i] .. "'\n")
|
||||
os.exit(1)
|
||||
else
|
||||
table.insert(mirrors, arg[i])
|
||||
end
|
||||
branch_name = arg[i]
|
||||
elseif arg[i]:sub(0, 1) == '-' then
|
||||
io.stderr:write("Error parsing command line: unexpected argument '" .. arg[i] .. "'\n")
|
||||
os.exit(1)
|
||||
else
|
||||
table.insert(mirrors, arg[i])
|
||||
end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -70,274 +70,274 @@ parse_args()
|
|||
|
||||
local branch = uci:get_all('autoupdater', branch_name)
|
||||
if not branch then
|
||||
io.stderr:write("Can't find configuration for branch '" .. branch_name .. "'\n")
|
||||
os.exit(1)
|
||||
io.stderr:write("Can't find configuration for branch '" .. branch_name .. "'\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
|
||||
if settings.enabled ~= '1' and not force then
|
||||
io.stderr:write('autoupdater is disabled.\n')
|
||||
os.exit(0)
|
||||
io.stderr:write('autoupdater is disabled.\n')
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
|
||||
-- Verifies a file given as a list of lines with a list of signatures using ecdsaverify
|
||||
local function verify_lines(lines, sigs)
|
||||
local command = {'ecdsaverify', '-n', tostring(branch.good_signatures)}
|
||||
local command = {'ecdsaverify', '-n', tostring(branch.good_signatures)}
|
||||
|
||||
-- Build command line from sigs and branch.pubkey
|
||||
for _, sig in ipairs(sigs) do
|
||||
if sig:match('^' .. string.rep('%x', 128) .. '$') then
|
||||
table.insert(command, '-s')
|
||||
table.insert(command, sig)
|
||||
end
|
||||
end
|
||||
-- Build command line from sigs and branch.pubkey
|
||||
for _, sig in ipairs(sigs) do
|
||||
if sig:match('^' .. string.rep('%x', 128) .. '$') then
|
||||
table.insert(command, '-s')
|
||||
table.insert(command, sig)
|
||||
end
|
||||
end
|
||||
|
||||
for _, key in ipairs(branch.pubkey) do
|
||||
if key:match('^' .. string.rep('%x', 64) .. '$') then
|
||||
table.insert(command, '-p')
|
||||
table.insert(command, key)
|
||||
end
|
||||
end
|
||||
for _, key in ipairs(branch.pubkey) do
|
||||
if key:match('^' .. string.rep('%x', 64) .. '$') then
|
||||
table.insert(command, '-p')
|
||||
table.insert(command, key)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Call ecdsautils
|
||||
local pid, f = autoupdater_util.popen(true, unpack(command))
|
||||
-- Call ecdsautils
|
||||
local pid, f = autoupdater_util.popen(true, unpack(command))
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
f:write(line)
|
||||
f:write('\n')
|
||||
end
|
||||
for _, line in ipairs(lines) do
|
||||
f:write(line)
|
||||
f:write('\n')
|
||||
end
|
||||
|
||||
f:close()
|
||||
f:close()
|
||||
|
||||
|
||||
local wpid, status, code = nixio.waitpid(pid)
|
||||
return wpid and status == 'exited' and code == 0
|
||||
local wpid, status, code = nixio.waitpid(pid)
|
||||
return wpid and status == 'exited' and code == 0
|
||||
end
|
||||
|
||||
|
||||
-- Downloads, parses and verifies the update manifest from a mirror
|
||||
-- Returns a table with the fields version, checksum and filename if everything is ok, nil otherwise
|
||||
local function read_manifest(mirror)
|
||||
local sep = false
|
||||
local sep = false
|
||||
|
||||
local lines = {}
|
||||
local sigs = {}
|
||||
local lines = {}
|
||||
local sigs = {}
|
||||
|
||||
local branch_ok = false
|
||||
local branch_ok = false
|
||||
|
||||
local ret = {}
|
||||
local ret = {}
|
||||
|
||||
-- Remove potential trailing slash
|
||||
mirror = mirror:gsub('/$', '')
|
||||
-- Remove potential trailing slash
|
||||
mirror = mirror:gsub('/$', '')
|
||||
|
||||
|
||||
local starttime = os.time()
|
||||
local pid, manifest_loader = autoupdater_util.popen(false, 'wget', '-T', '120', '-O-', string.format('%s/%s.manifest', mirror, branch.name))
|
||||
local starttime = os.time()
|
||||
local pid, manifest_loader = autoupdater_util.popen(false, 'wget', '-T', '120', '-O-', string.format('%s/%s.manifest', mirror, branch.name))
|
||||
|
||||
local data = ''
|
||||
local data = ''
|
||||
|
||||
-- Read all lines from the manifest
|
||||
-- The upper part is saved to lines, the lower part to sigs
|
||||
while true do
|
||||
-- If the manifest download takes more than 5 minutes, we don't really
|
||||
-- have a chance to download a whole image
|
||||
local timeout = starttime+300 - os.time()
|
||||
if timeout < 0 or not nixio.poll({{fd = manifest_loader, events = nixio.poll_flags('in')}}, timeout * 1000) then
|
||||
io.stderr:write("Timeout while reading manifest.\n")
|
||||
nixio.kill(pid, nixio.const.SIGTERM)
|
||||
manifest_loader:close()
|
||||
return nil
|
||||
end
|
||||
-- Read all lines from the manifest
|
||||
-- The upper part is saved to lines, the lower part to sigs
|
||||
while true do
|
||||
-- If the manifest download takes more than 5 minutes, we don't really
|
||||
-- have a chance to download a whole image
|
||||
local timeout = starttime+300 - os.time()
|
||||
if timeout < 0 or not nixio.poll({{fd = manifest_loader, events = nixio.poll_flags('in')}}, timeout * 1000) then
|
||||
io.stderr:write("Timeout while reading manifest.\n")
|
||||
nixio.kill(pid, nixio.const.SIGTERM)
|
||||
manifest_loader:close()
|
||||
return nil
|
||||
end
|
||||
|
||||
local r = manifest_loader:read(1024)
|
||||
if not r or r == '' then
|
||||
break
|
||||
end
|
||||
data = data .. r
|
||||
local r = manifest_loader:read(1024)
|
||||
if not r or r == '' then
|
||||
break
|
||||
end
|
||||
data = data .. r
|
||||
|
||||
while data:match('\n') do
|
||||
local line, rest = data:match('^([^\n]*)\n(.*)$')
|
||||
data = rest
|
||||
while data:match('\n') do
|
||||
local line, rest = data:match('^([^\n]*)\n(.*)$')
|
||||
data = rest
|
||||
|
||||
if not sep then
|
||||
if line == '---' then
|
||||
sep = true
|
||||
else
|
||||
table.insert(lines, line)
|
||||
if not sep then
|
||||
if line == '---' then
|
||||
sep = true
|
||||
else
|
||||
table.insert(lines, line)
|
||||
|
||||
if line == ('BRANCH=' .. branch.name) then
|
||||
branch_ok = true
|
||||
end
|
||||
if line == ('BRANCH=' .. branch.name) then
|
||||
branch_ok = true
|
||||
end
|
||||
|
||||
local date = line:match('^DATE=(.+)$')
|
||||
local priority = line:match('^PRIORITY=([%d%.]+)$')
|
||||
local model, version, checksum, filename = line:match('^([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)$')
|
||||
local date = line:match('^DATE=(.+)$')
|
||||
local priority = line:match('^PRIORITY=([%d%.]+)$')
|
||||
local model, version, checksum, filename = line:match('^([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)$')
|
||||
|
||||
if date then
|
||||
ret.date = autoupdater_util.parse_date(date)
|
||||
elseif priority then
|
||||
ret.priority = tonumber(priority)
|
||||
elseif model == platform_info.get_image_name() then
|
||||
ret.version = version
|
||||
ret.checksum = checksum
|
||||
ret.filename = filename
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(sigs, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
manifest_loader:close()
|
||||
if date then
|
||||
ret.date = autoupdater_util.parse_date(date)
|
||||
elseif priority then
|
||||
ret.priority = tonumber(priority)
|
||||
elseif model == platform_info.get_image_name() then
|
||||
ret.version = version
|
||||
ret.checksum = checksum
|
||||
ret.filename = filename
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(sigs, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
manifest_loader:close()
|
||||
|
||||
-- Do some very basic checks before checking the signatures
|
||||
-- (as the signature verification is computationally expensive)
|
||||
if not sep then
|
||||
io.stderr:write('There seems to have gone something wrong downloading the manifest from ' .. mirror .. '\n')
|
||||
return nil
|
||||
end
|
||||
-- Do some very basic checks before checking the signatures
|
||||
-- (as the signature verification is computationally expensive)
|
||||
if not sep then
|
||||
io.stderr:write('There seems to have gone something wrong downloading the manifest from ' .. mirror .. '\n')
|
||||
return nil
|
||||
end
|
||||
|
||||
if not ret.date or not ret.priority then
|
||||
io.stderr:write('The manifest downloaded from ' .. mirror .. ' is invalid (DATE or PRIORITY missing)\n')
|
||||
return nil
|
||||
end
|
||||
if not ret.date or not ret.priority then
|
||||
io.stderr:write('The manifest downloaded from ' .. mirror .. ' is invalid (DATE or PRIORITY missing)\n')
|
||||
return nil
|
||||
end
|
||||
|
||||
if not branch_ok then
|
||||
io.stderr:write('Wrong branch. We are on ', branch.name, '.\n')
|
||||
return nil
|
||||
end
|
||||
if not branch_ok then
|
||||
io.stderr:write('Wrong branch. We are on ', branch.name, '.\n')
|
||||
return nil
|
||||
end
|
||||
|
||||
if not ret.version then
|
||||
io.stderr:write('No matching firmware found (model ' .. platform_info.get_image_name() .. ')\n')
|
||||
return nil
|
||||
end
|
||||
if not ret.version then
|
||||
io.stderr:write('No matching firmware found (model ' .. platform_info.get_image_name() .. ')\n')
|
||||
return nil
|
||||
end
|
||||
|
||||
if not verify_lines(lines, sigs) then
|
||||
io.stderr:write('Not enough valid signatures!\n')
|
||||
return nil
|
||||
end
|
||||
if not verify_lines(lines, sigs) then
|
||||
io.stderr:write('Not enough valid signatures!\n')
|
||||
return nil
|
||||
end
|
||||
|
||||
return ret
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
-- Downloads the firmware image from a mirror to a given output file
|
||||
local function fetch_firmware(mirror, filename, output)
|
||||
-- Let's give the image download 30 minutes, hopefully more than enough
|
||||
if autoupdater_util.exec(1800, 'wget', '-T', '120', '-O', output, mirror .. '/' .. filename) ~= 0 then
|
||||
io.stderr:write('Error downloading the image from ' .. mirror .. '\n')
|
||||
return false
|
||||
end
|
||||
-- Let's give the image download 30 minutes, hopefully more than enough
|
||||
if autoupdater_util.exec(1800, 'wget', '-T', '120', '-O', output, mirror .. '/' .. filename) ~= 0 then
|
||||
io.stderr:write('Error downloading the image from ' .. mirror .. '\n')
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Returns the computed update probability
|
||||
local function get_probability(date, priority)
|
||||
local seconds = priority * 86400
|
||||
local diff = os.difftime(os.time(), date)
|
||||
local seconds = priority * 86400
|
||||
local diff = os.difftime(os.time(), date)
|
||||
|
||||
if diff < 0 then
|
||||
-- When the difference is negative, there are two possibilities: The manifest contains a wrong date, or our own clock is wrong.
|
||||
-- As there isn't anything useful to do for an incorrect manifest, we'll assume the latter case and update anyways as we
|
||||
-- can't do anything better
|
||||
io.stderr:write('Warning: clock seems to be incorrect.\n')
|
||||
if diff < 0 then
|
||||
-- When the difference is negative, there are two possibilities: The manifest contains a wrong date, or our own clock is wrong.
|
||||
-- As there isn't anything useful to do for an incorrect manifest, we'll assume the latter case and update anyways as we
|
||||
-- can't do anything better
|
||||
io.stderr:write('Warning: clock seems to be incorrect.\n')
|
||||
|
||||
if tonumber(fs.readfile('/proc/uptime'):match('^([^ ]+) ')) < 600 then
|
||||
-- If the uptime is very low, it's possible we just didn't get the time over NTP yet, so we'll just wait until the next time the updater runs
|
||||
return 0
|
||||
else
|
||||
-- Will give 1 when priority == 0, and lower probabilities the higher the priority value is
|
||||
-- (similar to the old static probability system)
|
||||
return 0.75^priority
|
||||
end
|
||||
if tonumber(fs.readfile('/proc/uptime'):match('^([^ ]+) ')) < 600 then
|
||||
-- If the uptime is very low, it's possible we just didn't get the time over NTP yet, so we'll just wait until the next time the updater runs
|
||||
return 0
|
||||
else
|
||||
-- Will give 1 when priority == 0, and lower probabilities the higher the priority value is
|
||||
-- (similar to the old static probability system)
|
||||
return 0.75^priority
|
||||
end
|
||||
|
||||
elseif fallback then
|
||||
if diff >= seconds + 86400 then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif fallback then
|
||||
if diff >= seconds + 86400 then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
|
||||
elseif diff >= seconds then
|
||||
return 1
|
||||
elseif diff >= seconds then
|
||||
return 1
|
||||
|
||||
else
|
||||
local x = diff/seconds
|
||||
-- This is the most simple polynomial with value 0 at 0, 1 at 1, and whose first derivative is 0 at both 0 and 1
|
||||
-- (we all love continuously differentiable functions, right?)
|
||||
return (-2)*x^3 + 3*x^2
|
||||
end
|
||||
else
|
||||
local x = diff/seconds
|
||||
-- This is the most simple polynomial with value 0 at 0, 1 at 1, and whose first derivative is 0 at both 0 and 1
|
||||
-- (we all love continuously differentiable functions, right?)
|
||||
return (-2)*x^3 + 3*x^2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Tries to perform an update from a given mirror
|
||||
local function autoupdate(mirror)
|
||||
local download_d_dir = '/usr/lib/autoupdater/download.d'
|
||||
local abort_d_dir = '/usr/lib/autoupdater/abort.d'
|
||||
local upgrade_d_dir = '/usr/lib/autoupdater/upgrade.d'
|
||||
local download_d_dir = '/usr/lib/autoupdater/download.d'
|
||||
local abort_d_dir = '/usr/lib/autoupdater/abort.d'
|
||||
local upgrade_d_dir = '/usr/lib/autoupdater/upgrade.d'
|
||||
|
||||
|
||||
local manifest = read_manifest(mirror)
|
||||
if not manifest then
|
||||
return false
|
||||
end
|
||||
local manifest = read_manifest(mirror)
|
||||
if not manifest then
|
||||
return false
|
||||
end
|
||||
|
||||
if not autoupdater_version.newer_than(manifest.version, old_version) then
|
||||
io.stderr:write('No new firmware available.\n')
|
||||
return true
|
||||
end
|
||||
if not autoupdater_version.newer_than(manifest.version, old_version) then
|
||||
io.stderr:write('No new firmware available.\n')
|
||||
return true
|
||||
end
|
||||
|
||||
io.stderr:write('New version available.\n')
|
||||
io.stderr:write('New version available.\n')
|
||||
|
||||
|
||||
if not force and math.random() >= get_probability(manifest.date, manifest.priority) then
|
||||
io.stderr:write('No autoupdate this time. Use -f to override.\n')
|
||||
return true
|
||||
end
|
||||
if not force and math.random() >= get_probability(manifest.date, manifest.priority) then
|
||||
io.stderr:write('No autoupdate this time. Use -f to override.\n')
|
||||
return true
|
||||
end
|
||||
|
||||
autoupdater_util.run_dir(download_d_dir)
|
||||
collectgarbage()
|
||||
autoupdater_util.run_dir(download_d_dir)
|
||||
collectgarbage()
|
||||
|
||||
local image = os.tmpname()
|
||||
if not fetch_firmware(mirror, manifest.filename, image) then
|
||||
autoupdater_util.run_dir(abort_d_dir)
|
||||
return false
|
||||
end
|
||||
local image = os.tmpname()
|
||||
if not fetch_firmware(mirror, manifest.filename, image) then
|
||||
autoupdater_util.run_dir(abort_d_dir)
|
||||
return false
|
||||
end
|
||||
|
||||
local popen = io.popen(string.format("exec sha512sum '%s'", image))
|
||||
local checksum = popen:read('*l'):match('^%x+')
|
||||
popen:close()
|
||||
if checksum ~= manifest.checksum then
|
||||
io.stderr:write('Invalid image checksum!\n')
|
||||
os.remove(image)
|
||||
autoupdater_util.run_dir(abort_d_dir)
|
||||
return false
|
||||
end
|
||||
local popen = io.popen(string.format("exec sha512sum '%s'", image))
|
||||
local checksum = popen:read('*l'):match('^%x+')
|
||||
popen:close()
|
||||
if checksum ~= manifest.checksum then
|
||||
io.stderr:write('Invalid image checksum!\n')
|
||||
os.remove(image)
|
||||
autoupdater_util.run_dir(abort_d_dir)
|
||||
return false
|
||||
end
|
||||
|
||||
autoupdater_util.run_dir(upgrade_d_dir)
|
||||
autoupdater_util.run_dir(upgrade_d_dir)
|
||||
|
||||
io.stderr:write('Upgrading firmware...\n')
|
||||
local null = nixio.open('/dev/null', 'w+')
|
||||
if null then
|
||||
nixio.dup(null, nixio.stdin)
|
||||
nixio.dup(null, nixio.stderr)
|
||||
if null:fileno() > 2 then
|
||||
null:close()
|
||||
end
|
||||
end
|
||||
io.stderr:write('Upgrading firmware...\n')
|
||||
local null = nixio.open('/dev/null', 'w+')
|
||||
if null then
|
||||
nixio.dup(null, nixio.stdin)
|
||||
nixio.dup(null, nixio.stderr)
|
||||
if null:fileno() > 2 then
|
||||
null:close()
|
||||
end
|
||||
end
|
||||
|
||||
nixio.exec('/sbin/sysupgrade', image)
|
||||
nixio.exec('/sbin/sysupgrade', image)
|
||||
|
||||
-- This should never be reached as nixio.exec replaces the autoupdater process unless /sbin/sysupgrade can't be executed
|
||||
-- We output the error message through stdout as stderr isn't available anymore
|
||||
io.write('Failed to call sysupgrade?\n')
|
||||
os.remove(image)
|
||||
autoupdater_util.run_dir(abort_d_dir)
|
||||
os.exit(1)
|
||||
-- This should never be reached as nixio.exec replaces the autoupdater process unless /sbin/sysupgrade can't be executed
|
||||
-- We output the error message through stdout as stderr isn't available anymore
|
||||
io.write('Failed to call sysupgrade?\n')
|
||||
os.remove(image)
|
||||
autoupdater_util.run_dir(abort_d_dir)
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
|
||||
|
@ -345,24 +345,24 @@ local lockfile = '/var/lock/autoupdater.lock'
|
|||
local lockfd = nixio.open(lockfile, 'w', 'rw-------')
|
||||
|
||||
if not lockfd:lock('tlock') then
|
||||
io.stderr:write(string.format(
|
||||
"Unable to lock file %s. Make sure there is no other instance of the autoupdater running.\n",
|
||||
lockfile, err
|
||||
))
|
||||
os.exit(1)
|
||||
io.stderr:write(string.format(
|
||||
"Unable to lock file %s. Make sure there is no other instance of the autoupdater running.\n",
|
||||
lockfile, err
|
||||
))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
|
||||
if #mirrors == 0 then
|
||||
while #branch.mirror > 0 do
|
||||
table.insert(mirrors, table.remove(branch.mirror, math.random(#branch.mirror)))
|
||||
end
|
||||
while #branch.mirror > 0 do
|
||||
table.insert(mirrors, table.remove(branch.mirror, math.random(#branch.mirror)))
|
||||
end
|
||||
end
|
||||
|
||||
for k, mirror in ipairs(mirrors) do
|
||||
if autoupdate(mirror) then
|
||||
os.exit(0)
|
||||
end
|
||||
if autoupdate(mirror) then
|
||||
os.exit(0)
|
||||
end
|
||||
end
|
||||
|
||||
io.stderr:write('No usable mirror found.\n')
|
||||
|
|
Loading…
Reference in New Issue