mirror of
https://github.com/Doodle3D/doodle3d-firmware.git
synced 2024-11-16 02:37:57 +01:00
b6c983f446
Improved logging.
590 lines
21 KiB
Lua
Executable File
590 lines
21 KiB
Lua
Executable File
#!/usr/bin/env lua
|
|
|
|
-- TODO/NOTES:
|
|
-- M.checkValidImage(verEnt) -> doet exists+fileSize/MD5 check
|
|
-- after download: (can use checkValidImage for this)
|
|
-- - remove file on fail
|
|
-- - check size or md5 and remove file on mismatch [osx: md5 -q <file>]
|
|
-- add to status: validImage: none|<version> (can use checkValidImage for this)
|
|
-- any more TODO's across this file?
|
|
-- max 1 image tegelijk (moet api doen), en rekening houden met printbuffer (printen blokkeren?)
|
|
|
|
-- MAYBE/LATER:
|
|
-- add API calls to retrieve a list of all versions with their info (i.e., the result of getAvailableVersions)
|
|
-- wget: add provision (in verbose mode?) to use -v instead of -q and disable output redirection
|
|
-- wget: configurable timeout?
|
|
-- max cache lifetime for index file?
|
|
-- document index file format (Version first, then in any order: Files: sysup; factory, FileSize: sysup; factory, MD5: sysup; factory, ChangelogStart:, ..., ChangelogEnd:)
|
|
-- remove /etc/wifibox-version on macbook...
|
|
-- copy improved fileSize back to utils (add unit tests!)
|
|
-- create new utils usable by updater as well as api? (remove dependencies on uci and logger etc)
|
|
-- note: take care not to print any text in module functions, as this breaks http responses
|
|
-- change representation of sysupgrade/factory info in versionInfo? (and also in image index?) <- create api call to get all info on all versions?
|
|
|
|
local M = {}
|
|
|
|
-- NOTE: 'INSTALLED' will never be returned (and probably neither will 'INSTALLING') since in that case the device is flashing or rebooting
|
|
M.STATE = { NONE = 1, DOWNLOADING = 2, DOWNLOAD_FAILED = 3, IMAGE_READY = 4, INSTALLING = 5, INSTALLED = 6, INSTALL_FAILED = 7 }
|
|
M.STATE_NAMES = {
|
|
[M.STATE.NONE] = 'none', [M.STATE.DOWNLOADING] = 'downloading', [M.STATE.DOWNLOAD_FAILED] = 'download_failed', [M.STATE.IMAGE_READY] = 'image_ready',
|
|
[M.STATE.INSTALLING] = 'installing', [M.STATE.INSTALLED] = 'installed', [M.STATE.INSTALL_FAILED] = 'install_failed'
|
|
}
|
|
|
|
M.DEFAULT_BASE_URL = 'http://doodle3d.com/updates'
|
|
--M.DEFAULT_BASE_URL = 'http://localhost/~USERNAME/wifibox/updates'
|
|
M.IMAGE_INDEX_FILE = 'wifibox-image.index'
|
|
M.CACHE_PATH = '/tmp/d3d-updater'
|
|
M.STATE_FILE = 'update-state'
|
|
M.WGET_OPTIONS = "-q -t 1 -T 30"
|
|
--M.WGET_OPTIONS = "-v -t 1 -T 30"
|
|
|
|
local verbosity = 0
|
|
local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger
|
|
|
|
|
|
|
|
|
|
---------------------
|
|
-- LOCAL FUNCTIONS --
|
|
---------------------
|
|
|
|
-- use level==1 for important messages, 0 for regular messages and -1 for less important messages
|
|
local function P(lvl, msg)
|
|
if log then
|
|
if lvl == -1 then log:debug(msg)
|
|
elseif lvl == 0 or lvl == 1 then log:info(msg)
|
|
end
|
|
else
|
|
if (-lvl <= verbosity) then print(msg) end
|
|
end
|
|
end
|
|
|
|
local function D(msg) P(-1, (log and msg or "(DBG) " .. msg)) end
|
|
|
|
local function E(msg)
|
|
if log then log:error(msg)
|
|
else io.stderr:write(msg .. '\n')
|
|
end
|
|
end
|
|
|
|
-- dontShift is optional
|
|
-- Note: os.execute() return value is shifted one byte to the left, this function
|
|
-- takes that fact into account, unless dontShift is true.
|
|
local function wgetStatusToString(exitStatus, dontShift)
|
|
if not dontShift then exitStatus = exitStatus / 256 end
|
|
-- adapted from man(1) wget on OSX
|
|
local statusTexts = {
|
|
['0'] = 'Ok',
|
|
['1'] = 'Generic error',
|
|
['2'] = 'Parse error', -- for instance, when parsing command-line options, the .wgetrc or .netrc...
|
|
['3'] = 'File I/O error',
|
|
['4'] = 'Network failure',
|
|
['5'] = 'SSL verification failure',
|
|
['6'] = 'Username/password authentication failure',
|
|
['7'] = 'Protocol error',
|
|
['8'] = 'Server issued an error response'
|
|
}
|
|
local result = statusTexts[tostring(exitStatus)]
|
|
|
|
if result then return exitStatus .. ": " .. result
|
|
else return exitStatus
|
|
end
|
|
end
|
|
|
|
local function createCacheDirectory()
|
|
if os.execute('mkdir -p ' .. M.CACHE_PATH) ~= 0 then
|
|
return nil,"Error: could not create cache directory '" .. M.CACHE_PATH .. "'"
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function getState()
|
|
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'r')
|
|
if not file then return M.STATE.NONE,"" end
|
|
|
|
local state = file:read('*a')
|
|
file:close()
|
|
local code,msg = string.match(state, '([^|]+)|+(.*)')
|
|
return code,msg
|
|
end
|
|
|
|
-- NOTE: make sure the cache directory exists before calling this function or it will fail.
|
|
-- NOTE: this function _can_ fail but we don't expect this to happen so the return value is ignored for now
|
|
local function setState(code, msg)
|
|
local s = code .. '|' .. msg
|
|
D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')")
|
|
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'w')
|
|
|
|
if not file then
|
|
E("error: could not open state file for writing (" .. msg .. ")")
|
|
return false
|
|
end
|
|
|
|
file:write(s)
|
|
file:close()
|
|
return true
|
|
end
|
|
|
|
-- trim whitespace from both ends of string (from http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76)
|
|
local function trim(s)
|
|
if type(s) ~= 'string' then return s end
|
|
return (s:find('^%s*$') and '' or s:match('^%s*(.*%S)'))
|
|
end
|
|
|
|
-- from utils.lua
|
|
local function readFile(filePath, trimResult)
|
|
local f, msg, nr = io.open(filePath, 'r')
|
|
if not f then return nil,msg,nr end
|
|
|
|
local res = f:read('*all')
|
|
f:close()
|
|
|
|
if trimResult then
|
|
res = trim(res)
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- from utils.lua
|
|
local function exists(file)
|
|
if not file or type(file) ~= 'string' or file:len() == 0 then
|
|
return nil, "file must be a non-empty string"
|
|
end
|
|
|
|
local r = io.open(file, 'r') -- ignore returned message
|
|
if r then r:close() end
|
|
return r ~= nil
|
|
end
|
|
|
|
-- from utils.lua
|
|
--argument: either an open file or a filename
|
|
local function fileSize(file)
|
|
local size = nil
|
|
if type(file) == 'file' then
|
|
local current = file:seek()
|
|
size = file:seek('end')
|
|
file:seek('set', current)
|
|
elseif type(file) == 'string' then
|
|
local f = io.open(file)
|
|
if f then
|
|
size = f:seek('end')
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
return size
|
|
end
|
|
|
|
|
|
-- returns return value of command
|
|
local function runCommand(command, dryRun) D("about to run: '" .. command .. "'"); return (not dryRun) and os.execute(command) or 0 end
|
|
|
|
-- returns return value of wget (or nil if saveDir is nil or empty), filename is optional
|
|
-- NOTE: leaving out filename will cause issues with files not being overwritten but suffixed with '.1', '.2',etc instead
|
|
local function downloadFile(url, saveDir, filename)
|
|
if not saveDir or saveDir:len() == 0 then return nil, "saveDir must be non-empty" end
|
|
local outArg = (filename:len() > 0) and (' -O' .. filename) or ''
|
|
if filename:len() > 0 then
|
|
return runCommand('wget ' .. M.WGET_OPTIONS .. ' -O ' .. saveDir .. '/' .. filename .. ' ' .. url .. ' 2> /dev/null')
|
|
else
|
|
return runCommand('wget ' .. M.WGET_OPTIONS .. ' -P ' .. saveDir .. ' ' .. url .. ' 2> /dev/null')
|
|
end
|
|
end
|
|
|
|
local function parseCommandlineArguments(arglist)
|
|
local result = { verbosity = 0, baseUrl = M.DEFAULT_BASE_URL, action = nil }
|
|
local nextIsVersion, nextIsUrl = false, false
|
|
for index,argument in ipairs(arglist) do
|
|
if nextIsVersion then
|
|
result.version = argument; nextIsVersion = false
|
|
elseif nextIsUrl then
|
|
result.baseUrl = argument; nextIsUrl = false
|
|
else
|
|
if argument == '-h' then result.action = 'showHelp'
|
|
elseif argument == '-q' then result.verbosity = -1
|
|
elseif argument == '-V' then result.verbosity = 1
|
|
elseif argument == '-c' then result.useCache = true
|
|
elseif argument == '-C' then result.useCache = false
|
|
elseif argument == '-u' then nextIsUrl = true
|
|
elseif argument == '-v' then result.action = 'showCurrentVersion'
|
|
elseif argument == '-s' then result.action = 'showStatus'
|
|
elseif argument == '-l' then result.action = 'showAvailableVersions'
|
|
elseif argument == '-i' then result.action = 'showVersionInfo'; nextIsVersion = true
|
|
elseif argument == '-d' then result.action = 'imageDownload'; nextIsVersion = true
|
|
elseif argument == '-f' then result.action = 'imageInstall'; nextIsVersion = true
|
|
elseif argument == '-r' then result.action = 'clear'
|
|
else return nil,"unrecognized argument '" .. argument .. "'"
|
|
end
|
|
end
|
|
end
|
|
|
|
if result.version then
|
|
result.version = M.parseVersion(result.version)
|
|
if not result.version then
|
|
return nil,"error parsing specified version"
|
|
end
|
|
end
|
|
|
|
if nextIsVersion then return nil, "missing required version argument" end
|
|
if nextIsUrl then return nil, "missing required URL argument" end
|
|
|
|
return result
|
|
end
|
|
|
|
|
|
|
|
|
|
----------------------
|
|
-- MODULE FUNCTIONS --
|
|
----------------------
|
|
|
|
function M.setLogger(logger)
|
|
log = logger
|
|
end
|
|
|
|
-- baseUrl and useCache are optional
|
|
function M.getStatus(baseUrl, useCache)
|
|
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
|
local result = {}
|
|
|
|
local verTable,msg = M.getAvailableVersions(baseUrl, useCache)
|
|
if not verTable then return nil,msg end
|
|
|
|
local newest = verTable[#verTable]
|
|
result.currentVersion = M.getCurrentVersion()
|
|
result.newestVersion = newest.version
|
|
result.stateCode, result.stateText = getState()
|
|
result.stateCode = tonumber(result.stateCode)
|
|
|
|
if result.stateCode == M.STATE.DOWNLOADING then
|
|
result.progress = fileSize(M.CACHE_PATH .. '/' .. newest.sysupgradeFilename)
|
|
if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil)
|
|
result.imageSize = newest.sysupgradeFileSize
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
-- Turns a plain-text version into a table.
|
|
-- tables as argument are ignored so you can safely pass in an already parsed
|
|
-- version and expect it back unmodified.
|
|
function M.parseVersion(versionText)
|
|
if type(versionText) == 'table' then return versionText end
|
|
if not versionText or versionText:len() == 0 then return nil end
|
|
|
|
local major,minor,patch = versionText:match("^%s*(%d+)%.(%d+)%.(%d+)%s*$")
|
|
if not major or not minor or not patch then return nil end
|
|
|
|
return { ['major'] = major, ['minor'] = minor, ['patch'] = patch }
|
|
end
|
|
|
|
-- Formats a version as returned by parseVersion().
|
|
-- Strings are returned unmodified, so an 'already formatted' version can be
|
|
-- passed in safely and expected back unmodified.
|
|
function M.formatVersion(version)
|
|
if type(version) == 'string' then return version end
|
|
return version.major .. "." .. version.minor .. "." .. version.patch
|
|
end
|
|
|
|
-- expects two tables as created by M.parseVersion()
|
|
function M.compareVersions(versionA, versionB)
|
|
if type(versionA) ~= 'table' or type(versionB) ~= 'table' then return nil end
|
|
local diff = versionA.major - versionB.major
|
|
if diff == 0 then diff = versionA.minor - versionB.minor end
|
|
if diff == 0 then diff = versionA.patch - versionB.patch end
|
|
return diff > 0 and 1 or (diff < 0 and -1 or 0)
|
|
end
|
|
|
|
function M.findVersion(verTable, version)
|
|
for _,ent in pairs(verTable) do
|
|
if M.compareVersions(ent.version, version) == 0 then return ent end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- version may be a table or a string, devtype and isFactory are optional
|
|
function M.constructImageFilename(version, devType, isFactory)
|
|
local sf = isFactory and 'factory' or 'sysupgrade'
|
|
local v = M.formatVersion(version)
|
|
local dt = devType and devType or 'tl-mr3020'
|
|
return 'doodle3d-wifibox-' .. M.formatVersion(v) .. '-' .. dt .. '-' .. sf .. '.bin'
|
|
end
|
|
|
|
-- returns a plain text version
|
|
function M.getCurrentVersionText()
|
|
local res,msg,nr = readFile('/etc/wifibox-version', true)
|
|
if res then return res else return nil,msg,nr end
|
|
end
|
|
|
|
-- returns a table with major, minor and patch as keys
|
|
function M.getCurrentVersion()
|
|
local vt,msg = M.getCurrentVersionText()
|
|
return vt and M.parseVersion(vt) or nil,msg
|
|
end
|
|
|
|
-- requires url of image index file; returns an indexed (and sorted) table containing version tables
|
|
-- baseUrl and useCache are optional
|
|
function M.getAvailableVersions(baseUrl, useCache)
|
|
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
|
local indexFilename = M.CACHE_PATH .. '/' .. M.IMAGE_INDEX_FILE
|
|
|
|
local ccRv,ccMsg = createCacheDirectory()
|
|
if not ccRv then return nil,ccMsg end
|
|
|
|
if not useCache or not exists(indexFilename) then
|
|
local rv = downloadFile(baseUrl .. '/images/' .. M.IMAGE_INDEX_FILE, M.CACHE_PATH, M.IMAGE_INDEX_FILE)
|
|
if rv ~= 0 then return nil,"could not download image index file (" .. wgetStatusToString(rv) .. ")" end
|
|
end
|
|
|
|
local status,idxLines = pcall(io.lines, indexFilename)
|
|
|
|
if not status then return nil,"could not open image index file '" .. indexFilename .. "'" end --do not include io.lines error message
|
|
|
|
local result,entry = {}, nil
|
|
local lineno,changelogMode = 1, false
|
|
for line in idxLines do
|
|
local k,v = line:match('^(.-):(.*)$')
|
|
k,v = trim(k), trim(v)
|
|
if not log then D("#" .. lineno .. ": considering '" .. line .. "' (" .. (k or '<nil>') .. " / " .. (v or '<nil>') .. ")") end
|
|
if not changelogMode and (not k or not v) then return nil,"incorrectly formatted line in index file (line " .. lineno .. ")" end
|
|
|
|
if k == 'ChangelogEnd' then
|
|
changelogMode = false
|
|
elseif changelogMode then
|
|
entry.changelog = entry.changelog .. line .. '\n'
|
|
else
|
|
if k == 'Version' then
|
|
if entry ~= nil then table.insert(result, entry) end
|
|
|
|
local pv = M.parseVersion(v)
|
|
if not pv then return nil,"incorrect version text in index file (line " .. lineno .. ")" end
|
|
entry = { version = pv }
|
|
elseif k == 'ChangelogStart' then
|
|
changelogMode = true
|
|
entry.changelog = ""
|
|
elseif k == 'Files' then
|
|
local sName,fName = v:match('^(.-);(.*)$')
|
|
sName,fName = trim(sName), trim(fName)
|
|
if sName then entry.sysupgradeFilename = sName end
|
|
if fName then entry.factoryFilename = fName end
|
|
elseif k == 'FileSize' then
|
|
local sSize,fSize = v:match('^(.-);(.*)$')
|
|
sSize,fSize = trim(sSize), trim(fSize)
|
|
if sSize then entry.sysupgradeFileSize = tonumber(sSize) end
|
|
if fSize then entry.factoryFileSize = tonumber(fSize) end
|
|
elseif k == 'MD5' then
|
|
local sSum,fSum = v:match('^(.-);(.*)$')
|
|
sSum,fSum = trim(sSum), trim(fSum)
|
|
if sSum then entry.sysupgradeMD5 = sSum end
|
|
if fSum then entry.factoryMD5 = fSum end
|
|
else
|
|
P(-1, "ignoring unrecognized field in index file '" .. k .. "' (line " .. lineno .. ")")
|
|
end
|
|
end
|
|
lineno = lineno + 1
|
|
end
|
|
|
|
if entry ~= nil then table.insert(result, entry) end
|
|
|
|
table.sort(result, function(a,b)
|
|
return M.compareVersions(a.version,b.version) < 0
|
|
end)
|
|
|
|
return result
|
|
end
|
|
|
|
-- forceDownload, devtype and isFactory are optional
|
|
-- returns true or nil+msg or nil + return value from wget
|
|
function M.downloadImageFile(baseUrl, version, forceDownload, devType, isFactory)
|
|
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
|
local filename = M.constructImageFilename(version, devType, isFactory)
|
|
local doDownload = (type(forceDownload) == 'boolean') and forceDownload or (not exists(M.CACHE_PATH .. '/' .. filename))
|
|
|
|
local ccRv,ccMsg = createCacheDirectory()
|
|
if not ccRv then return nil,ccMsg end
|
|
|
|
--TODO: call M.checkValidImage, set doDownload to true if not valid
|
|
|
|
local rv = 0
|
|
if doDownload then
|
|
setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")")
|
|
rv = downloadFile(baseUrl .. '/images/' .. filename, M.CACHE_PATH, filename)
|
|
end
|
|
|
|
if rv == 0 then
|
|
--TODO: check if the downloaded file is complete and matches checksum
|
|
setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")")
|
|
return true
|
|
else
|
|
local ws = wgetStatusToString(rv)
|
|
setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (" .. ws .. ")")
|
|
return nil,ws
|
|
end
|
|
end
|
|
|
|
-- this function will not return
|
|
-- noRetain, devType and isFactory are optional
|
|
-- returns true or nil + wget return value
|
|
function M.flashImageVersion(version, noRetain, devType, isFactory)
|
|
local imgName = M.constructImageFilename(version, devType, isFactory)
|
|
local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade '
|
|
cmd = cmd .. M.CACHE_PATH .. '/' .. imgName
|
|
|
|
local ccRv,ccMsg = createCacheDirectory()
|
|
if not ccRv then return nil,ccMsg end
|
|
|
|
setState(M.STATE, "Installing new image (" .. imgName .. ")") -- yes this is rather pointless
|
|
local rv = runCommand(cmd, true) -- if everything goes to plan, this will not return
|
|
|
|
if rv == 0 then setState(M.STATE.INSTALLED, "Image installed")
|
|
else setState(M.STATE.INSTALL_FAILED, "Image installation failed (sysupgrade returned " .. rv .. ")")
|
|
end
|
|
|
|
return (rv == 0) and true or nil,rv
|
|
end
|
|
|
|
--returns true on success, or nil+msg otherwise
|
|
function M.clear()
|
|
local ccRv,ccMsg = createCacheDirectory()
|
|
if not ccRv then return nil,ccMsg end
|
|
|
|
D("Removing " .. M.CACHE_PATH .. "/doodle3d-wifibox-*.bin")
|
|
setState(M.STATE.NONE, "")
|
|
local rv = os.execute('rm -f ' .. M.CACHE_PATH .. '/doodle3d-wifibox-*.bin')
|
|
return (rv == 0) and true or nil,"could not remove image files"
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
----------
|
|
-- MAIN --
|
|
----------
|
|
|
|
local function main()
|
|
local useCache = true
|
|
local argTable,msg = parseCommandlineArguments(arg)
|
|
|
|
if not argTable then
|
|
E("error interpreting command-line arguments, try '-h' for help (".. msg ..")")
|
|
os.exit(1)
|
|
end
|
|
|
|
verbosity = argTable.verbosity
|
|
if argTable.useCache ~= nil then useCache = argTable.useCache end
|
|
|
|
P(0, "Doodle3D Wifibox firmware updater")
|
|
local cacheCreated,msg = createCacheDirectory()
|
|
if not cacheCreated then
|
|
E(msg)
|
|
os.exit(1)
|
|
end
|
|
|
|
if argTable.action == 'showHelp' then
|
|
P(1, "\t-h\t\tShow this help message")
|
|
P(1, "\t-q\t\tquiet mode")
|
|
P(1, "\t-V\t\tverbose mode")
|
|
P(1, "\t-c\t\tUse cache as much as possible")
|
|
P(1, "\t-C\t\tDo not use the cache")
|
|
P(1, "\t-u <base_url>\tUse specified base URL (default: " .. M.DEFAULT_BASE_URL .. ")")
|
|
P(1, "\t-v\t\tShow current image version")
|
|
P(1, "\t-s\t\tShow current update status")
|
|
P(1, "\t-l\t\tShow list of available image versions (and which one has been downloaded, if any)")
|
|
P(1, "\t-i <version>\tShow information (changelog) about the requested image version")
|
|
P(1, "\t-d <version>\tDownload requested image version")
|
|
P(1, "\t-f <version>\tFlash to requested image version (by means of sysupgrade)")
|
|
P(1, "\t-r\t\tClear downloaded images and reset state")
|
|
os.exit(10)
|
|
|
|
elseif argTable.action == 'showCurrentVersion' then
|
|
local vText,msg,nr = M.getCurrentVersionText()
|
|
if not vText then E("error reading firmware version (" .. nr .. ": " .. msg .. ")"); os.exit(1) end
|
|
local v = M.parseVersion(vText)
|
|
if not v then E("error parsing version '" .. vText .. "'"); os.exit(2) end
|
|
P(1, "version: " .. M.formatVersion(v))
|
|
|
|
elseif argTable.action == 'showStatus' then
|
|
local status = M.getStatus(argTable.baseUrl, useCache)
|
|
P(0, "Current update status:")
|
|
P(1, " currentVersion:\t" .. (M.formatVersion(status.currentVersion) or '?'))
|
|
P(1, " newestVersion:\t" .. (M.formatVersion(status.newestVersion) or '?'))
|
|
|
|
if status.stateText and status.stateText:len() > 0 then
|
|
P(1, " state:\t\t" .. M.STATE_NAMES[status.stateCode] .. " (" .. status.stateText .. ")")
|
|
else
|
|
P(1, " state:\t\t" .. M.STATE_NAMES[status.stateCode])
|
|
end
|
|
|
|
if status.stateCode == M.STATE.DOWNLOADING then
|
|
local percent = (status.imageSize > 0) and (math.ceil(status.progress / status.imageSize * 1000) / 10) or 0
|
|
P(1, " download progress:\t" .. status.progress .. "/" .. status.imageSize .. " (" .. percent .. "%)")
|
|
end
|
|
|
|
elseif argTable.action == 'showAvailableVersions' then
|
|
local verTable,msg = M.getAvailableVersions(argTable.baseUrl, useCache)
|
|
if not verTable then
|
|
E("error collecting version information (" .. msg .. ")")
|
|
os.exit(2)
|
|
end
|
|
|
|
P(0, "Available versions:")
|
|
for _,ent in ipairs(verTable) do P(1, M.formatVersion(ent.version)) end
|
|
|
|
elseif argTable.action == 'showVersionInfo' then
|
|
local verTable,msg = M.getAvailableVersions(argTable.baseUrl, useCache)
|
|
if not verTable then
|
|
E("error parsing image index file (" .. msg .. ")")
|
|
os.exit(2)
|
|
end
|
|
|
|
local vEnt,msg = M.findVersion(verTable, argTable.version)
|
|
|
|
if vEnt then
|
|
P(0, "Information on version:")
|
|
P(1, " version:\t\t" .. M.formatVersion(vEnt.version))
|
|
P(1, " sysupgradeFilename:\t" .. (vEnt.sysupgradeFilename or '-'))
|
|
P(1, " sysupgradeFileSize:\t" .. (vEnt.sysupgradeFileSize or '-'))
|
|
P(1, " sysupgradeMD5:\t" .. (vEnt.sysupgradeMD5 or '-'))
|
|
P(1, " factoryFilename:\t" .. (vEnt.factoryFilename or '-'))
|
|
P(1, " factoryFileSize:\t" .. (vEnt.factoryFileSize or '-'))
|
|
P(1, " factoryMD5:\t\t" .. (vEnt.factoryMD5 or '-'))
|
|
if vEnt.changelog then
|
|
P(1, "\n--- Changelog ---\n" .. vEnt.changelog .. '---')
|
|
else
|
|
P(1, " changelog:\t\t-")
|
|
end
|
|
else
|
|
P(1, "not found")
|
|
end
|
|
|
|
elseif argTable.action == 'imageDownload' then
|
|
--TODO: first check if version exists
|
|
local rv,msg = M.downloadImageFile(argTable.baseUrl, argTable.version, not useCache) --TEMP
|
|
if not rv then E("could not download file (" .. msg .. ")")
|
|
else P(1, "success")
|
|
end
|
|
elseif argTable.action == 'clear' then
|
|
local rv,msg = M.clear()
|
|
if not rv then P(1, "error (" .. msg .. ")")
|
|
else P(1, "success")
|
|
end
|
|
|
|
elseif argTable.action == 'imageInstall' then
|
|
local rv = M.flashImageVersion(argTable.version)
|
|
E("error: flash function returned, the device should have been flashed and rebooted instead")
|
|
os.exit(3)
|
|
|
|
else
|
|
P(0, "usage: d3d-updater [-hqVcCvslr] [-u base_url] [-i version] [-d version] [-f version]")
|
|
end
|
|
|
|
os.exit(0)
|
|
end
|
|
|
|
-- only execute the main function if an arg table is present, this enables usage both as module and as standalone script
|
|
if arg ~= nil then main() end
|
|
|
|
return M
|