ipkg.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. -- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
  2. -- Copyright 2008 Steven Barth <steven@midlink.org>
  3. -- Licensed to the public under the Apache License 2.0.
  4. local os = require "os"
  5. local io = require "io"
  6. local fs = require "nixio.fs"
  7. local util = require "luci.util"
  8. local type = type
  9. local pairs = pairs
  10. local error = error
  11. local table = table
  12. local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
  13. local icfg = "/etc/opkg.conf"
  14. module "luci.model.ipkg"
  15. -- Internal action function
  16. local function _action(cmd, ...)
  17. local cmdline = { ipkg, cmd }
  18. local k, v
  19. for k, v in pairs({...}) do
  20. cmdline[#cmdline+1] = util.shellquote(v)
  21. end
  22. local c = "%s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" % table.concat(cmdline, " ")
  23. local r = os.execute(c)
  24. local e = fs.readfile("/tmp/opkg.stderr")
  25. local o = fs.readfile("/tmp/opkg.stdout")
  26. fs.unlink("/tmp/opkg.stderr")
  27. fs.unlink("/tmp/opkg.stdout")
  28. return r, o or "", e or ""
  29. end
  30. -- Internal parser function
  31. local function _parselist(rawdata)
  32. if type(rawdata) ~= "function" then
  33. error("OPKG: Invalid rawdata given")
  34. end
  35. local data = {}
  36. local c = {}
  37. local l = nil
  38. for line in rawdata do
  39. if line:sub(1, 1) ~= " " then
  40. local key, val = line:match("(.-): ?(.*)%s*")
  41. if key and val then
  42. if key == "Package" then
  43. c = {Package = val}
  44. data[val] = c
  45. elseif key == "Status" then
  46. c.Status = {}
  47. for j in val:gmatch("([^ ]+)") do
  48. c.Status[j] = true
  49. end
  50. else
  51. c[key] = val
  52. end
  53. l = key
  54. end
  55. else
  56. -- Multi-line field
  57. c[l] = c[l] .. "\n" .. line
  58. end
  59. end
  60. return data
  61. end
  62. -- Internal lookup function
  63. local function _lookup(cmd, pkg)
  64. local cmdline = { ipkg, cmd }
  65. if pkg then
  66. cmdline[#cmdline+1] = util.shellquote(pkg)
  67. end
  68. -- OPKG sometimes kills the whole machine because it sucks
  69. -- Therefore we have to use a sucky approach too and use
  70. -- tmpfiles instead of directly reading the output
  71. local tmpfile = os.tmpname()
  72. os.execute("%s >%s 2>/dev/null" %{ table.concat(cmdline, " "), tmpfile })
  73. local data = _parselist(io.lines(tmpfile))
  74. os.remove(tmpfile)
  75. return data
  76. end
  77. function info(pkg)
  78. return _lookup("info", pkg)
  79. end
  80. function status(pkg)
  81. return _lookup("status", pkg)
  82. end
  83. function install(...)
  84. return _action("install", ...)
  85. end
  86. function installed(pkg)
  87. local p = status(pkg)[pkg]
  88. return (p and p.Status and p.Status.installed)
  89. end
  90. function remove(...)
  91. return _action("remove", ...)
  92. end
  93. function update()
  94. return _action("update")
  95. end
  96. function upgrade()
  97. return _action("upgrade")
  98. end
  99. -- List helper
  100. local function _list(action, pat, cb)
  101. local cmdline = { ipkg, action }
  102. if pat then
  103. cmdline[#cmdline+1] = util.shellquote(pat)
  104. end
  105. local fd = io.popen(table.concat(cmdline, " "))
  106. if fd then
  107. local name, version, sz, desc
  108. while true do
  109. local line = fd:read("*l")
  110. if not line then break end
  111. name, version, sz, desc = line:match("^(.-) %- (.-) %- (.-) %- (.+)")
  112. if not name then
  113. name, version, sz = line:match("^(.-) %- (.-) %- (.+)")
  114. desc = ""
  115. end
  116. if name and version then
  117. if #version > 26 then
  118. version = version:sub(1,21) .. ".." .. version:sub(-3,-1)
  119. end
  120. cb(name, version, sz, desc)
  121. end
  122. name = nil
  123. version = nil
  124. sz = nil
  125. desc = nil
  126. end
  127. fd:close()
  128. end
  129. end
  130. function list_all(pat, cb)
  131. _list("list --size", pat, cb)
  132. end
  133. function list_installed(pat, cb)
  134. _list("list_installed --size", pat, cb)
  135. end
  136. function find(pat, cb)
  137. _list("find --size", pat, cb)
  138. end
  139. function overlay_root()
  140. local od = "/"
  141. local fd = io.open(icfg, "r")
  142. if fd then
  143. local ln
  144. repeat
  145. ln = fd:read("*l")
  146. if ln and ln:match("^%s*option%s+overlay_root%s+") then
  147. od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
  148. local s = fs.stat(od)
  149. if not s or s.type ~= "dir" then
  150. od = "/"
  151. end
  152. break
  153. end
  154. until not ln
  155. fd:close()
  156. end
  157. return od
  158. end
  159. function compare_versions(ver1, comp, ver2)
  160. if not ver1 or not ver2
  161. or not comp or not (#comp > 0) then
  162. error("Invalid parameters")
  163. return nil
  164. end
  165. -- correct compare string
  166. if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
  167. elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
  168. elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
  169. elseif comp == "=" or comp == "==" then comp = "=="
  170. elseif comp == "<<" then comp = "<"
  171. elseif comp == ">>" then comp = ">"
  172. else
  173. error("Invalid compare string")
  174. return nil
  175. end
  176. local av1 = util.split(ver1, "[%.%-]", nil, true)
  177. local av2 = util.split(ver2, "[%.%-]", nil, true)
  178. local max = table.getn(av1)
  179. if (table.getn(av1) < table.getn(av2)) then
  180. max = table.getn(av2)
  181. end
  182. for i = 1, max, 1 do
  183. local s1 = av1[i] or ""
  184. local s2 = av2[i] or ""
  185. -- first "not equal" found return true
  186. if comp == "~=" and (s1 ~= s2) then return true end
  187. -- first "lower" found return true
  188. if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
  189. -- first "greater" found return true
  190. if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
  191. -- not equal then return false
  192. if (s1 ~= s2) then return false end
  193. end
  194. -- all equal and not compare greater or lower then true
  195. return not (comp == "<" or comp == ">")
  196. end