libs: merge libs/uci into libs/core
[project/luci.git] / libs / core / 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 error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
34 local require, getmetatable, type = require, getmetatable, type
35
36 --- LuCI UCI model library.
37 -- The typical workflow for UCI is: Get a cursor instance from the
38 -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
39 -- save the changes to the staging area via Cursor.save and finally
40 -- Cursor.commit the data to the actual config files.
41 -- LuCI then needs to Cursor.apply the changes so deamons etc. are
42 -- reloaded.
43 -- @cstyle instance
44 module "luci.model.uci"
45
46 --- Create a new UCI-Cursor.
47 -- @class function
48 -- @name cursor
49 -- @return UCI-Cursor
50 cursor = uci.cursor
51
52 APIVERSION = uci.APIVERSION
53
54 --- Create a new Cursor initialized to the state directory.
55 -- @return UCI cursor
56 function cursor_state()
57 return cursor(nil, "/var/state")
58 end
59
60
61 inst = cursor()
62 inst_state = cursor_state()
63
64 local Cursor = getmetatable(inst)
65
66 --- Applies UCI configuration changes
67 -- @param configlist List of UCI configurations
68 -- @param command Don't apply only return the command
69 function Cursor.apply(self, configlist, command)
70 configlist = self:_affected(configlist)
71 local reloadcmd = "/sbin/luci-reload " .. table.concat(configlist, " ")
72
73 return command and reloadcmd or os.execute(reloadcmd .. " >/dev/null 2>&1")
74 end
75
76
77 --- Delete all sections of a given type that match certain criteria.
78 -- @param config UCI config
79 -- @param type UCI section type
80 -- @param comparator Function that will be called for each section and
81 -- returns a boolean whether to delete the current section (optional)
82 function Cursor.delete_all(self, config, stype, comparator)
83 local del = {}
84
85 if type(comparator) == "table" then
86 local tbl = comparator
87 comparator = function(section)
88 for k, v in pairs(tbl) do
89 if section[k] ~= v then
90 return false
91 end
92 end
93 return true
94 end
95 end
96
97 local function helper (section)
98
99 if not comparator or comparator(section) then
100 del[#del+1] = section[".name"]
101 end
102 end
103
104 self:foreach(config, stype, helper)
105
106 for i, j in ipairs(del) do
107 self:delete(config, j)
108 end
109 end
110
111 --- Create a new section and initialize it with data.
112 -- @param config UCI config
113 -- @param type UCI section type
114 -- @param name UCI section name (optional)
115 -- @param values Table of key - value pairs to initialize the section with
116 -- @return Name of created section
117 function Cursor.section(self, config, type, name, values)
118 local stat = true
119 if name then
120 stat = self:set(config, name, type)
121 else
122 name = self:add(config, type)
123 stat = name and true
124 end
125
126 if stat and values then
127 stat = self:tset(config, name, values)
128 end
129
130 return stat and name
131 end
132
133 --- Updated the data of a section using data from a table.
134 -- @param config UCI config
135 -- @param section UCI section name (optional)
136 -- @param values Table of key - value pairs to update the section with
137 function Cursor.tset(self, config, section, values)
138 local stat = true
139 for k, v in pairs(values) do
140 if k:sub(1, 1) ~= "." then
141 stat = stat and self:set(config, section, k, v)
142 end
143 end
144 return stat
145 end
146
147 --- Get a boolean option and return it's value as true or false.
148 -- @param config UCI config
149 -- @param section UCI section name
150 -- @param option UCI option
151 -- @return Boolean
152 function Cursor.get_bool(self, ...)
153 local val = self:get(...)
154 return ( val == "1" or val == "true" or val == "yes" or val == "on" )
155 end
156
157 --- Get an option or list and return values as table.
158 -- @param config UCI config
159 -- @param section UCI section name
160 -- @param option UCI option
161 -- @return UCI value
162 function Cursor.get_list(self, config, section, option)
163 if config and section and option then
164 local val = self:get(config, section, option)
165 return ( type(val) == "table" and val or { val } )
166 end
167 return nil
168 end
169
170 --- Set given values as list.
171 -- @param config UCI config
172 -- @param section UCI section name
173 -- @param option UCI option
174 -- @param value UCI value
175 -- @return Boolean whether operation succeeded
176 function Cursor.set_list(self, config, section, option, value)
177 if config and section and option then
178 return self:set(
179 config, section, option,
180 ( type(value) == "table" and value or { value } )
181 )
182 end
183 return false
184 end
185
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}
189
190 local c = cursor()
191 c:load("ucitrack")
192
193 -- Resolve dependencies
194 local reloadlist = {}
195
196 local function _resolve_deps(name)
197 local reload = {name}
198 local deps = {}
199
200 c:foreach("ucitrack", name,
201 function(section)
202 if section.affects then
203 for i, aff in ipairs(section.affects) do
204 deps[#deps+1] = aff
205 end
206 end
207 end)
208
209 for i, dep in ipairs(deps) do
210 for j, add in ipairs(_resolve_deps(dep)) do
211 reload[#reload+1] = add
212 end
213 end
214
215 return reload
216 end
217
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 reloadlist[#reloadlist+1] = e
223 end
224 end
225 end
226
227 return reloadlist
228 end
229
230
231 --- Add an anonymous section.
232 -- @class function
233 -- @name Cursor.add
234 -- @param config UCI config
235 -- @param type UCI section type
236 -- @return Name of created section
237
238 --- Get a table of saved but uncommitted changes.
239 -- @class function
240 -- @name Cursor.changes
241 -- @param config UCI config
242 -- @return Table of changes
243 -- @see Cursor.save
244
245 --- Commit saved changes.
246 -- @class function
247 -- @name Cursor.commit
248 -- @param config UCI config
249 -- @return Boolean whether operation succeeded
250 -- @see Cursor.revert
251 -- @see Cursor.save
252
253 --- Deletes a section or an option.
254 -- @class function
255 -- @name Cursor.delete
256 -- @param config UCI config
257 -- @param section UCI section name
258 -- @param option UCI option (optional)
259 -- @return Boolean whether operation succeeded
260
261 --- Call a function for every section of a certain type.
262 -- @class function
263 -- @name Cursor.foreach
264 -- @param config UCI config
265 -- @param type UCI section type
266 -- @param callback Function to be called
267 -- @return Boolean whether operation succeeded
268
269 --- Get a section type or an option
270 -- @class function
271 -- @name Cursor.get
272 -- @param config UCI config
273 -- @param section UCI section name
274 -- @param option UCI option (optional)
275 -- @return UCI value
276
277 --- Get all sections of a config or all values of a section.
278 -- @class function
279 -- @name Cursor.get_all
280 -- @param config UCI config
281 -- @param section UCI section name (optional)
282 -- @return Table of UCI sections or table of UCI values
283
284 --- Manually load a config.
285 -- @class function
286 -- @name Cursor.load
287 -- @param config UCI config
288 -- @return Boolean whether operation succeeded
289 -- @see Cursor.save
290 -- @see Cursor.unload
291
292 --- Revert saved but uncommitted changes.
293 -- @class function
294 -- @name Cursor.revert
295 -- @param config UCI config
296 -- @return Boolean whether operation succeeded
297 -- @see Cursor.commit
298 -- @see Cursor.save
299
300 --- Saves changes made to a config to make them committable.
301 -- @class function
302 -- @name Cursor.save
303 -- @param config UCI config
304 -- @return Boolean whether operation succeeded
305 -- @see Cursor.load
306 -- @see Cursor.unload
307
308 --- Set a value or create a named section.
309 -- @class function
310 -- @name Cursor.set
311 -- @param config UCI config
312 -- @param section UCI section name
313 -- @param option UCI option or UCI section type
314 -- @param value UCI value or nil if you want to create a section
315 -- @return Boolean whether operation succeeded
316
317 --- Get the configuration directory.
318 -- @class function
319 -- @name Cursor.get_confdir
320 -- @return Configuration directory
321
322 --- Get the directory for uncomitted changes.
323 -- @class function
324 -- @name Cursor.get_savedir
325 -- @return Save directory
326
327 --- Set the configuration directory.
328 -- @class function
329 -- @name Cursor.set_confdir
330 -- @param directory UCI configuration directory
331 -- @return Boolean whether operation succeeded
332
333 --- Set the directory for uncommited changes.
334 -- @class function
335 -- @name Cursor.set_savedir
336 -- @param directory UCI changes directory
337 -- @return Boolean whether operation succeeded
338
339 --- Discard changes made to a config.
340 -- @class function
341 -- @name Cursor.unload
342 -- @param config UCI config
343 -- @return Boolean whether operation succeeded
344 -- @see Cursor.load
345 -- @see Cursor.save