luci-base: break circular luci.config <> luci.model.uci dependency
[project/luci.git] / modules / luci-base / luasrc / model / uci.lua
index bbd9b4c..461ba9d 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,36 +134,99 @@ 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
 
---[[
-function apply(self, configs, command)
-       local _, config
+function apply(self, rollback)
+       local _, err
+
+       if rollback then
+               local conf = require "luci.config"
+               local timeout = tonumber(conf and conf.apply and conf.apply.rollback or "") or 0
 
-       assert(not command, "Apply command not supported anymore")
+               _, err = call("apply", {
+                       timeout  = (timeout > 30) and timeout or 30,
+                       rollback = true
+               })
 
-       if type(configs) == "table" then
-               for _, config in ipairs(configs) do
-                       util.ubus("service", "event", {
-                               type = "config.change",
-                               data = { package = config }
+               if not err then
+                       util.ubus("session", "set", {
+                               ubus_rpc_session = session_id,
+                               values = { rollback = os.time() + timeout }
                        })
                end
+       else
+               _, err = call("changes", {})
+
+               if not err then
+                       if type(_) == "table" and type(_.changes) == "table" then
+                               local k, v
+                               for k, v in pairs(_.changes) do
+                                       _, err = call("commit", { config = k })
+                                       if err then
+                                               break
+                                       end
+                               end
+                       end
+               end
+
+               if not err then
+                       _, err = call("apply", { rollback = false })
+               end
+       end
+
+       return (err == nil), ERRSTR[err]
+end
+
+function confirm(self)
+       local _, err = call("confirm", {})
+       if not err then
+               util.ubus("session", "set", {
+                       ubus_rpc_session = session_id,
+                       values = { rollback = 0 }
+               })
+       end
+       return (err == nil), ERRSTR[err]
+end
+
+function rollback(self)
+       local _, err = call("rollback", {})
+       if not err then
+               util.ubus("session", "set", {
+                       ubus_rpc_session = session_id,
+                       values = { rollback = 0 }
+               })
+       end
+       return (err == nil), ERRSTR[err]
+end
+
+function rollback_pending(self)
+       local deadline, err = util.ubus("session", "get", {
+               ubus_rpc_session = session_id,
+               keys = { "rollback" }
+       })
+
+       if type(deadline) == "table" and
+          type(deadline.values) == "table" and
+          type(deadline.values.rollback) == "number" and
+          deadline.values.rollback > os.time()
+       then
+               return true, deadline.values.rollback - os.time()
        end
+
+       return false, ERRSTR[err]
 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
                })
@@ -182,11 +263,11 @@ function foreach(self, config, stype, callback)
        end
 end
 
-function get(self, config, section, option)
+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", "get", {
+               local rv, err = call(operation, {
                        config  = config,
                        section = section,
                        option  = option
@@ -211,8 +292,16 @@ function get(self, config, section, option)
        end
 end
 
+function get(self, ...)
+       return _get(self, "get", ...)
+end
+
+function get_state(self, ...)
+       return _get(self, "state", ...)
+end
+
 function get_all(self, config, section)
-       local rv, err = util.ubus("uci", "get", {
+       local rv, err = call("get", {
                config  = config,
                section = section
        })
@@ -234,7 +323,7 @@ end
 function get_first(self, config, stype, option, default)
        local rv = default
 
-       self:foreach(conf, stype, function(s)
+       self:foreach(config, stype, function(s)
                local val = not option and s[".name"] or s[option]
 
                if type(default) == "number" then
@@ -263,7 +352,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,
@@ -289,7 +378,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 }
@@ -311,7 +400,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
@@ -345,7 +434,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
        })
@@ -355,7 +444,7 @@ end
 
 
 function delete(self, config, section, option)
-       local _, err = util.ubus("uci", "delete", {
+       local _, err = call("delete", {
                config  = config,
                section = section,
                option  = option
@@ -366,13 +455,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
                })
@@ -381,7 +470,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
                                        })
@@ -389,7 +478,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
                })
@@ -399,59 +488,3 @@ function delete_all(self, config, stype, comparator)
 
        return (err == nil), ERRSTR[err]
 end
-
-
-function 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"
-                       % util.shellquote(table.concat(configlist, " ")))
-       end
-end
-
--- Return a list of initscripts affected by configuration changes.
-function _affected(self, configlist)
-       configlist = type(configlist) == "table" and configlist or { configlist }
-
-       -- Resolve dependencies
-       local reloadlist = { }
-
-       local function _resolve_deps(name)
-               local reload = { name }
-               local deps = { }
-
-               self:foreach("ucitrack", name,
-                       function(section)
-                               if section.affects then
-                                       for i, aff in ipairs(section.affects) do
-                                               deps[#deps+1] = aff
-                                       end
-                               end
-                       end)
-
-               local i, dep
-               for i, dep in ipairs(deps) do
-                       local j, add
-                       for j, add in ipairs(_resolve_deps(dep)) do
-                               reload[#reload+1] = add
-                       end
-               end
-
-               return reload
-       end
-
-       -- Collect initscripts
-       local j, config
-       for j, config in ipairs(configlist) do
-               local i, e
-               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