11 Copyright 2008 Steven Barth <steven@midlink.org>
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
17 http://www.apache.org/licenses/LICENSE-2.0
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
26 local os = require "os"
27 local uci = require "uci"
28 local util = require "luci.util"
29 local table = require "table"
32 local setmetatable, rawget, rawset = setmetatable, rawget, rawset
33 local require, getmetatable = require, getmetatable
34 local error, pairs, ipairs = error, pairs, ipairs
35 local type, tostring, tonumber = type, tostring, tonumber
37 --- LuCI UCI model library.
38 -- The typical workflow for UCI is: Get a cursor instance from the
39 -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
40 -- save the changes to the staging area via Cursor.save and finally
41 -- Cursor.commit the data to the actual config files.
42 -- LuCI then needs to Cursor.apply the changes so deamons etc. are
45 module "luci.model.uci"
47 --- Create a new UCI-Cursor.
53 APIVERSION = uci.APIVERSION
55 --- Create a new Cursor initialized to the state directory.
57 function cursor_state()
58 return cursor(nil, "/var/state")
63 inst_state = cursor_state()
65 local Cursor = getmetatable(inst)
67 --- Applies UCI configuration changes
68 -- @param configlist List of UCI configurations
69 -- @param command Don't apply only return the command
70 function Cursor.apply(self, configlist, command)
71 configlist = self:_affected(configlist)
72 local reloadcmd = "/sbin/luci-reload " .. table.concat(configlist, " ")
74 return command and reloadcmd or os.execute(reloadcmd .. " >/dev/null 2>&1")
78 --- Delete all sections of a given type that match certain criteria.
79 -- @param config UCI config
80 -- @param type UCI section type
81 -- @param comparator Function that will be called for each section and
82 -- returns a boolean whether to delete the current section (optional)
83 function Cursor.delete_all(self, config, stype, comparator)
86 if type(comparator) == "table" then
87 local tbl = comparator
88 comparator = function(section)
89 for k, v in pairs(tbl) do
90 if section[k] ~= v then
98 local function helper (section)
100 if not comparator or comparator(section) then
101 del[#del+1] = section[".name"]
105 self:foreach(config, stype, helper)
107 for i, j in ipairs(del) do
108 self:delete(config, j)
112 --- Create a new section and initialize it with data.
113 -- @param config UCI config
114 -- @param type UCI section type
115 -- @param name UCI section name (optional)
116 -- @param values Table of key - value pairs to initialize the section with
117 -- @return Name of created section
118 function Cursor.section(self, config, type, name, values)
121 stat = self:set(config, name, type)
123 name = self:add(config, type)
127 if stat and values then
128 stat = self:tset(config, name, values)
134 --- Updated the data of a section using data from a table.
135 -- @param config UCI config
136 -- @param section UCI section name (optional)
137 -- @param values Table of key - value pairs to update the section with
138 function Cursor.tset(self, config, section, values)
140 for k, v in pairs(values) do
141 if k:sub(1, 1) ~= "." then
142 stat = stat and self:set(config, section, k, v)
148 --- Get a boolean option and return it's value as true or false.
149 -- @param config UCI config
150 -- @param section UCI section name
151 -- @param option UCI option
153 function Cursor.get_bool(self, ...)
154 local val = self:get(...)
155 return ( val == "1" or val == "true" or val == "yes" or val == "on" )
158 --- Get an option or list and return values as table.
159 -- @param config UCI config
160 -- @param section UCI section name
161 -- @param option UCI option
163 function Cursor.get_list(self, config, section, option)
164 if config and section and option then
165 local val = self:get(config, section, option)
166 return ( type(val) == "table" and val or { val } )
171 --- Get the given option from the first section with the given type.
172 -- @param config UCI config
173 -- @param type UCI section type
174 -- @param option UCI option (optional)
175 -- @param default Default value (optional)
177 function Cursor.get_first(self, conf, stype, opt, def)
180 self:foreach(conf, stype,
182 local val = not opt and s['.name'] or s[opt]
184 if type(def) == "number" then
186 elseif type(def) == "boolean" then
187 val = (val == "1" or val == "true" or val == "enabled")
199 --- Set given values as list.
200 -- @param config UCI config
201 -- @param section UCI section name
202 -- @param option UCI option
203 -- @param value UCI value
204 -- @return Boolean whether operation succeeded
205 function Cursor.set_list(self, config, section, option, value)
206 if config and section and option then
208 config, section, option,
209 ( type(value) == "table" and value or { value } )
215 -- Return a list of initscripts affected by configuration changes.
216 function Cursor._affected(self, configlist)
217 configlist = type(configlist) == "table" and configlist or {configlist}
222 -- Resolve dependencies
223 local reloadlist = {}
225 local function _resolve_deps(name)
226 local reload = {name}
229 c:foreach("ucitrack", name,
231 if section.affects then
232 for i, aff in ipairs(section.affects) do
238 for i, dep in ipairs(deps) do
239 for j, add in ipairs(_resolve_deps(dep)) do
240 reload[#reload+1] = add
247 -- Collect initscripts
248 for j, config in ipairs(configlist) do
249 for i, e in ipairs(_resolve_deps(config)) do
250 if not util.contains(reloadlist, e) then
251 reloadlist[#reloadlist+1] = e
259 --- Create a sub-state of this cursor. The sub-state is tied to the parent
260 -- curser, means it the parent unloads or loads configs, the sub state will
262 -- @return UCI state cursor tied to the parent cursor
263 function Cursor.substate(self)
264 Cursor._substates = Cursor._substates or { }
265 Cursor._substates[self] = Cursor._substates[self] or cursor_state()
266 return Cursor._substates[self]
269 local _load = Cursor.load
270 function Cursor.load(self, ...)
271 if Cursor._substates and Cursor._substates[self] then
272 _load(Cursor._substates[self], ...)
274 return _load(self, ...)
277 local _unload = Cursor.unload
278 function Cursor.unload(self, ...)
279 if Cursor._substates and Cursor._substates[self] then
280 _unload(Cursor._substates[self], ...)
282 return _unload(self, ...)
286 --- Add an anonymous section.
289 -- @param config UCI config
290 -- @param type UCI section type
291 -- @return Name of created section
293 --- Get a table of saved but uncommitted changes.
295 -- @name Cursor.changes
296 -- @param config UCI config
297 -- @return Table of changes
300 --- Commit saved changes.
302 -- @name Cursor.commit
303 -- @param config UCI config
304 -- @return Boolean whether operation succeeded
305 -- @see Cursor.revert
308 --- Deletes a section or an option.
310 -- @name Cursor.delete
311 -- @param config UCI config
312 -- @param section UCI section name
313 -- @param option UCI option (optional)
314 -- @return Boolean whether operation succeeded
316 --- Call a function for every section of a certain type.
318 -- @name Cursor.foreach
319 -- @param config UCI config
320 -- @param type UCI section type
321 -- @param callback Function to be called
322 -- @return Boolean whether operation succeeded
324 --- Get a section type or an option
327 -- @param config UCI config
328 -- @param section UCI section name
329 -- @param option UCI option (optional)
332 --- Get all sections of a config or all values of a section.
334 -- @name Cursor.get_all
335 -- @param config UCI config
336 -- @param section UCI section name (optional)
337 -- @return Table of UCI sections or table of UCI values
339 --- Manually load a config.
342 -- @param config UCI config
343 -- @return Boolean whether operation succeeded
345 -- @see Cursor.unload
347 --- Revert saved but uncommitted changes.
349 -- @name Cursor.revert
350 -- @param config UCI config
351 -- @return Boolean whether operation succeeded
352 -- @see Cursor.commit
355 --- Saves changes made to a config to make them committable.
358 -- @param config UCI config
359 -- @return Boolean whether operation succeeded
361 -- @see Cursor.unload
363 --- Set a value or create a named section.
366 -- @param config UCI config
367 -- @param section UCI section name
368 -- @param option UCI option or UCI section type
369 -- @param value UCI value or nil if you want to create a section
370 -- @return Boolean whether operation succeeded
372 --- Get the configuration directory.
374 -- @name Cursor.get_confdir
375 -- @return Configuration directory
377 --- Get the directory for uncomitted changes.
379 -- @name Cursor.get_savedir
380 -- @return Save directory
382 --- Set the configuration directory.
384 -- @name Cursor.set_confdir
385 -- @param directory UCI configuration directory
386 -- @return Boolean whether operation succeeded
388 --- Set the directory for uncommited changes.
390 -- @name Cursor.set_savedir
391 -- @param directory UCI changes directory
392 -- @return Boolean whether operation succeeded
394 --- Discard changes made to a config.
396 -- @name Cursor.unload
397 -- @param config UCI config
398 -- @return Boolean whether operation succeeded