-- acf model for packages (apk) module (..., package.seeall) require("apk") require("modelfunctions") require("posix") require("fs") require("format") local configfile = "/etc/apk/repositories" local worldfile = "/etc/apk/world" local cachelink = "/etc/apk/cache" local lbuconffile = "/etc/lbu/lbu.conf" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " local repo = nil local install_cache = false local upgrade_cache = false local toplevel -- ################################################################################ -- LOCAL FUNCTIONS local function gettoplevel() if not toplevel then toplevel = {} local top = format.string_to_table(fs.read_file(worldfile) or "", "%s+") for i,name in ipairs(top) do toplevel[name] = i end end return toplevel end local run_apk_cmd = function(cmd) local c = path.."apk "..cmd.." 2>&1" local f = io.popen(c) local cmdresult = f:read("*a") f:close() return cmdresult end local reload_upgrades = function() if repo then -- clear out upgrade info for name,value in pairs(repo) do if value then value.upgrade = nil end end -- read in which need upgrades local f = io.popen(path.."apk version -l '<' 2>/dev/null") for line in f:lines() do local name = string.match(line, "(%S+)%-%d") if repo[name] then repo[name].upgrade = true end end f:close() upgrade_cache = true end return repo end local reload_installed = function() if repo then -- clear out installed info for name,value in pairs(repo) do if value then value.installed = nil value.comment = nil end end -- read in which are installed local f = io.popen(path.."apk info -vv 2>/dev/null") for line in f:lines() do local name, ver, comment = string.match(line, "(%S+)%-(%d+%S*)%s+%-%s+(.*)") if not repo[name] then repo[name] = {} end repo[name].installed = ver repo[name].comment = comment end f:close() install_cache = true end return repo end local repository = function() if not repo then -- read in all of the packages local f = io.popen(path.."apk search 2>/dev/null") repo = {} install_cache = false upgrade_cache = false for line in f:lines() do local name, ver = string.match(line, "(.*)%-(%d+.*)") if name and (not repo[name] or repo[name].version < ver) then repo[name] = {} repo[name].version = ver end end f:close() end if not install_cache then reload_installed() end if not upgrade_cache then reload_upgrades() end return repo end -- Find all the packages that this package depends on (using recursion) function find_dependents(package) repo = repo or repository() if not repo[package] then return {} end if not repo[package].dependents then repo[package].dependents = {} local cmd = path .. "apk info -R "..package local f = io.popen(cmd) for line in f:lines() do if not line:find("depends on:") and not line:find("^%s*$") then table.insert(repo[package].dependents, line) for i,dep in ipairs(find_dependents(line, saved, output)) do table.insert(repo[package].dependents, dep) end end end end return repo[package].dependents end local function upgrade_available(package) local retval = false repo = repo or repository() if repo[package] and repo[package].upgrade then retval = true else -- check the dependents for i,dep in ipairs(find_dependents(package)) do if repo[dep] and repo[dep].upgrade then retval = true break end end end return retval end -- ################################################################################ -- PUBLIC FUNCTIONS get_loaded_packages = function() repo = repository() toplevel = gettoplevel() -- read in the loaded packages local top = cfe({ type="list", value={}, label="Top Level Packages"}) local depend = cfe({ type="list", value={}, label="Dependent Packages"}) for name,value in pairs(repo) do if value.installed then local temp = {} temp.name = name temp.version = value.installed temp.description = value.comment temp.upgrade = value.upgrade if toplevel[name] then top.value[#top.value+1] = temp else depend.value[#depend.value+1] = temp end end end table.sort(top.value, function(a,b) return (a.name < b.name) end) table.sort(depend.value, function(a,b) return (a.name < b.name) end) return cfe({ type="group", value={toplevel=top, dependent=depend}, label="Installed Packages" }) end get_available_packages = function() repo = repository() -- available are all except same version installed local available = cfe({ type="list", value={}, label="Available Packages" }) for name,value in pairs(repo) do if value.version and (not value.installed or value.upgrade) then local temp = {} temp.name = name temp.version = value.version temp.upgrade = value.upgrade available.value[#available.value + 1] = temp end end table.sort(available.value, function(a,b) return (a.name < b.name) end) return available end delete_package = function(package, sessiondata) local success, cmdresult = apk.delete(package) if success then -- Destroy menu and permissions info in session so recalculated if sessiondata then sessiondata.menu = nil end if sessiondata then sessiondata.permissions = nil end end return cfe({ value=cmdresult, label="Result of Delete" }) end install_package = function(package,sessiondata) local success, cmdresult = apk.install(package) if success then -- Destroy menu and permissions info in session so recalculated if sessiondata then sessiondata.menu = nil end if sessiondata then sessiondata.permissions = nil end end return cfe({ value=cmdresult, label="Result of Install" }) end upgrade_package = function(package) return cfe({ value=run_apk_cmd("fix -u "..package), label="Result of Package Upgrade" }) end update_all = function() return cfe({ value=run_apk_cmd("update"), label="Result of Update" }) end upgrade_all = function() result = {} result[#result+1] = run_apk_cmd("update") result[#result+1] = run_apk_cmd("add -u apk-tools") result[#result+1] = run_apk_cmd("upgrade") return cfe({ value=table.concat(result, ""), label="Result of Upgrade" }) -- return cfe({ value=run_apk_cmd("upgrade -U"), label="Result of Upgrade" }) end get_cache = function() local cache = {} cache.enable = cfe({ type="boolean", value=false, label="Enable Cache" }) cache.directory = cfe({ label="Cache Directory" }) local link = posix.stat(cachelink, "type") if link == "link" then cache.enable.value = true cache.directory.value = posix.readlink(cachelink) else if link then cache.enable.errtxt = cachelink.." exists but is not a link" end local lbu_media = format.parse_ini_file(fs.read_file(lbuconffile), "", "LBU_MEDIA") if lbu_media then cache.directory.value = "/media/"..lbu_media.."/cache" end end return cfe({ type="group", value=cache, label="Cache Settings" }) end update_cache = function(cache) cache.value.enable.errtxt = nil if not cache.value.enable.value then os.remove(cachelink) else local success = false cache.errtxt = "Failed to set cache" if cache.value.directory.value == "" then cache.value.directory.errtxt = "Directory must be defined" else local dir = posix.stat(cache.value.directory.value, "type") if dir and dir ~= "directory" then cache.value.directory.errtxt = "Path exists but is not a directory" elseif not dir then success = fs.create_directory(cache.value.directory.value) if not success then cache.value.directory.errtxt = "Failed to create directory" end else success = true end end if success then os.remove(cachelink) posix.link(cache.value.directory.value, cachelink, true) cache.errtxt = nil end end return cache end get_configfile = function() return modelfunctions.getfiledetails(configfile) end update_configfile = function(newconfig) return modelfunctions.setfiledetails(newconfig, {configfile}) end get_package_details = function(package) repo = repo or repository() local details = {} details.package = cfe({ value=package, label="Package" }) details.version = cfe({ label="Available Version" }) details.installed = cfe({ label="Installed Version" }) details.comment = cfe({ label="Description" }) details.webpage = cfe({ label="Web Page" }) details.size = cfe({ label="Size" }) details.upgrade = cfe({ label="Upgrade Details" }) if not repo[package] then details.package.errtxt = "Invalid package" else details.version.value = repo[package].version if repo[package].installed then details.installed.value = repo[package].installed details.comment.value = repo[package].comment local cmdresult = format.string_to_table(run_apk_cmd("info -ws "..package), "\n") details.webpage.value = cmdresult[2] or "" details.size.value = cmdresult[5] or "" local dependents = find_dependents(package) table.insert(dependents, 1, package) local revdeps = {} details.upgrade.value = {} for i,val in ipairs(dependents) do if not revdeps[val] then revdeps[val] = true if repo[val].upgrade then table.insert(details.upgrade.value, val.." "..repo[val].installed.." -> "..repo[val].version) end end end details.upgrade.value = table.concat(details.upgrade.value, "\n") end end return cfe({ type="group", value=details, label="Package Details" }) end