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 section[k] ~= v then
88 local function helper (section)
90 if not comparator or comparator(section) then
91 del[#del+1] = 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 a boolean option and return it's value as true or false.
139 -- @param config UCI config
140 -- @param section UCI section name
141 -- @param option UCI option
143 function Cursor.get_bool(self, ...)
144 local val = self:get(...)
145 return ( val == "1" or val == "true" or val == "yes" or val == "on" )
148 --- Get an option or list and return values as table.
149 -- @param config UCI config
150 -- @param section UCI section name
151 -- @param option UCI option
153 function Cursor.get_list(self, config, section, option)
154 if config and section and option then
155 local val = self:get(config, section, option)
156 return ( type(val) == "table" and val or { val } )
161 --- Set given values as list.
162 -- @param config UCI config
163 -- @param section UCI section name
164 -- @param option UCI option
165 -- @param value UCI value
166 -- @return Boolean whether operation succeeded
167 function Cursor.set_list(self, config, section, option, value)
168 if config and section and option then
170 config, section, option,
171 ( type(value) == "table" and value or { value } )
177 -- Return a list of initscripts affected by configuration changes.
178 function Cursor._affected(self, configlist)
179 configlist = type(configlist) == "table" and configlist or {configlist}
184 -- Resolve dependencies
185 local reloadlist = {}
187 local function _resolve_deps(name)
188 local reload = {name}
191 c:foreach("ucitrack", name,
193 if section.affects then
194 for i, aff in ipairs(section.affects) do
200 for i, dep in ipairs(deps) do
201 for j, add in ipairs(_resolve_deps(dep)) do
202 reload[#reload+1] = add
209 -- Collect initscripts
210 for j, config in ipairs(configlist) do
211 for i, e in ipairs(_resolve_deps(config)) do
212 if not util.contains(reloadlist, e) then
213 reloadlist[#reloadlist+1] = e
222 --- Add an anonymous section.
225 -- @param config UCI config
226 -- @param type UCI section type
227 -- @return Name of created section
229 --- Get a table of unsaved changes.
231 -- @name Cursor.changes
232 -- @param config UCI config
233 -- @return Table of changes
235 --- Commit unsaved changes.
237 -- @name Cursor.commit
238 -- @param config UCI config
239 -- @return Boolean whether operation succeeded
240 -- @see Cursor.revert
242 --- Deletes a section or an option.
244 -- @name Cursor.delete
245 -- @param config UCI config
246 -- @param section UCI section name
247 -- @param option UCI option (optional)
248 -- @return Boolean whether operation succeeded
250 --- Call a function for every section of a certain type.
252 -- @name Cursor.foreach
253 -- @param config UCI config
254 -- @param type UCI section type
255 -- @param callback Function to be called
256 -- @return Boolean whether operation succeeded
258 --- Get a section type or an option
261 -- @param config UCI config
262 -- @param section UCI section name
263 -- @param option UCI option (optional)
266 --- Get all sections of a config or all values of a section.
268 -- @name Cursor.get_all
269 -- @param config UCI config
270 -- @param section UCI section name (optional)
271 -- @return Table of UCI sections or table of UCI values
273 --- Manually load a config.
276 -- @param config UCI config
277 -- @return Boolean whether operation succeeded
279 -- @see Cursor.unload
281 --- Revert unsaved changes.
283 -- @name Cursor.revert
284 -- @param config UCI config
285 -- @return Boolean whether operation succeeded
286 -- @see Cursor.commit
288 --- Saves changes made to a config to make them committable.
291 -- @param config UCI config
292 -- @return Boolean whether operation succeeded
294 -- @see Cursor.unload
296 --- Set a value or create a named section.
299 -- @param config UCI config
300 -- @param section UCI section name
301 -- @param option UCI option or UCI section type
302 -- @param value UCI value or nil if you want to create a section
303 -- @return Boolean whether operation succeeded
305 --- Get the configuration directory.
307 -- @name Cursor.get_confdir
308 -- @return Configuration directory
310 --- Get the directory for uncomitted changes.
312 -- @name Cursor.get_savedir
313 -- @return Save directory
315 --- Set the configuration directory.
317 -- @name Cursor.set_confdir
318 -- @param directory UCI configuration directory
319 -- @return Boolean whether operation succeeded
321 --- Set the directory for uncommited changes.
323 -- @name Cursor.set_savedir
324 -- @param directory UCI changes directory
325 -- @return Boolean whether operation succeeded
327 --- Discard changes made to a config.
329 -- @name Cursor.unload
330 -- @param config UCI config
331 -- @return Boolean whether operation succeeded