1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Licensed to the public under the Apache License 2.0.
4 local os = require "os"
5 local uci = require "uci"
6 local util = require "luci.util"
7 local table = require "table"
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
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
21 module "luci.model.uci"
25 APIVERSION = uci.APIVERSION
27 function cursor_state()
28 return cursor(nil, "/var/state")
33 inst_state = cursor_state()
35 local Cursor = getmetatable(inst)
37 function Cursor.apply(self, configlist, command)
38 configlist = self:_affected(configlist)
40 return { "/sbin/luci-reload", unpack(configlist) }
42 return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
43 % table.concat(configlist, " "))
48 -- returns a boolean whether to delete the current section (optional)
49 function Cursor.delete_all(self, config, stype, comparator)
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
64 local function helper (section)
66 if not comparator or comparator(section) then
67 del[#del+1] = section[".name"]
71 self:foreach(config, stype, helper)
73 for i, j in ipairs(del) do
74 self:delete(config, j)
78 function Cursor.section(self, config, type, name, values)
81 stat = self:set(config, name, type)
83 name = self:add(config, type)
87 if stat and values then
88 stat = self:tset(config, name, values)
94 function Cursor.tset(self, config, section, values)
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)
104 function Cursor.get_bool(self, ...)
105 local val = self:get(...)
106 return ( val == "1" or val == "true" or val == "yes" or val == "on" )
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 } )
117 function Cursor.get_first(self, conf, stype, opt, def)
120 self:foreach(conf, stype,
122 local val = not opt and s['.name'] or s[opt]
124 if type(def) == "number" then
126 elseif type(def) == "boolean" then
127 val = (val == "1" or val == "true" or
128 val == "yes" or val == "on")
140 function Cursor.set_list(self, config, section, option, value)
141 if config and section and option then
142 if not value or #value == 0 then
143 return self:delete(config, section, option)
146 config, section, option,
147 ( type(value) == "table" and value or { value } )
153 -- Return a list of initscripts affected by configuration changes.
154 function Cursor._affected(self, configlist)
155 configlist = type(configlist) == "table" and configlist or {configlist}
160 -- Resolve dependencies
161 local reloadlist = {}
163 local function _resolve_deps(name)
164 local reload = {name}
167 c:foreach("ucitrack", name,
169 if section.affects then
170 for i, aff in ipairs(section.affects) do
176 for i, dep in ipairs(deps) do
177 for j, add in ipairs(_resolve_deps(dep)) do
178 reload[#reload+1] = add
185 -- Collect initscripts
186 for j, config in ipairs(configlist) do
187 for i, e in ipairs(_resolve_deps(config)) do
188 if not util.contains(reloadlist, e) then
189 reloadlist[#reloadlist+1] = e
197 -- curser, means it the parent unloads or loads configs, the sub state will
199 function Cursor.substate(self)
200 Cursor._substates = Cursor._substates or { }
201 Cursor._substates[self] = Cursor._substates[self] or cursor_state()
202 return Cursor._substates[self]
205 local _load = Cursor.load
206 function Cursor.load(self, ...)
207 if Cursor._substates and Cursor._substates[self] then
208 _load(Cursor._substates[self], ...)
210 return _load(self, ...)
213 local _unload = Cursor.unload
214 function Cursor.unload(self, ...)
215 if Cursor._substates and Cursor._substates[self] then
216 _unload(Cursor._substates[self], ...)
218 return _unload(self, ...)