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

@ -43,8 +43,8 @@ Delete all sections of a given type that match certain criteria.
@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)
]] ]]
---[[ ---[[
@ -88,8 +88,8 @@ Get an option or list and return values as table.
@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.
]] ]]
---[[ ---[[
@ -113,15 +113,17 @@ has the same effect as deleting the option.
@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
item UCI list.
@return Boolean whether operation succeeded @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.
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.
curser, means it the parent unloads or loads configs, the sub state will
do so as well.
@class function @class function
@name Cursor.substate @name Cursor.substate
@return UCI state cursor tied to the parent cursor @return UCI state cursor tied to the parent cursor
@ -268,6 +270,14 @@ Get the directory for uncomitted changes.
@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.
@ -286,6 +296,15 @@ Set the directory for uncommited changes.
@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.