uci.lua 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. -- Copyright 2008 Steven Barth <steven@midlink.org>
  2. -- Licensed to the public under the Apache License 2.0.
  3. local os = require "os"
  4. local uci = require "uci"
  5. local util = require "luci.util"
  6. local table = require "table"
  7. local setmetatable, rawget, rawset = setmetatable, rawget, rawset
  8. local require, getmetatable = require, getmetatable
  9. local error, pairs, ipairs = error, pairs, ipairs
  10. local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
  11. -- The typical workflow for UCI is: Get a cursor instance from the
  12. -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
  13. -- save the changes to the staging area via Cursor.save and finally
  14. -- Cursor.commit the data to the actual config files.
  15. -- LuCI then needs to Cursor.apply the changes so deamons etc. are
  16. -- reloaded.
  17. module "luci.model.uci"
  18. cursor = uci.cursor
  19. APIVERSION = uci.APIVERSION
  20. function cursor_state()
  21. return cursor(nil, "/var/state")
  22. end
  23. inst = cursor()
  24. inst_state = cursor_state()
  25. local Cursor = getmetatable(inst)
  26. function Cursor.apply(self, configlist, command)
  27. configlist = self:_affected(configlist)
  28. if command then
  29. return { "/sbin/luci-reload", unpack(configlist) }
  30. else
  31. return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
  32. % table.concat(configlist, " "))
  33. end
  34. end
  35. -- returns a boolean whether to delete the current section (optional)
  36. function Cursor.delete_all(self, config, stype, comparator)
  37. local del = {}
  38. if type(comparator) == "table" then
  39. local tbl = comparator
  40. comparator = function(section)
  41. for k, v in pairs(tbl) do
  42. if section[k] ~= v then
  43. return false
  44. end
  45. end
  46. return true
  47. end
  48. end
  49. local function helper (section)
  50. if not comparator or comparator(section) then
  51. del[#del+1] = section[".name"]
  52. end
  53. end
  54. self:foreach(config, stype, helper)
  55. for i, j in ipairs(del) do
  56. self:delete(config, j)
  57. end
  58. end
  59. function Cursor.section(self, config, type, name, values)
  60. local stat = true
  61. if name then
  62. stat = self:set(config, name, type)
  63. else
  64. name = self:add(config, type)
  65. stat = name and true
  66. end
  67. if stat and values then
  68. stat = self:tset(config, name, values)
  69. end
  70. return stat and name
  71. end
  72. function Cursor.tset(self, config, section, values)
  73. local stat = true
  74. for k, v in pairs(values) do
  75. if k:sub(1, 1) ~= "." then
  76. stat = stat and self:set(config, section, k, v)
  77. end
  78. end
  79. return stat
  80. end
  81. function Cursor.get_bool(self, ...)
  82. local val = self:get(...)
  83. return ( val == "1" or val == "true" or val == "yes" or val == "on" )
  84. end
  85. function Cursor.get_list(self, config, section, option)
  86. if config and section and option then
  87. local val = self:get(config, section, option)
  88. return ( type(val) == "table" and val or { val } )
  89. end
  90. return {}
  91. end
  92. function Cursor.get_first(self, conf, stype, opt, def)
  93. local rv = def
  94. self:foreach(conf, stype,
  95. function(s)
  96. local val = not opt and s['.name'] or s[opt]
  97. if type(def) == "number" then
  98. val = tonumber(val)
  99. elseif type(def) == "boolean" then
  100. val = (val == "1" or val == "true" or
  101. val == "yes" or val == "on")
  102. end
  103. if val ~= nil then
  104. rv = val
  105. return false
  106. end
  107. end)
  108. return rv
  109. end
  110. function Cursor.set_list(self, config, section, option, value)
  111. if config and section and option then
  112. if not value or #value == 0 then
  113. return self:delete(config, section, option)
  114. end
  115. return self:set(
  116. config, section, option,
  117. ( type(value) == "table" and value or { value } )
  118. )
  119. end
  120. return false
  121. end
  122. -- Return a list of initscripts affected by configuration changes.
  123. function Cursor._affected(self, configlist)
  124. configlist = type(configlist) == "table" and configlist or {configlist}
  125. local c = cursor()
  126. c:load("ucitrack")
  127. -- Resolve dependencies
  128. local reloadlist = {}
  129. local function _resolve_deps(name)
  130. local reload = {name}
  131. local deps = {}
  132. c:foreach("ucitrack", name,
  133. function(section)
  134. if section.affects then
  135. for i, aff in ipairs(section.affects) do
  136. deps[#deps+1] = aff
  137. end
  138. end
  139. end)
  140. for i, dep in ipairs(deps) do
  141. for j, add in ipairs(_resolve_deps(dep)) do
  142. reload[#reload+1] = add
  143. end
  144. end
  145. return reload
  146. end
  147. -- Collect initscripts
  148. for j, config in ipairs(configlist) do
  149. for i, e in ipairs(_resolve_deps(config)) do
  150. if not util.contains(reloadlist, e) then
  151. reloadlist[#reloadlist+1] = e
  152. end
  153. end
  154. end
  155. return reloadlist
  156. end
  157. -- curser, means it the parent unloads or loads configs, the sub state will
  158. -- do so as well.
  159. function Cursor.substate(self)
  160. Cursor._substates = Cursor._substates or { }
  161. Cursor._substates[self] = Cursor._substates[self] or cursor_state()
  162. return Cursor._substates[self]
  163. end
  164. local _load = Cursor.load
  165. function Cursor.load(self, ...)
  166. if Cursor._substates and Cursor._substates[self] then
  167. _load(Cursor._substates[self], ...)
  168. end
  169. return _load(self, ...)
  170. end
  171. local _unload = Cursor.unload
  172. function Cursor.unload(self, ...)
  173. if Cursor._substates and Cursor._substates[self] then
  174. _unload(Cursor._substates[self], ...)
  175. end
  176. return _unload(self, ...)
  177. end