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

View file

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