luci-base: implement session handling in luci.model.uci

Introduce luci.model.uci.set_session_id() and luci.model.uci.get_session_id()
to set and get the effective session ID respectively.

When a session ID is set, it is sent as `ubus_rpc_session` attribute to rpcd,
causing it to use per-session change directories, isolating LuCI changes from
the global system uci state.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2018-04-19 11:42:12 +02:00
parent fc8f825e2f
commit 9b22c9c1e1
2 changed files with 193 additions and 156 deletions

View file

@ -32,6 +32,15 @@ local ERRSTR = {
"Connection failed"
}
local session_id = nil
local function call(cmd, args)
if type(args) == "table" and session_id then
args.ubus_rpc_session = session_id
end
return util.ubus("uci", cmd, args)
end
function cursor()
return _M
@ -54,6 +63,10 @@ function get_savedir(self)
return "/tmp/.uci"
end
function get_session_id(self)
return session_id
end
function set_confdir(self, directory)
return false
end
@ -62,6 +75,11 @@ function set_savedir(self, directory)
return false
end
function set_session_id(self, id)
session_id = id
return true
end
function load(self, config)
return true
@ -77,7 +95,7 @@ end
function changes(self, config)
local rv = util.ubus("uci", "changes", { config = config })
local rv = call("changes", { config = config })
local res = {}
if type(rv) == "table" and type(rv.changes) == "table" then
@ -116,12 +134,12 @@ end
function revert(self, config)
local _, err = util.ubus("uci", "revert", { config = config })
local _, err = call("revert", { config = config })
return (err == nil), ERRSTR[err]
end
function commit(self, config)
local _, err = util.ubus("uci", "commit", { config = config })
local _, err = call("commit", { config = config })
return (err == nil), ERRSTR[err]
end
@ -133,7 +151,7 @@ function apply(self, configs, command)
if type(configs) == "table" then
for _, config in ipairs(configs) do
util.ubus("service", "event", {
call("service", "event", {
type = "config.change",
data = { package = config }
})
@ -145,7 +163,7 @@ end
function foreach(self, config, stype, callback)
if type(callback) == "function" then
local rv, err = util.ubus("uci", "get", {
local rv, err = call("get", {
config = config,
type = stype
})
@ -186,7 +204,7 @@ local function _get(self, operation, config, section, option)
if section == nil then
return nil
elseif type(option) == "string" and option:byte(1) ~= 46 then
local rv, err = util.ubus("uci", operation, {
local rv, err = call(operation, {
config = config,
section = section,
option = option
@ -220,7 +238,7 @@ function get_state(self, ...)
end
function get_all(self, config, section)
local rv, err = util.ubus("uci", "get", {
local rv, err = call("get", {
config = config,
section = section
})
@ -271,7 +289,7 @@ end
function section(self, config, stype, name, values)
local rv, err = util.ubus("uci", "add", {
local rv, err = call("add", {
config = config,
type = stype,
name = name,
@ -297,7 +315,7 @@ function set(self, config, section, option, value)
local sname, err = self:section(config, option, section)
return (not not sname), err
else
local _, err = util.ubus("uci", "set", {
local _, err = call("set", {
config = config,
section = section,
values = { [option] = value }
@ -319,7 +337,7 @@ function set_list(self, config, section, option, value)
end
function tset(self, config, section, values)
local _, err = util.ubus("uci", "set", {
local _, err = call("set", {
config = config,
section = section,
values = values
@ -353,7 +371,7 @@ function reorder(self, config, section, index)
return false, "Invalid argument"
end
local _, err = util.ubus("uci", "order", {
local _, err = call("order", {
config = config,
sections = sections
})
@ -363,7 +381,7 @@ end
function delete(self, config, section, option)
local _, err = util.ubus("uci", "delete", {
local _, err = call("delete", {
config = config,
section = section,
option = option
@ -374,13 +392,13 @@ end
function delete_all(self, config, stype, comparator)
local _, err
if type(comparator) == "table" then
_, err = util.ubus("uci", "delete", {
_, err = call("delete", {
config = config,
type = stype,
match = comparator
})
elseif type(comparator) == "function" then
local rv = util.ubus("uci", "get", {
local rv = call("get", {
config = config,
type = stype
})
@ -389,7 +407,7 @@ function delete_all(self, config, stype, comparator)
local sname, section
for sname, section in pairs(rv.values) do
if comparator(section) then
_, err = util.ubus("uci", "delete", {
_, err = call("delete", {
config = config,
section = sname
})
@ -397,7 +415,7 @@ function delete_all(self, config, stype, comparator)
end
end
elseif comparator == nil then
_, err = util.ubus("uci", "delete", {
_, err = call("delete", {
config = config,
type = stype
})

View file

@ -14,224 +14,226 @@ module "luci.model.uci"
---[[
Create a new UCI-Cursor.
@class function
@name cursor
@return UCI-Cursor
@class function
@name cursor
@return UCI-Cursor
]]
---[[
Create a new Cursor initialized to the state directory.
@class function
@name cursor_state
@return UCI cursor
@class function
@name cursor_state
@return UCI cursor
]]
---[[
Applies UCI configuration changes
@class function
@name Cursor.apply
@param configlist List of UCI configurations
@param command Don't apply only return the command
@class function
@name Cursor.apply
@param configlist List of UCI configurations
@param command Don't apply only return the command
]]
---[[
Delete all sections of a given type that match certain criteria.
@class function
@name Cursor.delete_all
@class function
@name Cursor.delete_all
@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)
@param comparator Function that will be called for each section and returns
a boolean whether to delete the current section (optional)
]]
---[[
Create a new section and initialize it with data.
@class function
@name Cursor.section
@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
@class function
@name Cursor.section
@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
]]
---[[
Updated the data of a section using data from a table.
@class function
@name Cursor.tset
@param config UCI config
@param section UCI section name (optional)
@param values Table of key - value pairs to update the section with
@class function
@name Cursor.tset
@param config UCI config
@param section UCI section name (optional)
@param values Table of key - value pairs to update the section with
]]
---[[
Get a boolean option and return it's value as true or false.
@class function
@name Cursor.get_bool
@param config UCI config
@param section UCI section name
@param option UCI option
@return Boolean
@class function
@name Cursor.get_bool
@param config UCI config
@param section UCI section name
@param option UCI option
@return Boolean
]]
---[[
Get an option or list and return values as table.
@class function
@name Cursor.get_list
@param config UCI config
@param section UCI section name
@param option UCI option
@return table. If the option was not found, you will simply get
-- an empty table.
@class function
@name Cursor.get_list
@param config UCI config
@param section UCI section name
@param option UCI option
@return table. If the option was not found, you will simply get an empty
table.
]]
---[[
Get the given option from the first section with the given type.
@class function
@name Cursor.get_first
@param config UCI config
@param type UCI section type
@param option UCI option (optional)
@param default Default value (optional)
@return UCI value
@class function
@name Cursor.get_first
@param config UCI config
@param type UCI section type
@param option UCI option (optional)
@param default Default value (optional)
@return UCI value
]]
---[[
Set given values as list. Setting a list option to an empty list
has the same effect as deleting the option.
@class function
@name Cursor.set_list
@param config UCI config
@param section UCI section name
@param option UCI option
@param value value or table. Raw values will become a single item table.
@return Boolean whether operation succeeded
@class function
@name Cursor.set_list
@param config UCI config
@param section UCI section name
@param option UCI option
@param value Value or table. Non-table values will be set as single
item UCI list.
@return Boolean whether operation succeeded
]]
---[[
Create a sub-state of this cursor. The sub-state is tied to the parent
Create a sub-state of this cursor.
curser, means it the parent unloads or loads configs, the sub state will
do so as well.
@class function
@name Cursor.substate
@return UCI state cursor tied to the parent 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.
@class function
@name Cursor.substate
@return UCI state cursor tied to the parent cursor
]]
---[[
Add an anonymous section.
@class function
@name Cursor.add
@param config UCI config
@param type UCI section type
@return Name of created 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
@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
@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
@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
@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
@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
@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
@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
@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
@class function
@name Cursor.save
@param config UCI config
@return Boolean whether operation succeeded
@see Cursor.load
@see Cursor.unload
]]
---[[
@ -243,57 +245,74 @@ then a named section of the given type is created.
When invoked with four arguments `config`, `sectionname`, `optionname` and
`optionvalue` then the value of the specified option is set to the given value.
@class function
@name Cursor.set
@param config UCI config
@param section UCI section name
@param option UCI option or UCI section type
@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 nothing if you want to create a section
@return Boolean whether operation succeeded
@return Boolean whether operation succeeded
]]
---[[
Get the configuration directory.
@class function
@name Cursor.get_confdir
@return 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
@class function
@name Cursor.get_savedir
@return Save directory
]]
---[[
Get the effective session ID.
@class function
@name Cursor.get_session_id
@return String containing the session ID
]]
---[[
Set the configuration directory.
@class function
@name Cursor.set_confdir
@class function
@name Cursor.set_confdir
@param directory UCI configuration directory
@return Boolean whether operation succeeded
@return Boolean whether operation succeeded
]]
---[[
Set the directory for uncommited changes.
@class function
@name Cursor.set_savedir
@class function
@name Cursor.set_savedir
@param directory UCI changes directory
@return Boolean whether operation succeeded
@return Boolean whether operation succeeded
]]
---[[
Set the effective session ID.
@class function
@name Cursor.set_session_id
@param id String containing the session ID to set
@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
@class function
@name Cursor.unload
@param config UCI config
@return Boolean whether operation succeeded
@see Cursor.load
@see Cursor.save
]]