Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
6.1KB

  1. local serial = require "serialization"
  2. local dl = require "download"
  3. local mtar = require "libmtar"
  4. local pkg = {}
  5. pkg.cfgPath = "/boot/cfg/pkg"
  6. pkg.sourcePath = pkg.cfgPath .. "/sources.cfg"
  7. pkg.installedPath = pkg.cfgPath .. "/installed.cfg"
  8. local w, lz16 = pcall(require,"liblz16")
  9. if not w then lz16 = nil end
  10. fs.makeDirectory("/boot/pkg")
  11. fs.makeDirectory("/boot/cfg/pkg")
  12. require "pkgfs"
  13. local kver,kvar = _OSVERSION:match("(%x+)%-([^-]-)$")
  14. kver,kvar = kver or "unknown", kvar or "base"
  15. local function getSources()
  16. local f = io.open(pkg.sourcePath,"rb")
  17. if not f then return {main={path="https://oc.shadowkat.net/psychos/pkg",cache=true,name="main"}} end
  18. local c = f:read("*a")
  19. f:close()
  20. return serial.unserialize(c)
  21. end
  22. local function saveSources(t)
  23. fs.makeDirectory(pkg.cfgPath)
  24. local f = io.open(pkg.sourcePath,"wb")
  25. f:write(serial.serialize(t))
  26. f:close()
  27. end
  28. local function getInstalled()
  29. local f = io.open(pkg.installedPath,"rb")
  30. if not f then return {psychos={version=kver},["kernel-"..kvar]={version=kver}} end
  31. local c = f:read("*a")
  32. f:close()
  33. return serial.unserialize(c)
  34. end
  35. local function saveInstalled(t)
  36. fs.makeDirectory(pkg.cfgPath)
  37. local f = io.open(pkg.installedPath,"wb")
  38. if not f then return false end
  39. f:write(serial.serialize(t))
  40. f:close()
  41. end
  42. local function getRepoMeta(repo)
  43. if not getSources()[repo].cache or not fs.exists("/boot/cfg/pkg/repo-"..repo..".cfg") then
  44. dl(getSources()[repo].path.."/packages.cfg","/boot/cfg/pkg/repo-"..repo..".cfg")
  45. end
  46. local f = io.open("/boot/cfg/pkg/repo-"..repo..".cfg","rb")
  47. local rt = serial.unserialize(f:read("*a"))
  48. f:close()
  49. if not getSources()[repo].cache then
  50. fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg")
  51. end
  52. return rt
  53. end
  54. local function activatePackage(path,compressed)
  55. require("pkgfs").add(path,compressed)
  56. end
  57. local function deactivatePackage(path)
  58. require("pkgfs").remove(path)
  59. end
  60. local function fnormalize(s)
  61. return table.concat(fs.segments(s),"/")
  62. end
  63. local function installSystemPackage(path,comp)
  64. local f
  65. if comp and lz16 then
  66. f = lz16.open(path,"rb")
  67. else
  68. f = io.open(path,"rb")
  69. end
  70. for fname, read, size in mtar.iter(f) do
  71. local opath = "/boot/"..fnormalize(fname)
  72. print(opath..": "..tostring(size))
  73. fs.makeDirectory(opath:match("(.+)/[^/]+"))
  74. local of = io.open(opath,"wb")
  75. if not of then error("unable to open "..opath.." for writing") end
  76. local tmp
  77. repeat
  78. tmp = read(2048) or ""
  79. of:write(tmp)
  80. until not tmp or tmp:len() < 1
  81. of:close()
  82. end
  83. return true
  84. end
  85. function pkg.addRepo(name,path,cache) -- string string boolean -- boolean -- Adds a repository, referred to as *name*, to the list of package sources, with the remote path *path*. If *cache* is set, keep a local copy of the repository index.
  86. local sources = getSources()
  87. sources[name] = {path=path,cache=cache,name=name}
  88. saveSources(sources)
  89. end
  90. function pkg.delRepo(name) -- string -- boolean -- Removes a repository from the list of repositories.
  91. local sources = getSources()
  92. sources[name] = nil
  93. saveSources(sources)
  94. end
  95. function pkg.update() -- Re-download cached repository indexes.
  96. for repo,meta in pairs(getSources()) do
  97. fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg")
  98. if meta.cache then
  99. getRepoMeta(repo)
  100. end
  101. end
  102. end
  103. function pkg.list(filter,installed) -- string boolean -- -- Print a list of available packages matching *filter*, optionally filtering to only installed packages if *installed* is set.
  104. filter = filter or ""
  105. local pkglist = {}
  106. for repo,_ in pairs(getSources()) do
  107. for pkg,meta in pairs(getRepoMeta(repo)) do
  108. if pkg:find(filter) or (pkg.meta or ""):find(filter) then
  109. meta.repo = repo
  110. pkglist[pkg] = meta
  111. end
  112. end
  113. end
  114. for k,v in pairs(pkglist) do
  115. if v.system then io.write("\27[31m") end
  116. print(string.format("%s/%s: %s\27[0m\n %s\n Authors: %s",v.repo,k,v.name,v.description,v.authors))
  117. end
  118. end
  119. function pkg.getMeta(pkgname) -- string -- table -- Returns the metadata for a the package specified in *pkgname*.
  120. print("Finding package "..pkgname)
  121. for repo,info in pairs(getSources()) do
  122. local pkg = getRepoMeta(repo)[pkgname]
  123. if pkg then
  124. print("Package "..pkgname.." located in repo "..repo.." at "..info.path)
  125. pkg.repository = info
  126. return pkg
  127. end
  128. end
  129. end
  130. function pkg.get(pkgname,auto) -- string boolean -- boolean -- Downloads and mounts a package, identified as *pkgname,* onto the pkgfs. Setting *auto* will flag the package as automatically installed; this is used for dependencies.
  131. local pkginfo = pkg.getMeta(pkgname)
  132. if not pkginfo then error("unable to locate package "..pkgname) end
  133. pkginfo.manual = not auto
  134. fs.makeDirectory("/boot/pkg")
  135. for k,v in ipairs(pkginfo.dependencies or {}) do
  136. if not getInstalled()[v] then
  137. pkg.get(v,true)
  138. end
  139. end
  140. dl(pkginfo.repository.path.."/"..pkginfo.filename,"/boot/pkg/"..pkginfo.filename)
  141. local installed = getInstalled()
  142. installed[pkgname] = pkginfo
  143. saveInstalled(installed)
  144. if pkginfo.system then
  145. local rv = installSystemPackage("/boot/pkg/"..pkginfo.filename,pkginfo.compressed)
  146. fs.remove("/boot/pkg/"..pkginfo.filename)
  147. return rv
  148. end
  149. pcall(activatePackage,"/boot/pkg/"..pkginfo.filename,pkginfo.compressed)
  150. return true
  151. end
  152. function pkg.upgrade(force) -- boolean -- -- Upgrades all packages on the system to the current version stored in the relevant repository. If *force* is set, re-download all packages.
  153. pkg.update()
  154. fs.makeDirectory("/boot/pkg")
  155. local installed = getInstalled()
  156. for repo,info in pairs(getSources()) do
  157. for pkgname,pkginfo in pairs(getRepoMeta(repo)) do
  158. if installed[pkgname] and pkginfo.version ~= installed[pkgname].version or force then
  159. pkg.remove(pkgname)
  160. pkg.get(pkgname,pkginfo.auto)
  161. end
  162. end
  163. end
  164. end
  165. function pkg.remove(pkgname) -- string -- boolean -- Remove the package *pkgname* from the pkgfs and package directory.
  166. local installed = getInstalled()
  167. local pkginfo = installed[pkgname]
  168. if not pkginfo then return true end
  169. pcall(deactivatePackage,"/boot/pkg/"..pkginfo.filename)
  170. fs.remove("/boot/pkg/"..pkginfo.filename)
  171. if pkginfo.system then
  172. for k,v in pairs(pkginfo.files) do
  173. fs.remove(v)
  174. end
  175. end
  176. installed[pkgname] = nil
  177. saveInstalled(installed)
  178. return true
  179. end
  180. return pkg