Merge pull request #302 from chris5560/master
[project/luci.git] / modules / luci-base / luasrc / model / uci.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 local os = require "os"
5 local uci = require "uci"
6 local util = require "luci.util"
7 local table = require "table"
8
9
10 local setmetatable, rawget, rawset = setmetatable, rawget, rawset
11 local require, getmetatable = require, getmetatable
12 local error, pairs, ipairs = error, pairs, ipairs
13 local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
14
15 -- The typical workflow for UCI is: Get a cursor instance from the
16 -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
17 -- save the changes to the staging area via Cursor.save and finally
18 -- Cursor.commit the data to the actual config files.
19 -- LuCI then needs to Cursor.apply the changes so deamons etc. are
20 -- reloaded.
21 module "luci.model.uci"
22
23 cursor = uci.cursor
24
25 APIVERSION = uci.APIVERSION
26
27 function cursor_state()
28 return cursor(nil, "/var/state")
29 end
30
31
32 inst = cursor()
33 inst_state = cursor_state()
34
35 local Cursor = getmetatable(inst)
36
37 function Cursor.apply(self, configlist, command)
38 configlist = self:_affected(configlist)
39 if command then
40 return { "/sbin/luci-reload", unpack(configlist) }
41 else
42 return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
43 % table.concat(configlist, " "))
44 end
45 end
46
47
48 -- returns a boolean whether to delete the current section (optional)
49 function Cursor.delete_all(self, config, stype, comparator)
50 local del = {}
51
52 if type(comparator) == "table" then
53 local tbl = comparator
54 comparator = function(section)
55 for k, v in pairs(tbl) do
56 if section[k] ~= v then
57 return false
58 end
59 end
60 return true
61 end
62 end
63
64 local function helper (section)
65
66 if not comparator or comparator(section) then
67 del[#del+1] = section[".name"]
68 end
69 end
70
71 self:foreach(config, stype, helper)
72
73 for i, j in ipairs(del) do
74 self:delete(config, j)
75 end
76 end
77
78 function Cursor.section(self, config, type, name, values)
79 local stat = true
80 if name then
81 stat = self:set(config, name, type)
82 else
83 name = self:add(config, type)
84 stat = name and true
85 end
86
87 if stat and values then
88 stat = self:tset(config, name, values)
89 end
90
91 return stat and name
92 end
93
94 function Cursor.tset(self, config, section, values)
95 local stat = true
96 for k, v in pairs(values) do
97 if k:sub(1, 1) ~= "." then
98 stat = stat and self:set(config, section, k, v)
99 end
100 end
101 return stat
102 end
103
104 function Cursor.get_bool(self, ...)
105 local val = self:get(...)
106 return ( val == "1" or val == "true" or val == "yes" or val == "on" )
107 end
108
109 function Cursor.get_list(self, config, section, option)
110 if config and section and option then
111 local val = self:get(config, section, option)
112 return ( type(val) == "table" and val or { val } )
113 end
114 return nil
115 end
116
117 function Cursor.get_first(self, conf, stype, opt, def)
118 local rv = def
119
120 self:foreach(conf, stype,
121 function(s)
122 local val = not opt and s['.name'] or s[opt]
123
124 if type(def) == "number" then
125 val = tonumber(val)
126 elseif type(def) == "boolean" then
127 val = (val == "1" or val == "true" or
128 val == "yes" or val == "on")
129 end
130
131 if val ~= nil then
132 rv = val
133 return false
134 end
135 end)
136
137 return rv
138 end
139
140 function Cursor.set_list(self, config, section, option, value)
141 if config and section and option then
142 return self:set(
143 config, section, option,
144 ( type(value) == "table" and value or { value } )
145 )
146 end
147 return false
148 end
149
150 -- Return a list of initscripts affected by configuration changes.
151 function Cursor._affected(self, configlist)
152 configlist = type(configlist) == "table" and configlist or {configlist}
153
154 local c = cursor()
155 c:load("ucitrack")
156
157 -- Resolve dependencies
158 local reloadlist = {}
159
160 local function _resolve_deps(name)
161 local reload = {name}
162 local deps = {}
163
164 c:foreach("ucitrack", name,
165 function(section)
166 if section.affects then
167 for i, aff in ipairs(section.affects) do
168 deps[#deps+1] = aff
169 end
170 end
171 end)
172
173 for i, dep in ipairs(deps) do
174 for j, add in ipairs(_resolve_deps(dep)) do
175 reload[#reload+1] = add
176 end
177 end
178
179 return reload
180 end
181
182 -- Collect initscripts
183 for j, config in ipairs(configlist) do
184 for i, e in ipairs(_resolve_deps(config)) do
185 if not util.contains(reloadlist, e) then
186 reloadlist[#reloadlist+1] = e
187 end
188 end
189 end
190
191 return reloadlist
192 end
193
194 -- curser, means it the parent unloads or loads configs, the sub state will
195 -- do so as well.
196 function Cursor.substate(self)
197 Cursor._substates = Cursor._substates or { }
198 Cursor._substates[self] = Cursor._substates[self] or cursor_state()
199 return Cursor._substates[self]
200 end
201
202 local _load = Cursor.load
203 function Cursor.load(self, ...)
204 if Cursor._substates and Cursor._substates[self] then
205 _load(Cursor._substates[self], ...)
206 end
207 return _load(self, ...)
208 end
209
210 local _unload = Cursor.unload
211 function Cursor.unload(self, ...)
212 if Cursor._substates and Cursor._substates[self] then
213 _unload(Cursor._substates[self], ...)
214 end
215 return _unload(self, ...)
216 end
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233