build: introduce luci-base
[project/luci.git] / modules / base / luasrc / model / uci.lua
1 --[[
2 LuCI - UCI model
3
4 Description:
5 Generalized UCI model
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
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
16
17         http://www.apache.org/licenses/LICENSE-2.0
18
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.
24
25 ]]--
26 local os    = require "os"
27 local uci   = require "uci"
28 local util  = require "luci.util"
29 local table = require "table"
30
31
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, unpack = type, tostring, tonumber, unpack
36
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
43 -- reloaded.
44 -- @cstyle      instance
45 module "luci.model.uci"
46
47 --- Create a new UCI-Cursor.
48 -- @class function
49 -- @name cursor
50 -- @return      UCI-Cursor
51 cursor = uci.cursor
52
53 APIVERSION = uci.APIVERSION
54
55 --- Create a new Cursor initialized to the state directory.
56 -- @return UCI cursor
57 function cursor_state()
58         return cursor(nil, "/var/state")
59 end
60
61
62 inst = cursor()
63 inst_state = cursor_state()
64
65 local Cursor = getmetatable(inst)
66
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         if command then
73                 return { "/sbin/luci-reload", unpack(configlist) }
74         else
75                 return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
76                         % table.concat(configlist, " "))
77         end
78 end
79
80
81 --- Delete all sections of a given type that match certain criteria.
82 -- @param config                UCI config
83 -- @param type                  UCI section type
84 -- @param comparator    Function that will be called for each section and
85 -- returns a boolean whether to delete the current section (optional)
86 function Cursor.delete_all(self, config, stype, comparator)
87         local del = {}
88
89         if type(comparator) == "table" then
90                 local tbl = comparator
91                 comparator = function(section)
92                         for k, v in pairs(tbl) do
93                                 if section[k] ~= v then
94                                         return false
95                                 end
96                         end
97                         return true
98                 end
99         end
100
101         local function helper (section)
102
103                 if not comparator or comparator(section) then
104                         del[#del+1] = section[".name"]
105                 end
106         end
107
108         self:foreach(config, stype, helper)
109
110         for i, j in ipairs(del) do
111                 self:delete(config, j)
112         end
113 end
114
115 --- Create a new section and initialize it with data.
116 -- @param config        UCI config
117 -- @param type          UCI section type
118 -- @param name          UCI section name (optional)
119 -- @param values        Table of key - value pairs to initialize the section with
120 -- @return                      Name of created section
121 function Cursor.section(self, config, type, name, values)
122         local stat = true
123         if name then
124                 stat = self:set(config, name, type)
125         else
126                 name = self:add(config, type)
127                 stat = name and true
128         end
129
130         if stat and values then
131                 stat = self:tset(config, name, values)
132         end
133
134         return stat and name
135 end
136
137 --- Updated the data of a section using data from a table.
138 -- @param config        UCI config
139 -- @param section       UCI section name (optional)
140 -- @param values        Table of key - value pairs to update the section with
141 function Cursor.tset(self, config, section, values)
142         local stat = true
143         for k, v in pairs(values) do
144                 if k:sub(1, 1) ~= "." then
145                         stat = stat and self:set(config, section, k, v)
146                 end
147         end
148         return stat
149 end
150
151 --- Get a boolean option and return it's value as true or false.
152 -- @param config        UCI config
153 -- @param section       UCI section name
154 -- @param option        UCI option
155 -- @return                      Boolean
156 function Cursor.get_bool(self, ...)
157         local val = self:get(...)
158         return ( val == "1" or val == "true" or val == "yes" or val == "on" )
159 end
160
161 --- Get an option or list and return values as table.
162 -- @param config        UCI config
163 -- @param section       UCI section name
164 -- @param option        UCI option
165 -- @return                      UCI value
166 function Cursor.get_list(self, config, section, option)
167         if config and section and option then
168                 local val = self:get(config, section, option)
169                 return ( type(val) == "table" and val or { val } )
170         end
171         return nil
172 end
173
174 --- Get the given option from the first section with the given type.
175 -- @param config        UCI config
176 -- @param type          UCI section type
177 -- @param option        UCI option (optional)
178 -- @param default       Default value (optional)
179 -- @return                      UCI value
180 function Cursor.get_first(self, conf, stype, opt, def)
181         local rv = def
182
183         self:foreach(conf, stype,
184                 function(s)
185                         local val = not opt and s['.name'] or s[opt]
186
187                         if type(def) == "number" then
188                                 val = tonumber(val)
189                         elseif type(def) == "boolean" then
190                                 val = (val == "1" or val == "true" or
191                                        val == "yes" or val == "on")
192                         end
193
194                         if val ~= nil then
195                                 rv = val
196                                 return false
197                         end
198                 end)
199
200         return rv
201 end
202
203 --- Set given values as list.
204 -- @param config        UCI config
205 -- @param section       UCI section name
206 -- @param option        UCI option
207 -- @param value         UCI value
208 -- @return                      Boolean whether operation succeeded
209 function Cursor.set_list(self, config, section, option, value)
210         if config and section and option then
211                 return self:set(
212                         config, section, option,
213                         ( type(value) == "table" and value or { value } )
214                 )
215         end
216         return false
217 end
218
219 -- Return a list of initscripts affected by configuration changes.
220 function Cursor._affected(self, configlist)
221         configlist = type(configlist) == "table" and configlist or {configlist}
222
223         local c = cursor()
224         c:load("ucitrack")
225
226         -- Resolve dependencies
227         local reloadlist = {}
228
229         local function _resolve_deps(name)
230                 local reload = {name}
231                 local deps = {}
232
233                 c:foreach("ucitrack", name,
234                         function(section)
235                                 if section.affects then
236                                         for i, aff in ipairs(section.affects) do
237                                                 deps[#deps+1] = aff
238                                         end
239                                 end
240                         end)
241
242                 for i, dep in ipairs(deps) do
243                         for j, add in ipairs(_resolve_deps(dep)) do
244                                 reload[#reload+1] = add
245                         end
246                 end
247
248                 return reload
249         end
250
251         -- Collect initscripts
252         for j, config in ipairs(configlist) do
253                 for i, e in ipairs(_resolve_deps(config)) do
254                         if not util.contains(reloadlist, e) then
255                                 reloadlist[#reloadlist+1] = e
256                         end
257                 end
258         end
259
260         return reloadlist
261 end
262
263 --- Create a sub-state of this cursor. The sub-state is tied to the parent
264 -- curser, means it the parent unloads or loads configs, the sub state will
265 -- do so as well.
266 -- @return                      UCI state cursor tied to the parent cursor
267 function Cursor.substate(self)
268         Cursor._substates = Cursor._substates or { }
269         Cursor._substates[self] = Cursor._substates[self] or cursor_state()
270         return Cursor._substates[self]
271 end
272
273 local _load = Cursor.load
274 function Cursor.load(self, ...)
275         if Cursor._substates and Cursor._substates[self] then
276                 _load(Cursor._substates[self], ...)
277         end
278         return _load(self, ...)
279 end
280
281 local _unload = Cursor.unload
282 function Cursor.unload(self, ...)
283         if Cursor._substates and Cursor._substates[self] then
284                 _unload(Cursor._substates[self], ...)
285         end
286         return _unload(self, ...)
287 end
288
289
290 --- Add an anonymous section.
291 -- @class function
292 -- @name Cursor.add
293 -- @param config        UCI config
294 -- @param type          UCI section type
295 -- @return                      Name of created section
296
297 --- Get a table of saved but uncommitted changes.
298 -- @class function
299 -- @name Cursor.changes
300 -- @param config        UCI config
301 -- @return                      Table of changes
302 -- @see Cursor.save
303
304 --- Commit saved changes.
305 -- @class function
306 -- @name Cursor.commit
307 -- @param config        UCI config
308 -- @return                      Boolean whether operation succeeded
309 -- @see Cursor.revert
310 -- @see Cursor.save
311
312 --- Deletes a section or an option.
313 -- @class function
314 -- @name Cursor.delete
315 -- @param config        UCI config
316 -- @param section       UCI section name
317 -- @param option        UCI option (optional)
318 -- @return                      Boolean whether operation succeeded
319
320 --- Call a function for every section of a certain type.
321 -- @class function
322 -- @name Cursor.foreach
323 -- @param config        UCI config
324 -- @param type          UCI section type
325 -- @param callback      Function to be called
326 -- @return                      Boolean whether operation succeeded
327
328 --- Get a section type or an option
329 -- @class function
330 -- @name Cursor.get
331 -- @param config        UCI config
332 -- @param section       UCI section name
333 -- @param option        UCI option (optional)
334 -- @return                      UCI value
335
336 --- Get all sections of a config or all values of a section.
337 -- @class function
338 -- @name Cursor.get_all
339 -- @param config        UCI config
340 -- @param section       UCI section name (optional)
341 -- @return                      Table of UCI sections or table of UCI values
342
343 --- Manually load a config.
344 -- @class function
345 -- @name Cursor.load
346 -- @param config        UCI config
347 -- @return                      Boolean whether operation succeeded
348 -- @see Cursor.save
349 -- @see Cursor.unload
350
351 --- Revert saved but uncommitted changes.
352 -- @class function
353 -- @name Cursor.revert
354 -- @param config        UCI config
355 -- @return                      Boolean whether operation succeeded
356 -- @see Cursor.commit
357 -- @see Cursor.save
358
359 --- Saves changes made to a config to make them committable.
360 -- @class function
361 -- @name Cursor.save
362 -- @param config        UCI config
363 -- @return                      Boolean whether operation succeeded
364 -- @see Cursor.load
365 -- @see Cursor.unload
366
367 --- Set a value or create a named section.
368 -- @class function
369 -- @name Cursor.set
370 -- @param config        UCI config
371 -- @param section       UCI section name
372 -- @param option        UCI option or UCI section type
373 -- @param value         UCI value or nil if you want to create a section
374 -- @return                      Boolean whether operation succeeded
375
376 --- Get the configuration directory.
377 -- @class function
378 -- @name Cursor.get_confdir
379 -- @return                      Configuration directory
380
381 --- Get the directory for uncomitted changes.
382 -- @class function
383 -- @name Cursor.get_savedir
384 -- @return                      Save directory
385
386 --- Set the configuration directory.
387 -- @class function
388 -- @name Cursor.set_confdir
389 -- @param directory     UCI configuration directory
390 -- @return                      Boolean whether operation succeeded
391
392 --- Set the directory for uncommited changes.
393 -- @class function
394 -- @name Cursor.set_savedir
395 -- @param directory     UCI changes directory
396 -- @return                      Boolean whether operation succeeded
397
398 --- Discard changes made to a config.
399 -- @class function
400 -- @name Cursor.unload
401 -- @param config        UCI config
402 -- @return                      Boolean whether operation succeeded
403 -- @see Cursor.load
404 -- @see Cursor.save