Merge pull request #128 from freifunk-gluon/autoupdater
Autoupdater improvements
This commit is contained in:
commit
50c763b895
|
@ -0,0 +1,6 @@
|
|||
Executable files in abort.d will be executed if downloading the upgrade
|
||||
image has failed and should revert the actions from download.d.
|
||||
|
||||
We can't really do anything when the download has succeeded, but the
|
||||
flashing has failed, as the autoupdater process will have been replaced by
|
||||
sysupgrade by then.
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
sync
|
||||
sysctl -w vm.drop_caches=3
|
|
@ -0,0 +1,2 @@
|
|||
Executable files in download.d will be executed after the update manifest
|
||||
has been verified, but before the actual image is downloaded.
|
|
@ -1,13 +1,28 @@
|
|||
local io = io
|
||||
local math = math
|
||||
local nixio = require 'nixio'
|
||||
local fs = require 'nixio.fs'
|
||||
local util = require 'nixio.util'
|
||||
|
||||
|
||||
module 'autoupdater.util'
|
||||
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(...)
|
||||
local pid, errno, error = nixio.fork()
|
||||
if pid == 0 then
|
||||
nixio.execp(...)
|
||||
os.exit(127)
|
||||
elseif pid > 0 then
|
||||
local wpid, status, code = nixio.waitpid(pid)
|
||||
return wpid and status == 'exited' and code
|
||||
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(command)
|
||||
function popen(...)
|
||||
local inr, inw = nixio.pipe()
|
||||
local pid = nixio.fork()
|
||||
|
||||
|
@ -21,7 +36,41 @@ function popen(command)
|
|||
inr:close()
|
||||
inw:close()
|
||||
|
||||
nixio.exec('/bin/sh', '-c', command)
|
||||
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 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
|
||||
|
||||
local files = util.consume(fs.dir(dir))
|
||||
if not files then
|
||||
return
|
||||
end
|
||||
|
||||
table.sort(files)
|
||||
|
||||
for _, entry in ipairs(files) do
|
||||
if is_ok(entry) then
|
||||
exec(dir .. '/' .. entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -80,24 +80,26 @@ 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 = string.format('ecdsaverify -n %i', 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
|
||||
command = command .. ' -s ' .. sig
|
||||
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
|
||||
command = command .. ' -p ' .. key
|
||||
table.insert(command, '-p')
|
||||
table.insert(command, key)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Call ecdsautils
|
||||
local pid, f = autoupdater_util.popen(command)
|
||||
local pid, f = autoupdater_util.popen(unpack(command))
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
f:write(line)
|
||||
|
@ -126,7 +128,7 @@ local function read_manifest(mirror)
|
|||
|
||||
-- Read all lines from the manifest
|
||||
-- The upper part is saves to lines, the lower part to sigs
|
||||
for line in io.popen(string.format("wget -T 120 -O- '%s/%s.manifest'", mirror, branch.name), 'r'):lines() do
|
||||
for line in io.popen(string.format("exec wget -T 120 -O- '%s/%s.manifest'", mirror, branch.name), 'r'):lines() do
|
||||
if not sep then
|
||||
if line == '---' then
|
||||
sep = true
|
||||
|
@ -189,7 +191,7 @@ end
|
|||
|
||||
-- Downloads the firmware image from a mirror to a given output file
|
||||
local function fetch_firmware(mirror, filename, output)
|
||||
if os.execute(string.format("wget -T 120 -O '%s' '%s/%s'", output, mirror, filename)) ~= 0 then
|
||||
if autoupdater_util.exec('wget', '-T', '120', '-O', output, mirror .. '/' .. filename) ~= 0 then
|
||||
io.stderr:write('Error downloading the image from ' .. mirror .. '\n')
|
||||
return false
|
||||
end
|
||||
|
@ -239,6 +241,10 @@ 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 manifest = read_manifest(mirror)
|
||||
if not manifest then
|
||||
return false
|
||||
|
@ -257,21 +263,22 @@ local function autoupdate(mirror)
|
|||
return true
|
||||
end
|
||||
|
||||
|
||||
os.execute('sync; sysctl -w vm.drop_caches=3')
|
||||
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 popen = io.popen(string.format("sha512sum '%s'", image))
|
||||
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
|
||||
|
||||
|
@ -291,6 +298,7 @@ local function autoupdate(mirror)
|
|||
-- 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
|
||||
|
||||
|
|
Loading…
Reference in New Issue