mirror of
https://github.com/JamesonHuang/OpenWrt_Luci_Lua.git
synced 2024-11-24 06:10:11 +00:00
405 lines
10 KiB
Lua
405 lines
10 KiB
Lua
|
--[[
|
||
|
LuCI - UCI model
|
||
|
|
||
|
Description:
|
||
|
Generalized UCI model
|
||
|
|
||
|
FileId:
|
||
|
$Id: uci.lua 8127 2011-12-20 19:02:14Z jow $
|
||
|
|
||
|
License:
|
||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
|
||
|
]]--
|
||
|
local os = require "os"
|
||
|
local uci = require "uci"
|
||
|
local util = require "luci.util"
|
||
|
local table = require "table"
|
||
|
|
||
|
|
||
|
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
||
|
local require, getmetatable = require, getmetatable
|
||
|
local error, pairs, ipairs = error, pairs, ipairs
|
||
|
local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
|
||
|
|
||
|
--- LuCI UCI model library.
|
||
|
-- The typical workflow for UCI is: Get a cursor instance from the
|
||
|
-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
|
||
|
-- save the changes to the staging area via Cursor.save and finally
|
||
|
-- Cursor.commit the data to the actual config files.
|
||
|
-- LuCI then needs to Cursor.apply the changes so deamons etc. are
|
||
|
-- reloaded.
|
||
|
-- @cstyle instance
|
||
|
module "luci.model.uci"
|
||
|
|
||
|
--- Create a new UCI-Cursor.
|
||
|
-- @class function
|
||
|
-- @name cursor
|
||
|
-- @return UCI-Cursor
|
||
|
cursor = uci.cursor
|
||
|
|
||
|
APIVERSION = uci.APIVERSION
|
||
|
|
||
|
--- Create a new Cursor initialized to the state directory.
|
||
|
-- @return UCI cursor
|
||
|
function cursor_state()
|
||
|
return cursor(nil, "/var/state")
|
||
|
end
|
||
|
|
||
|
|
||
|
inst = cursor()
|
||
|
inst_state = cursor_state()
|
||
|
|
||
|
local Cursor = getmetatable(inst)
|
||
|
|
||
|
--- Applies UCI configuration changes
|
||
|
-- @param configlist List of UCI configurations
|
||
|
-- @param command Don't apply only return the command
|
||
|
function Cursor.apply(self, configlist, command)
|
||
|
configlist = self:_affected(configlist)
|
||
|
if command then
|
||
|
return { "/sbin/luci-reload", unpack(configlist) }
|
||
|
else
|
||
|
return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
|
||
|
% table.concat(configlist, " "))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
--- Delete all sections of a given type that match certain criteria.
|
||
|
-- @param config UCI config
|
||
|
-- @param type UCI section type
|
||
|
-- @param comparator Function that will be called for each section and
|
||
|
-- returns a boolean whether to delete the current section (optional)
|
||
|
function Cursor.delete_all(self, config, stype, comparator)
|
||
|
local del = {}
|
||
|
|
||
|
if type(comparator) == "table" then
|
||
|
local tbl = comparator
|
||
|
comparator = function(section)
|
||
|
for k, v in pairs(tbl) do
|
||
|
if section[k] ~= v then
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function helper (section)
|
||
|
|
||
|
if not comparator or comparator(section) then
|
||
|
del[#del+1] = section[".name"]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
self:foreach(config, stype, helper)
|
||
|
|
||
|
for i, j in ipairs(del) do
|
||
|
self:delete(config, j)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Create a new section and initialize it with data.
|
||
|
-- @param config UCI config
|
||
|
-- @param type UCI section type
|
||
|
-- @param name UCI section name (optional)
|
||
|
-- @param values Table of key - value pairs to initialize the section with
|
||
|
-- @return Name of created section
|
||
|
function Cursor.section(self, config, type, name, values)
|
||
|
local stat = true
|
||
|
if name then
|
||
|
stat = self:set(config, name, type)
|
||
|
else
|
||
|
name = self:add(config, type)
|
||
|
stat = name and true
|
||
|
end
|
||
|
|
||
|
if stat and values then
|
||
|
stat = self:tset(config, name, values)
|
||
|
end
|
||
|
|
||
|
return stat and name
|
||
|
end
|
||
|
|
||
|
--- Updated the data of a section using data from a table.
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name (optional)
|
||
|
-- @param values Table of key - value pairs to update the section with
|
||
|
function Cursor.tset(self, config, section, values)
|
||
|
local stat = true
|
||
|
for k, v in pairs(values) do
|
||
|
if k:sub(1, 1) ~= "." then
|
||
|
stat = stat and self:set(config, section, k, v)
|
||
|
end
|
||
|
end
|
||
|
return stat
|
||
|
end
|
||
|
|
||
|
--- Get a boolean option and return it's value as true or false.
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name
|
||
|
-- @param option UCI option
|
||
|
-- @return Boolean
|
||
|
function Cursor.get_bool(self, ...)
|
||
|
local val = self:get(...)
|
||
|
return ( val == "1" or val == "true" or val == "yes" or val == "on" )
|
||
|
end
|
||
|
|
||
|
--- Get an option or list and return values as table.
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name
|
||
|
-- @param option UCI option
|
||
|
-- @return UCI value
|
||
|
function Cursor.get_list(self, config, section, option)
|
||
|
if config and section and option then
|
||
|
local val = self:get(config, section, option)
|
||
|
return ( type(val) == "table" and val or { val } )
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
--- Get the given option from the first section with the given type.
|
||
|
-- @param config UCI config
|
||
|
-- @param type UCI section type
|
||
|
-- @param option UCI option (optional)
|
||
|
-- @param default Default value (optional)
|
||
|
-- @return UCI value
|
||
|
function Cursor.get_first(self, conf, stype, opt, def)
|
||
|
local rv = def
|
||
|
|
||
|
self:foreach(conf, stype,
|
||
|
function(s)
|
||
|
local val = not opt and s['.name'] or s[opt]
|
||
|
|
||
|
if type(def) == "number" then
|
||
|
val = tonumber(val)
|
||
|
elseif type(def) == "boolean" then
|
||
|
val = (val == "1" or val == "true" or
|
||
|
val == "yes" or val == "on")
|
||
|
end
|
||
|
|
||
|
if val ~= nil then
|
||
|
rv = val
|
||
|
return false
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
return rv
|
||
|
end
|
||
|
|
||
|
--- Set given values as list.
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name
|
||
|
-- @param option UCI option
|
||
|
-- @param value UCI value
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
function Cursor.set_list(self, config, section, option, value)
|
||
|
if config and section and option then
|
||
|
return self:set(
|
||
|
config, section, option,
|
||
|
( type(value) == "table" and value or { value } )
|
||
|
)
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- Return a list of initscripts affected by configuration changes.
|
||
|
function Cursor._affected(self, configlist)
|
||
|
configlist = type(configlist) == "table" and configlist or {configlist}
|
||
|
|
||
|
local c = cursor()
|
||
|
c:load("ucitrack")
|
||
|
|
||
|
-- Resolve dependencies
|
||
|
local reloadlist = {}
|
||
|
|
||
|
local function _resolve_deps(name)
|
||
|
local reload = {name}
|
||
|
local deps = {}
|
||
|
|
||
|
c:foreach("ucitrack", name,
|
||
|
function(section)
|
||
|
if section.affects then
|
||
|
for i, aff in ipairs(section.affects) do
|
||
|
deps[#deps+1] = aff
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
for i, dep in ipairs(deps) do
|
||
|
for j, add in ipairs(_resolve_deps(dep)) do
|
||
|
reload[#reload+1] = add
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return reload
|
||
|
end
|
||
|
|
||
|
-- Collect initscripts
|
||
|
for j, config in ipairs(configlist) do
|
||
|
for i, e in ipairs(_resolve_deps(config)) do
|
||
|
if not util.contains(reloadlist, e) then
|
||
|
reloadlist[#reloadlist+1] = e
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return reloadlist
|
||
|
end
|
||
|
|
||
|
--- Create a sub-state of this cursor. The sub-state is tied to the parent
|
||
|
-- curser, means it the parent unloads or loads configs, the sub state will
|
||
|
-- do so as well.
|
||
|
-- @return UCI state cursor tied to the parent cursor
|
||
|
function Cursor.substate(self)
|
||
|
Cursor._substates = Cursor._substates or { }
|
||
|
Cursor._substates[self] = Cursor._substates[self] or cursor_state()
|
||
|
return Cursor._substates[self]
|
||
|
end
|
||
|
|
||
|
local _load = Cursor.load
|
||
|
function Cursor.load(self, ...)
|
||
|
if Cursor._substates and Cursor._substates[self] then
|
||
|
_load(Cursor._substates[self], ...)
|
||
|
end
|
||
|
return _load(self, ...)
|
||
|
end
|
||
|
|
||
|
local _unload = Cursor.unload
|
||
|
function Cursor.unload(self, ...)
|
||
|
if Cursor._substates and Cursor._substates[self] then
|
||
|
_unload(Cursor._substates[self], ...)
|
||
|
end
|
||
|
return _unload(self, ...)
|
||
|
end
|
||
|
|
||
|
|
||
|
--- Add an anonymous section.
|
||
|
-- @class function
|
||
|
-- @name Cursor.add
|
||
|
-- @param config UCI config
|
||
|
-- @param type UCI section type
|
||
|
-- @return Name of created section
|
||
|
|
||
|
--- Get a table of saved but uncommitted changes.
|
||
|
-- @class function
|
||
|
-- @name Cursor.changes
|
||
|
-- @param config UCI config
|
||
|
-- @return Table of changes
|
||
|
-- @see Cursor.save
|
||
|
|
||
|
--- Commit saved changes.
|
||
|
-- @class function
|
||
|
-- @name Cursor.commit
|
||
|
-- @param config UCI config
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
-- @see Cursor.revert
|
||
|
-- @see Cursor.save
|
||
|
|
||
|
--- Deletes a section or an option.
|
||
|
-- @class function
|
||
|
-- @name Cursor.delete
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name
|
||
|
-- @param option UCI option (optional)
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
|
||
|
--- Call a function for every section of a certain type.
|
||
|
-- @class function
|
||
|
-- @name Cursor.foreach
|
||
|
-- @param config UCI config
|
||
|
-- @param type UCI section type
|
||
|
-- @param callback Function to be called
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
|
||
|
--- Get a section type or an option
|
||
|
-- @class function
|
||
|
-- @name Cursor.get
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name
|
||
|
-- @param option UCI option (optional)
|
||
|
-- @return UCI value
|
||
|
|
||
|
--- Get all sections of a config or all values of a section.
|
||
|
-- @class function
|
||
|
-- @name Cursor.get_all
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name (optional)
|
||
|
-- @return Table of UCI sections or table of UCI values
|
||
|
|
||
|
--- Manually load a config.
|
||
|
-- @class function
|
||
|
-- @name Cursor.load
|
||
|
-- @param config UCI config
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
-- @see Cursor.save
|
||
|
-- @see Cursor.unload
|
||
|
|
||
|
--- Revert saved but uncommitted changes.
|
||
|
-- @class function
|
||
|
-- @name Cursor.revert
|
||
|
-- @param config UCI config
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
-- @see Cursor.commit
|
||
|
-- @see Cursor.save
|
||
|
|
||
|
--- Saves changes made to a config to make them committable.
|
||
|
-- @class function
|
||
|
-- @name Cursor.save
|
||
|
-- @param config UCI config
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
-- @see Cursor.load
|
||
|
-- @see Cursor.unload
|
||
|
|
||
|
--- Set a value or create a named section.
|
||
|
-- @class function
|
||
|
-- @name Cursor.set
|
||
|
-- @param config UCI config
|
||
|
-- @param section UCI section name
|
||
|
-- @param option UCI option or UCI section type
|
||
|
-- @param value UCI value or nil if you want to create a section
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
|
||
|
--- Get the configuration directory.
|
||
|
-- @class function
|
||
|
-- @name Cursor.get_confdir
|
||
|
-- @return Configuration directory
|
||
|
|
||
|
--- Get the directory for uncomitted changes.
|
||
|
-- @class function
|
||
|
-- @name Cursor.get_savedir
|
||
|
-- @return Save directory
|
||
|
|
||
|
--- Set the configuration directory.
|
||
|
-- @class function
|
||
|
-- @name Cursor.set_confdir
|
||
|
-- @param directory UCI configuration directory
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
|
||
|
--- Set the directory for uncommited changes.
|
||
|
-- @class function
|
||
|
-- @name Cursor.set_savedir
|
||
|
-- @param directory UCI changes directory
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
|
||
|
--- Discard changes made to a config.
|
||
|
-- @class function
|
||
|
-- @name Cursor.unload
|
||
|
-- @param config UCI config
|
||
|
-- @return Boolean whether operation succeeded
|
||
|
-- @see Cursor.load
|
||
|
-- @see Cursor.save
|