0
0
mirror of https://github.com/Doodle3D/doodle3d-firmware.git synced 2025-04-19 09:36:30 +02:00

Make cache path configurable.

Fix handling return values of os.execute (Lua 5.1 is quite different from 5.2).
This commit is contained in:
Wouter R 2014-02-19 11:50:25 +01:00
parent 689696c0bd
commit 81f450aaf6
2 changed files with 80 additions and 40 deletions

View File

@ -10,12 +10,12 @@
--- This script provides an interface to upgrade or downgrade the Doodle3D wifibox. --- This script provides an interface to upgrade or downgrade the Doodle3D wifibox.
-- It can both be used as a standalone command-line tool and as a Lua library. -- It can both be used as a standalone command-line tool and as a Lua library.
-- TODO/NOTES: -- TODO/NOTES: (from old script)
-- add to status: validImage: none|<version> (can use checkValidImage for this) -- add to status: validImage: none|<version> (can use checkValidImage for this)
-- any more TODO's across this file? -- any more TODO's across this file?
-- max 1 image tegelijk (moet api doen), en rekening houden met printbuffer (printen blokkeren?) -- max 1 image tegelijk (moet api doen), en rekening houden met printbuffer (printen blokkeren?)
-- MAYBE/LATER: -- MAYBE/LATER: (from old script)
-- add API calls to retrieve a list of all versions with their info (i.e., the result of getAvailableVersions) -- 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: add provision (in verbose mode?) to use '-v' instead of '-q' and disable output redirection
-- document index file format (Version first, then in any order: Files: sysup; factory, FileSize: sysup; factory, MD5: sysup; factory, ChangelogStart:, ..., ChangelogEnd:) -- document index file format (Version first, then in any order: Files: sysup; factory, FileSize: sysup; factory, MD5: sysup; factory, ChangelogStart:, ..., ChangelogEnd:)
@ -57,17 +57,18 @@ M.IMAGE_STABLE_INDEX_FILE = 'wifibox-image.index'
M.IMAGE_BETA_INDEX_FILE = 'wifibox-image.beta.index' M.IMAGE_BETA_INDEX_FILE = 'wifibox-image.beta.index'
--- Path to the updater cache. --- Path to the updater cache.
M.CACHE_PATH = '/tmp/d3d-updater' M.DEFAULT_CACHE_PATH = '/tmp/d3d-updater'
--- Name of the file to store current state in, this file resides in @{CACHE_PATH}. --- Name of the file to store current state in, this file resides in @{cachePath}.
M.STATE_FILE = 'update-state' M.STATE_FILE = 'update-state'
M.WGET_OPTIONS = "-q -t 1 -T 30" M.WGET_OPTIONS = "-q -t 1 -T 30"
--M.WGET_OPTIONS = "-v -t 1 -T 30" --M.WGET_OPTIONS = "-v -t 1 -T 30"
local verbosity = 0 -- set by parseCommandlineArguments() local verbosity = 0 -- set by parseCommandlineArguments() or @{setVerbosity}
local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger
local useCache = true -- default, can be overwritten using M.setUseCache() local useCache = true -- default, can be overwritten using @{setUseCache}
local cachePath = M.DEFAULT_CACHE_PATH -- default, can be change using @{setCachePath}
local baseUrl = M.DEFAULT_BASE_URL -- default, can be overwritten by M.setBaseUrl() local baseUrl = M.DEFAULT_BASE_URL -- default, can be overwritten by M.setBaseUrl()
@ -104,7 +105,24 @@ local function E(msg)
end end
end end
--- Splits the return status from `os.execute`, which consists of two bytes. compatlua51 = _VERSION == 'Lua 5.1'
--- execute a shell command. Taken from penlight library.
-- This is a compatibility function that returns the same for Lua 5.1 and Lua 5.2
-- @param cmd a shell command
-- @return true if successful
-- @return actual return code
function compatexecute (cmd)
local res1,res2,res3 = os.execute(cmd)
if compatlua51 then
local cmd,sys = splitExitStatus(res1)
return (res1 == 0) and true or nil, sys
else
return res1, res3
end
end
--- Splits the return status from `os.execute` (only Lua <= 5.1), which consists of two bytes.
-- --
-- `os.execute` internally calls [system](http://linux.die.net/man/3/system), -- `os.execute` internally calls [system](http://linux.die.net/man/3/system),
-- which usually returns the command exit status as high byte (see [WEXITSTATUS](http://linux.die.net/man/2/wait)). -- which usually returns the command exit status as high byte (see [WEXITSTATUS](http://linux.die.net/man/2/wait)).
@ -123,11 +141,12 @@ end
-- @number exitStatus An exit status from wget. -- @number exitStatus An exit status from wget.
-- @treturn string|number Either the status followed by a description, or a message indicating the call was interrupted, or just the status if it was not recognized. -- @treturn string|number Either the status followed by a description, or a message indicating the call was interrupted, or just the status if it was not recognized.
local function wgetStatusToString(exitStatus) local function wgetStatusToString(exitStatus)
local wgetStatus,systemStatus = splitExitStatus(exitStatus) -- local wgetStatus,systemStatus = splitExitStatus(exitStatus)
local wgetStatus = exitStatus
if systemStatus ~= 0 then -- if systemStatus ~= 0 then
return "interrupted:" .. systemStatus -- return "interrupted: " .. systemStatus
end -- end
-- adapted from man(1) wget on OSX -- adapted from man(1) wget on OSX
local statusTexts = { local statusTexts = {
@ -152,8 +171,9 @@ end
-- @return bool|nil True, or nil on error. -- @return bool|nil True, or nil on error.
-- @return ?string A message in case of error. -- @return ?string A message in case of error.
local function createCacheDirectory() local function createCacheDirectory()
if os.execute('mkdir -p ' .. M.CACHE_PATH) ~= 0 then local _,rv = compatexecute('mkdir -p ' .. cachePath)
return nil,"Error: could not create cache directory '" .. M.CACHE_PATH .. "'" if rv ~= 0 then
return nil,"Error: could not create cache directory '" .. cachePath .. "'"
end end
return true return true
end end
@ -162,7 +182,7 @@ end
-- @treturn STATE The current state code (@{STATE}.NONE if no state has been set). -- @treturn STATE The current state code (@{STATE}.NONE if no state has been set).
-- @treturn string The current state message (empty string if no state has been set). -- @treturn string The current state message (empty string if no state has been set).
local function getState() local function getState()
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'r') local file,msg = io.open(cachePath .. '/' .. M.STATE_FILE, 'r')
if not file then return M.STATE.NONE,"" end if not file then return M.STATE.NONE,"" end
local state = file:read('*a') local state = file:read('*a')
@ -182,7 +202,7 @@ end
--- Read the contents of a file. --- Read the contents of a file.
-- --
-- TODO: this file has been copied from @{util.utils}.lua and should be merged again. -- TODO: this file has been copied from @{util.utils}.lua and should be merged back.
-- @string filePath The file to read. -- @string filePath The file to read.
-- @bool trimResult Whether or not to trim the read data. -- @bool trimResult Whether or not to trim the read data.
-- @treturn bool|nil True, or nil on error. -- @treturn bool|nil True, or nil on error.
@ -204,7 +224,7 @@ end
--- Reports whether or not a file exists. --- Reports whether or not a file exists.
-- --
-- TODO: this file has been copied from @{util.utils}.lua and should be merged again. -- TODO: this file has been copied from @{util.utils}.lua and should be merged back.
-- @string file The file to report about. -- @string file The file to report about.
-- @treturn bool True if the file exists, false otherwise. -- @treturn bool True if the file exists, false otherwise.
local function exists(file) local function exists(file)
@ -219,7 +239,7 @@ end
--- Reports the size of a file or file handle. --- Reports the size of a file or file handle.
-- --
-- TODO: this file has been copied from @{util.utils}.lua and should be merged again. -- TODO: this file has been copied from @{util.utils}.lua and should be merged back.
-- @param file A file path or open file handle. -- @param file A file path or open file handle.
-- @treturn number Size of the file. -- @treturn number Size of the file.
local function fileSize(file) local function fileSize(file)
@ -245,7 +265,8 @@ end
-- @treturn number Exit status of of command or -1 if dryRun is true. -- @treturn number Exit status of of command or -1 if dryRun is true.
local function runCommand(command, dryRun) local function runCommand(command, dryRun)
D("about to run: '" .. command .. "'") D("about to run: '" .. command .. "'")
return (not dryRun) and os.execute(command) or -1 if dryRun then return -1 end
return compatexecute(command)
end end
--- Removes a file. --- Removes a file.
@ -331,6 +352,14 @@ end
-- MODULE FUNCTIONS -- -- MODULE FUNCTIONS --
---------------------- ----------------------
--- Set verbosity (log level) that determines which messages do get logged and which do not.
-- @tparam number level The level to set, between -1 and 1.
function M.setVerbosity(level)
if level and level >= -1 and level <= 1 then
verbosity = level
end
end
--- Enables use of the given @{util.logger} object, otherwise `stdout`/`stderr` will be used. --- Enables use of the given @{util.logger} object, otherwise `stdout`/`stderr` will be used.
-- @tparam util.logger logger The logger to log future messages to. -- @tparam util.logger logger The logger to log future messages to.
function M.setLogger(logger) function M.setLogger(logger)
@ -351,6 +380,12 @@ function M.setBaseUrl(url)
baseUrl = url baseUrl = url
end end
--- Sets the filesystem path to use as cache for downloaded index and image files.
-- @string path The path to use, use nil to restore default @{DEFAULT_CACHE_PATH}.
function M.setCachePath(path)
cachePath = path or M.DEFAULT_CACHE_PATH
end
--- Returns a table with information about current update status of the wifibox. --- Returns a table with information about current update status of the wifibox.
-- --
-- The result table will contain at least the current version, current state code and text. -- The result table will contain at least the current version, current state code and text.
@ -380,7 +415,7 @@ function M.getStatus()
result.newestVersion = newest and newest.version or unknownVersion result.newestVersion = newest and newest.version or unknownVersion
if result.stateCode == M.STATE.DOWNLOADING then if result.stateCode == M.STATE.DOWNLOADING then
result.progress = fileSize(M.CACHE_PATH .. '/' .. newest.sysupgradeFilename) result.progress = fileSize(cachePath .. '/' .. newest.sysupgradeFilename)
if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil) if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil)
result.imageSize = newest.sysupgradeFileSize result.imageSize = newest.sysupgradeFileSize
end end
@ -503,7 +538,7 @@ function M.constructImageFilename(version, devType, isFactory)
return 'doodle3d-wifibox-' .. M.formatVersion(v) .. '-' .. dt .. '-' .. sf .. '.bin' return 'doodle3d-wifibox-' .. M.formatVersion(v) .. '-' .. dt .. '-' .. sf .. '.bin'
end end
--- Checks whether a valid image file is present in @{CACHE_PATH} for the given image properties. --- Checks whether a valid image file is present in @{cachePath} for the given image properties.
-- The versionEntry table will be augmented with an `isValid` key. -- The versionEntry table will be augmented with an `isValid` key.
-- --
-- NOTE: currently, this function only checks the image exists and has the correct size. -- NOTE: currently, this function only checks the image exists and has the correct size.
@ -515,8 +550,8 @@ end
-- @treturn bool True if a valid image is present, false otherwise. -- @treturn bool True if a valid image is present, false otherwise.
function M.checkValidImage(versionEntry, devType, isFactory) function M.checkValidImage(versionEntry, devType, isFactory)
local filename = M.constructImageFilename(versionEntry.version, devType, isFactory) local filename = M.constructImageFilename(versionEntry.version, devType, isFactory)
--return versionEntry.md5 == md5sum(M.CACHE_PATH .. '/' .. filename) --return versionEntry.md5 == md5sum(cachePath .. '/' .. filename)
local size = fileSize(M.CACHE_PATH .. '/' .. filename) local size = fileSize(cachePath .. '/' .. filename)
versionEntry.isValid = versionEntry.sysupgradeFileSize == size versionEntry.isValid = versionEntry.sysupgradeFileSize == size
return versionEntry.isValid return versionEntry.isValid
end end
@ -536,18 +571,14 @@ function M.getCurrentVersion()
end end
--- Returns an indexed and sorted table containing version information tables. --- Returns an indexed and sorted table containing version information tables.
-- The information is obtained from the either cached or downloaded image index (@{IMAGE_INDEX_FILE}). -- The information is obtained from the either cached or downloaded image index file.
-- @treturn table A table with a collection of version information tables. local function fetchIndexTable(indexFile, cachePath)
function M.getAvailableVersions()
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
local indexFilename = M.CACHE_PATH .. '/' .. M.IMAGE_INDEX_FILE local indexFilename = cachePath .. '/' .. indexFile
local ccRv,ccMsg = createCacheDirectory()
if not ccRv then return nil,ccMsg end
if not useCache or not exists(indexFilename) then if not useCache or not exists(indexFilename) then
local rv = downloadFile(baseUrl .. '/images/' .. M.IMAGE_INDEX_FILE, M.CACHE_PATH, M.IMAGE_INDEX_FILE) local rv1,rv2 = downloadFile(baseUrl .. '/images/' .. indexFile, cachePath, indexFile)
if rv ~= 0 then return nil,"could not download image index file (" .. wgetStatusToString(rv) .. ")" end if not rv1 then return nil,"could not download image index file (" .. wgetStatusToString(rv2) .. ")" end
end end
local status,idxLines = pcall(io.lines, indexFilename) local status,idxLines = pcall(io.lines, indexFilename)
@ -631,7 +662,7 @@ function M.downloadImageFile(versionEntry, devType, isFactory)
local rv = 0 local rv = 0
if doDownload then if doDownload then
M.setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")") M.setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")")
rv = downloadFile(baseUrl .. '/images/' .. filename, M.CACHE_PATH, filename) rv = downloadFile(baseUrl .. '/images/' .. filename, cachePath, filename)
end end
if rv == 0 then if rv == 0 then
@ -639,14 +670,14 @@ function M.downloadImageFile(versionEntry, devType, isFactory)
M.setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")") M.setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")")
return true return true
else else
removeFile(M.CACHE_PATH .. '/' .. filename) removeFile(cachePath .. '/' .. filename)
local ws = "Image download failed (invalid image)" local ws = "Image download failed (invalid image)"
M.setState(M.STATE.DOWNLOAD_FAILED, ws) M.setState(M.STATE.DOWNLOAD_FAILED, ws)
return nil,ws return nil,ws
end end
else else
local ws = wgetStatusToString(rv) local ws = wgetStatusToString(rv)
removeFile(M.CACHE_PATH .. '/' .. filename) removeFile(cachePath .. '/' .. filename)
M.setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (wget error: " .. ws .. ")") M.setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (wget error: " .. ws .. ")")
return nil,ws return nil,ws
end end
@ -665,7 +696,7 @@ function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
log:info("flashImageVersion") log:info("flashImageVersion")
local imgName = M.constructImageFilename(versionEntry.version, devType, isFactory) local imgName = M.constructImageFilename(versionEntry.version, devType, isFactory)
local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade ' local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade '
cmd = cmd .. M.CACHE_PATH .. '/' .. imgName cmd = cmd .. cachePath .. '/' .. imgName
local ccRv,ccMsg = createCacheDirectory() local ccRv,ccMsg = createCacheDirectory()
if not ccRv then return nil,ccMsg end if not ccRv then return nil,ccMsg end
@ -688,22 +719,22 @@ function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
return (rv == 0) and true or nil,rv return (rv == 0) and true or nil,rv
end end
--- Clears '*.bin' in the @{CACHE_PATH} directory. --- Clears '*.bin' in the @{cachePath} directory.
-- @treturn bool|nil True on success, or nil on error. -- @treturn bool|nil True on success, or nil on error.
-- @treturn ?string Descriptive message on error. -- @treturn ?string Descriptive message on error.
function M.clear() function M.clear()
local ccRv,ccMsg = createCacheDirectory() local ccRv,ccMsg = createCacheDirectory()
if not ccRv then return nil,ccMsg end if not ccRv then return nil,ccMsg end
D("Removing " .. M.CACHE_PATH .. "/doodle3d-wifibox-*.bin") D("Removing " .. cachePath .. "/doodle3d-wifibox-*.bin")
M.setState(M.STATE.NONE, "") M.setState(M.STATE.NONE, "")
local rv = os.execute('rm -f ' .. M.CACHE_PATH .. '/doodle3d-wifibox-*.bin') local rv = compatexecute('rm -f ' .. cachePath .. '/doodle3d-wifibox-*.bin')
return (rv == 0) and true or nil,"could not remove image files" return (rv == 0) and true or nil,"could not remove image files"
end end
--- Set updater state. --- Set updater state.
-- --
-- NOTE: make sure the cache directory @{CACHE_PATH} exists before calling this function or it will fail. -- NOTE: make sure the cache directory @{cachePath} exists before calling this function or it will fail.
-- --
-- NOTE: this function _can_ fail but this is not expected to happen so the return value is mostly ignored for now. -- NOTE: this function _can_ fail but this is not expected to happen so the return value is mostly ignored for now.
-- --
@ -713,7 +744,7 @@ end
function M.setState(code, msg) function M.setState(code, msg)
local s = code .. '|' .. msg local s = code .. '|' .. msg
D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')") D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')")
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'w') local file,msg = io.open(cachePath .. '/' .. M.STATE_FILE, 'w')
if not file then if not file then
E("error: could not open state file for writing (" .. msg .. ")") E("error: could not open state file for writing (" .. msg .. ")")

View File

@ -12,6 +12,12 @@ pl = pl()
local lfs = require('lfs') -- assume this exists since it's required by penlight as well local lfs = require('lfs') -- assume this exists since it's required by penlight as well
local argStash = arg
arg = nil
local upmgr = require('d3d-update-mgr') -- arg must be nil for the update manager to load as module
arg = argStash
local D3D_REPO_FIRMWARE_NAME = 'doodle3d-firmware' local D3D_REPO_FIRMWARE_NAME = 'doodle3d-firmware'
local D3D_REPO_CLIENT_NAME = 'doodle3d-client' local D3D_REPO_CLIENT_NAME = 'doodle3d-client'
local D3D_REPO_PRINT3D_NAME = 'print3d' local D3D_REPO_PRINT3D_NAME = 'print3d'
@ -121,6 +127,9 @@ local function main()
-- ... -- -- ... --
upmgr.setUseCache(false)
upmgr.setVerbosity(1)
upmgr.setCachePath(paths.cache)
--fetch index files and if requested also images and packages --fetch index files and if requested also images and packages