luci-base: implement session handling in luci.model.uci
authorJo-Philipp Wich <jo@mein.io>
Thu, 19 Apr 2018 09:42:12 +0000 (11:42 +0200)
committerJo-Philipp Wich <jo@mein.io>
Thu, 19 Apr 2018 09:42:12 +0000 (11:42 +0200)
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>
modules/luci-base/luasrc/model/uci.lua
modules/luci-base/luasrc/model/uci.luadoc

index 0e3950c..fc2a605 100644 (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
                })
index 49093c7..ef89d09 100644 (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
 ]]