1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Licensed to the public under the Apache License 2.0.
4 local os = require "os"
5 local uci = require "uci"
6 local util = require "luci.util"
7 local table = require "table"
10 local setmetatable, rawget, rawset = setmetatable, rawget, rawset
11 local require, getmetatable = require, getmetatable
12 local error, pairs, ipairs = error, pairs, ipairs
13 local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
15 --- LuCI UCI model library.
16 -- The typical workflow for UCI is: Get a cursor instance from the
17 -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
18 -- save the changes to the staging area via Cursor.save and finally
19 -- Cursor.commit the data to the actual config files.
20 -- LuCI then needs to Cursor.apply the changes so deamons etc. are
23 module "luci.model.uci"
25 --- Create a new UCI-Cursor.
31 APIVERSION = uci.APIVERSION
33 --- Create a new Cursor initialized to the state directory.
35 function cursor_state()
36 return cursor(nil, "/var/state")
41 inst_state = cursor_state()
43 local Cursor = getmetatable(inst)
45 --- Applies UCI configuration changes
46 -- @param configlist List of UCI configurations
47 -- @param command Don't apply only return the command
48 function Cursor.apply(self, configlist, command)
49 configlist = self:_affected(configlist)
51 return { "/sbin/luci-reload", unpack(configlist) }
53 return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
54 % table.concat(configlist, " "))
59 --- Delete all sections of a given type that match certain criteria.
60 -- @param config UCI config
61 -- @param type UCI section type
62 -- @param comparator Function that will be called for each section and
63 -- returns a boolean whether to delete the current section (optional)
64 function Cursor.delete_all(self, config, stype, comparator)
67 if type(comparator) == "table" then
68 local tbl = comparator
69 comparator = function(section)
70 for k, v in pairs(tbl) do
71 if section[k] ~= v then
79 local function helper (section)
81 if not comparator or comparator(section) then
82 del[#del+1] = section[".name"]
86 self:foreach(config, stype, helper)
88 for i, j in ipairs(del) do
89 self:delete(config, j)
93 --- Create a new section and initialize it with data.
94 -- @param config UCI config
95 -- @param type UCI section type
96 -- @param name UCI section name (optional)
97 -- @param values Table of key - value pairs to initialize the section with
98 -- @return Name of created section
99 function Cursor.section(self, config, type, name, values)
102 stat = self:set(config, name, type)
104 name = self:add(config, type)
108 if stat and values then
109 stat = self:tset(config, name, values)
115 --- Updated the data of a section using data from a table.
116 -- @param config UCI config
117 -- @param section UCI section name (optional)
118 -- @param values Table of key - value pairs to update the section with
119 function Cursor.tset(self, config, section, values)
121 for k, v in pairs(values) do
122 if k:sub(1, 1) ~= "." then
123 stat = stat and self:set(config, section, k, v)
129 --- Get a boolean option and return it's value as true or false.
130 -- @param config UCI config
131 -- @param section UCI section name
132 -- @param option UCI option
134 function Cursor.get_bool(self, ...)
135 local val = self:get(...)
136 return ( val == "1" or val == "true" or val == "yes" or val == "on" )
139 --- Get an option or list and return values as table.
140 -- @param config UCI config
141 -- @param section UCI section name
142 -- @param option UCI option
144 function Cursor.get_list(self, config, section, option)
145 if config and section and option then
146 local val = self:get(config, section, option)
147 return ( type(val) == "table" and val or { val } )
152 --- Get the given option from the first section with the given type.
153 -- @param config UCI config
154 -- @param type UCI section type
155 -- @param option UCI option (optional)
156 -- @param default Default value (optional)
158 function Cursor.get_first(self, conf, stype, opt, def)
161 self:foreach(conf, stype,
163 local val = not opt and s['.name'] or s[opt]
165 if type(def) == "number" then
167 elseif type(def) == "boolean" then
168 val = (val == "1" or val == "true" or
169 val == "yes" or val == "on")
181 --- Set given values as list.
182 -- @param config UCI config
183 -- @param section UCI section name
184 -- @param option UCI option
185 -- @param value UCI value
186 -- @return Boolean whether operation succeeded
187 function Cursor.set_list(self, config, section, option, value)
188 if config and section and option then
190 config, section, option,
191 ( type(value) == "table" and value or { value } )
197 -- Return a list of initscripts affected by configuration changes.
198 function Cursor._affected(self, configlist)
199 configlist = type(configlist) == "table" and configlist or {configlist}
204 -- Resolve dependencies
205 local reloadlist = {}
207 local function _resolve_deps(name)
208 local reload = {name}
211 c:foreach("ucitrack", name,
213 if section.affects then
214 for i, aff in ipairs(section.affects) do
220 for i, dep in ipairs(deps) do
221 for j, add in ipairs(_resolve_deps(dep)) do
222 reload[#reload+1] = add
229 -- Collect initscripts
230 for j, config in ipairs(configlist) do
231 for i, e in ipairs(_resolve_deps(config)) do
232 if not util.contains(reloadlist, e) then
233 reloadlist[#reloadlist+1] = e
241 --- Create a sub-state of this cursor. The sub-state is tied to the parent
242 -- curser, means it the parent unloads or loads configs, the sub state will
244 -- @return UCI state cursor tied to the parent cursor
245 function Cursor.substate(self)
246 Cursor._substates = Cursor._substates or { }
247 Cursor._substates[self] = Cursor._substates[self] or cursor_state()
248 return Cursor._substates[self]
251 local _load = Cursor.load
252 function Cursor.load(self, ...)
253 if Cursor._substates and Cursor._substates[self] then
254 _load(Cursor._substates[self], ...)
256 return _load(self, ...)
259 local _unload = Cursor.unload
260 function Cursor.unload(self, ...)
261 if Cursor._substates and Cursor._substates[self] then
262 _unload(Cursor._substates[self], ...)
264 return _unload(self, ...)
268 --- Add an anonymous section.
271 -- @param config UCI config
272 -- @param type UCI section type
273 -- @return Name of created section
275 --- Get a table of saved but uncommitted changes.
277 -- @name Cursor.changes
278 -- @param config UCI config
279 -- @return Table of changes
282 --- Commit saved changes.
284 -- @name Cursor.commit
285 -- @param config UCI config
286 -- @return Boolean whether operation succeeded
287 -- @see Cursor.revert
290 --- Deletes a section or an option.
292 -- @name Cursor.delete
293 -- @param config UCI config
294 -- @param section UCI section name
295 -- @param option UCI option (optional)
296 -- @return Boolean whether operation succeeded
298 --- Call a function for every section of a certain type.
300 -- @name Cursor.foreach
301 -- @param config UCI config
302 -- @param type UCI section type
303 -- @param callback Function to be called
304 -- @return Boolean whether operation succeeded
306 --- Get a section type or an option
309 -- @param config UCI config
310 -- @param section UCI section name
311 -- @param option UCI option (optional)
314 --- Get all sections of a config or all values of a section.
316 -- @name Cursor.get_all
317 -- @param config UCI config
318 -- @param section UCI section name (optional)
319 -- @return Table of UCI sections or table of UCI values
321 --- Manually load a config.
324 -- @param config UCI config
325 -- @return Boolean whether operation succeeded
327 -- @see Cursor.unload
329 --- Revert saved but uncommitted changes.
331 -- @name Cursor.revert
332 -- @param config UCI config
333 -- @return Boolean whether operation succeeded
334 -- @see Cursor.commit
337 --- Saves changes made to a config to make them committable.
340 -- @param config UCI config
341 -- @return Boolean whether operation succeeded
343 -- @see Cursor.unload
345 --- Set a value or create a named section.
348 -- @param config UCI config
349 -- @param section UCI section name
350 -- @param option UCI option or UCI section type
351 -- @param value UCI value or nil if you want to create a section
352 -- @return Boolean whether operation succeeded
354 --- Get the configuration directory.
356 -- @name Cursor.get_confdir
357 -- @return Configuration directory
359 --- Get the directory for uncomitted changes.
361 -- @name Cursor.get_savedir
362 -- @return Save directory
364 --- Set the configuration directory.
366 -- @name Cursor.set_confdir
367 -- @param directory UCI configuration directory
368 -- @return Boolean whether operation succeeded
370 --- Set the directory for uncommited changes.
372 -- @name Cursor.set_savedir
373 -- @param directory UCI changes directory
374 -- @return Boolean whether operation succeeded
376 --- Discard changes made to a config.
378 -- @name Cursor.unload
379 -- @param config UCI config
380 -- @return Boolean whether operation succeeded