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 error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
34 local require, getmetatable, type = require, getmetatable, type
36 --- LuCI UCI model library.
38 module "luci.model.uci"
40 --- Create a new UCI-Cursor.
46 APIVERSION = uci.APIVERSION
48 --- Create a new Cursor initialized to the state directory.
50 function cursor_state()
51 return cursor(nil, "/var/state")
55 local Cursor = getmetatable(cursor())
57 --- Applies UCI configuration changes
58 -- @param configlist List of UCI configurations
59 -- @param command Don't apply only return the command
60 function Cursor.apply(self, configlist, command)
61 configlist = self:_affected(configlist)
62 local reloadcmd = "/sbin/luci-reload " .. table.concat(configlist, " ")
64 return command and reloadcmd or os.execute(reloadcmd .. " >/dev/null 2>&1")
68 --- Delete all sections of a given type that match certain criteria.
69 -- @param config UCI config
70 -- @param type UCI section type
71 -- @param comparator Function that will be called for each section and
72 -- returns a boolean whether to delete the current section (optional)
73 function Cursor.delete_all(self, config, stype, comparator)
76 if type(comparator) == "table" then
77 local tbl = comparator
78 comparator = function(section)
79 for k, v in pairs(tbl) do
80 if not section[k] == v then
88 local function helper (section)
90 if not comparator or comparator(section) then
91 table.insert(del, section[".name"])
95 self:foreach(config, stype, helper)
97 for i, j in ipairs(del) do
98 self:delete(config, j)
102 --- Create a new section and initialize it with data.
103 -- @param config UCI config
104 -- @param type UCI section type
105 -- @param name UCI section name (optional)
106 -- @param values Table of key - value pairs to initialize the section with
107 -- @return Name of created section
108 function Cursor.section(self, config, type, name, values)
111 stat = self:set(config, name, type)
113 name = self:add(config, type)
117 if stat and values then
118 stat = self:tset(config, name, values)
124 --- Updated the data of a section using data from a table.
125 -- @param config UCI config
126 -- @param section UCI section name (optional)
127 -- @param values Table of key - value pairs to update the section with
128 function Cursor.tset(self, config, section, values)
130 for k, v in pairs(values) do
131 if k:sub(1, 1) ~= "." then
132 stat = stat and self:set(config, section, k, v)
138 --- Get an option or list and return values as table.
139 -- @param config UCI config
140 -- @param section UCI section name
141 -- @param option UCI option
143 function Cursor.get_list(self, config, section, option)
144 if config and section and option then
145 local val = self:get(config, section, option)
146 return ( type(val) == "table" and val or { val } )
151 --- Set given values as list.
152 -- @param config UCI config
153 -- @param section UCI section name
154 -- @param option UCI option
155 -- @param value UCI value
156 -- @return Boolean whether operation succeeded
157 function Cursor.set_list(self, config, section, option, value)
158 if config and section and option then
160 config, section, option,
161 ( type(value) == "table" and value or { value } )
168 Cursor._changes = Cursor.changes
169 function Cursor.changes(self, config)
171 return Cursor._changes(self, config)
173 local changes = Cursor._changes(self)
174 util.copcall(function()
175 for k,v in pairs(require "luci.fs".dir(self:get_savedir())) do
176 if v ~= "." and v ~= ".." then
177 util.update(changes, Cursor._changes(self, v))
186 -- Return a list of initscripts affected by configuration changes.
187 function Cursor._affected(self, configlist)
188 configlist = type(configlist) == "table" and configlist or {configlist}
193 -- Resolve dependencies
194 local reloadlist = {}
196 local function _resolve_deps(name)
197 local reload = {name}
200 c:foreach("ucitrack", name,
202 if section.affects then
203 for i, aff in ipairs(section.affects) do
204 table.insert(deps, aff)
209 for i, dep in ipairs(deps) do
210 for j, add in ipairs(_resolve_deps(dep)) do
211 table.insert(reload, add)
218 -- Collect initscripts
219 for j, config in ipairs(configlist) do
220 for i, e in ipairs(_resolve_deps(config)) do
221 if not util.contains(reloadlist, e) then
222 table.insert(reloadlist, e)
231 --- Add an anonymous section.
234 -- @param config UCI config
235 -- @param type UCI section type
236 -- @return Name of created section
238 --- Get a table of unsaved changes.
240 -- @name Cursor.changes
241 -- @param config UCI config
242 -- @return Table of changes
244 --- Commit unsaved changes.
246 -- @name Cursor.commit
247 -- @param config UCI config
248 -- @return Boolean whether operation succeeded
249 -- @see Cursor.revert
251 --- Deletes a section or an option.
253 -- @name Cursor.delete
254 -- @param config UCI config
255 -- @param section UCI section name
256 -- @param option UCI option (optional)
257 -- @return Boolean whether operation succeeded
259 --- Call a function for every section of a certain type.
261 -- @name Cursor.foreach
262 -- @param config UCI config
263 -- @param type UCI section type
264 -- @param callback Function to be called
265 -- @return Boolean whether operation succeeded
267 --- Get a section type or an option
270 -- @param config UCI config
271 -- @param section UCI section name
272 -- @param option UCI option (optional)
275 --- Get all sections of a config or all values of a section.
277 -- @name Cursor.get_all
278 -- @param config UCI config
279 -- @param section UCI section name (optional)
280 -- @return Table of UCI sections or table of UCI values
282 --- Manually load a config.
285 -- @param config UCI config
286 -- @return Boolean whether operation succeeded
288 -- @see Cursor.unload
290 --- Revert unsaved changes.
292 -- @name Cursor.revert
293 -- @param config UCI config
294 -- @return Boolean whether operation succeeded
295 -- @see Cursor.commit
297 --- Saves changes made to a config to make them committable.
300 -- @param config UCI config
301 -- @return Boolean whether operation succeeded
303 -- @see Cursor.unload
305 --- Set a value or create a named section.
308 -- @param config UCI config
309 -- @param section UCI section name
310 -- @param option UCI option or UCI section type
311 -- @param value UCI value or nil if you want to create a section
312 -- @return Boolean whether operation succeeded
314 --- Get the configuration directory.
316 -- @name Cursor.get_confdir
317 -- @return Configuration directory
319 --- Get the directory for uncomitted changes.
321 -- @name Cursor.get_savedir
322 -- @return Save directory
324 --- Set the configuration directory.
326 -- @name Cursor.set_confdir
327 -- @param directory UCI configuration directory
328 -- @return Boolean whether operation succeeded
330 --- Set the directory for uncommited changes.
332 -- @name Cursor.set_savedir
333 -- @param directory UCI changes directory
334 -- @return Boolean whether operation succeeded
336 --- Discard changes made to a config.
338 -- @name Cursor.unload
339 -- @param config UCI config
340 -- @return Boolean whether operation succeeded