diff options
Diffstat (limited to 'modules/luci-base/luasrc/model')
-rw-r--r-- | modules/luci-base/luasrc/model/ipkg.lua | 23 | ||||
-rw-r--r-- | modules/luci-base/luasrc/model/network.lua | 2 | ||||
-rw-r--r-- | modules/luci-base/luasrc/model/uci.lua | 501 | ||||
-rw-r--r-- | modules/luci-base/luasrc/model/uci.luadoc | 299 |
4 files changed, 548 insertions, 277 deletions
diff --git a/modules/luci-base/luasrc/model/ipkg.lua b/modules/luci-base/luasrc/model/ipkg.lua index e653b03465..e27ea52895 100644 --- a/modules/luci-base/luasrc/model/ipkg.lua +++ b/modules/luci-base/luasrc/model/ipkg.lua @@ -20,12 +20,14 @@ module "luci.model.ipkg" -- Internal action function local function _action(cmd, ...) - local pkg = "" + local cmdline = { ipkg, cmd } + + local k, v for k, v in pairs({...}) do - pkg = pkg .. " '" .. v:gsub("'", "") .. "'" + cmdline[#cmdline+1] = util.shellquote(v) end - local c = "%s %s %s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" %{ ipkg, cmd, pkg } + local c = "%s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" % table.concat(cmdline, " ") local r = os.execute(c) local e = fs.readfile("/tmp/opkg.stderr") local o = fs.readfile("/tmp/opkg.stdout") @@ -74,17 +76,17 @@ local function _parselist(rawdata) end -- Internal lookup function -local function _lookup(act, pkg) - local cmd = ipkg .. " " .. act +local function _lookup(cmd, pkg) + local cmdline = { ipkg, cmd } if pkg then - cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'" + cmdline[#cmdline+1] = util.shellquote(pkg) end -- OPKG sometimes kills the whole machine because it sucks -- Therefore we have to use a sucky approach too and use -- tmpfiles instead of directly reading the output local tmpfile = os.tmpname() - os.execute(cmd .. (" >%s 2>/dev/null" % tmpfile)) + os.execute("%s >%s 2>/dev/null" %{ table.concat(cmdline, " "), tmpfile }) local data = _parselist(io.lines(tmpfile)) os.remove(tmpfile) @@ -123,9 +125,12 @@ end -- List helper local function _list(action, pat, cb) - local fd = io.popen(ipkg .. " " .. action .. - (pat and (" '%s'" % pat:gsub("'", "")) or "")) + local cmdline = { ipkg, action } + if pat then + cmdline[#cmdline+1] = util.shellquote(pat) + end + local fd = io.popen(table.concat(cmdline, " ")) if fd then local name, version, sz, desc while true do diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-base/luasrc/model/network.lua index 056fc67b14..dfe818bcc7 100644 --- a/modules/luci-base/luasrc/model/network.lua +++ b/modules/luci-base/luasrc/model/network.lua @@ -629,7 +629,7 @@ function get_interface(self, i) if _interfaces[i] or _wifi_iface(i) then return interface(i) else - local netid = _wifi_netid_by_netname(i) + local netid = _wifi_netid_by_sid(i) return netid and interface(netid) end end diff --git a/modules/luci-base/luasrc/model/uci.lua b/modules/luci-base/luasrc/model/uci.lua index 577c6cde08..fc2a605b34 100644 --- a/modules/luci-base/luasrc/model/uci.lua +++ b/modules/luci-base/luasrc/model/uci.lua @@ -2,13 +2,12 @@ -- Licensed to the public under the Apache License 2.0. local os = require "os" -local uci = require "uci" local util = require "luci.util" local table = require "table" local setmetatable, rawget, rawset = setmetatable, rawget, rawset -local require, getmetatable = require, getmetatable +local require, getmetatable, assert = require, getmetatable, assert local error, pairs, ipairs = error, pairs, ipairs local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack @@ -20,151 +19,436 @@ local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack -- reloaded. module "luci.model.uci" -cursor = uci.cursor +local ERRSTR = { + "Invalid command", + "Invalid argument", + "Method not found", + "Entry not found", + "No data", + "Permission denied", + "Timeout", + "Not supported", + "Unknown error", + "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 -APIVERSION = uci.APIVERSION + +function cursor() + return _M +end function cursor_state() - return cursor(nil, "/var/state") + return _M end +function substate(self) + return self +end -inst = cursor() -inst_state = cursor_state() -local Cursor = getmetatable(inst) +function get_confdir(self) + return "/etc/config" +end -function Cursor.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" - % table.concat(configlist, " ")) - end +function get_savedir(self) + return "/tmp/.uci" end +function get_session_id(self) + return session_id +end --- returns a boolean whether to delete the current section (optional) -function Cursor.delete_all(self, config, stype, comparator) - local del = {} +function set_confdir(self, directory) + return false +end - if type(comparator) == "table" then - local tbl = comparator - comparator = function(section) - for k, v in pairs(tbl) do - if section[k] ~= v then - return false +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 +end + +function save(self, config) + return true +end + +function unload(self, config) + return true +end + + +function changes(self, config) + local rv = call("changes", { config = config }) + local res = {} + + if type(rv) == "table" and type(rv.changes) == "table" then + local package, changes + for package, changes in pairs(rv.changes) do + res[package] = {} + + local _, change + for _, change in ipairs(changes) do + local operation, section, option, value = unpack(change) + if option and value and operation ~= "add" then + res[package][section] = res[package][section] or { } + + if operation == "list-add" then + local v = res[package][section][option] + if type(v) == "table" then + v[#v+1] = value or "" + elseif v ~= nil then + res[package][section][option] = { v, value } + else + res[package][section][option] = { value } + end + else + res[package][section][option] = value or "" + end + else + res[package][section] = res[package][section] or {} + res[package][section][".type"] = option or "" end end - return true end end - local function helper (section) + return res +end + + +function revert(self, config) + local _, err = call("revert", { config = config }) + return (err == nil), ERRSTR[err] +end + +function commit(self, config) + local _, err = call("commit", { config = config }) + return (err == nil), ERRSTR[err] +end + +--[[ +function apply(self, configs, command) + local _, config + + assert(not command, "Apply command not supported anymore") - if not comparator or comparator(section) then - del[#del+1] = section[".name"] + if type(configs) == "table" then + for _, config in ipairs(configs) do + call("service", "event", { + type = "config.change", + data = { package = config } + }) end end +end +]] + + +function foreach(self, config, stype, callback) + if type(callback) == "function" then + local rv, err = call("get", { + config = config, + type = stype + }) + + if type(rv) == "table" and type(rv.values) == "table" then + local sections = { } + local res = false + local index = 1 + + local _, section + for _, section in pairs(rv.values) do + section[".index"] = section[".index"] or index + sections[index] = section + index = index + 1 + end - self:foreach(config, stype, helper) + table.sort(sections, function(a, b) + return a[".index"] < b[".index"] + end) - for i, j in ipairs(del) do - self:delete(config, j) + for _, section in ipairs(sections) do + local continue = callback(section) + res = true + if continue == false then + break + end + end + return res + else + return false, ERRSTR[err] or "No data" + end + else + return false, "Invalid argument" end end -function Cursor.section(self, config, type, name, values) - local stat = true - if name then - stat = self:set(config, name, type) +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 = call(operation, { + config = config, + section = section, + option = option + }) + + if type(rv) == "table" then + return rv.value or nil + elseif err then + return false, ERRSTR[err] + else + return nil + end + elseif option == nil then + local values = self:get_all(config, section) + if values then + return values[".type"], values[".name"] + else + return nil + end else - name = self:add(config, type) - stat = name and true + return false, "Invalid argument" end +end - if stat and values then - stat = self:tset(config, name, values) - end +function get(self, ...) + return _get(self, "get", ...) +end - return stat and name +function get_state(self, ...) + return _get(self, "state", ...) end -function Cursor.tset(self, config, section, values) - local stat = true - for k, v in pairs(values) do - if k:sub(1, 1) ~= "." then - stat = stat and self:set(config, section, k, v) - end +function get_all(self, config, section) + local rv, err = call("get", { + config = config, + section = section + }) + + if type(rv) == "table" and type(rv.values) == "table" then + return rv.values + elseif err then + return false, ERRSTR[err] + else + return nil end - return stat end -function Cursor.get_bool(self, ...) +function get_bool(self, ...) local val = self:get(...) - return ( val == "1" or val == "true" or val == "yes" or val == "on" ) + return (val == "1" or val == "true" or val == "yes" or val == "on") +end + +function get_first(self, config, stype, option, default) + local rv = default + + self:foreach(config, stype, function(s) + local val = not option and s[".name"] or s[option] + + if type(default) == "number" then + val = tonumber(val) + elseif type(default) == "boolean" then + val = (val == "1" or val == "true" or + val == "yes" or val == "on") + end + + if val ~= nil then + rv = val + return false + end + end) + + return rv end -function Cursor.get_list(self, config, section, option) +function get_list(self, config, section, option) if config and section and option then local val = self:get(config, section, option) - return ( type(val) == "table" and val or { val } ) + return (type(val) == "table" and val or { val }) end - return {} + return { } end -function Cursor.get_first(self, conf, stype, opt, def) - local rv = def - self:foreach(conf, stype, - function(s) - local val = not opt and s['.name'] or s[opt] +function section(self, config, stype, name, values) + local rv, err = call("add", { + config = config, + type = stype, + name = name, + values = values + }) + + if type(rv) == "table" then + return rv.section + elseif err then + return false, ERRSTR[err] + else + return nil + end +end - if type(def) == "number" then - val = tonumber(val) - elseif type(def) == "boolean" then - val = (val == "1" or val == "true" or - val == "yes" or val == "on") + +function add(self, config, stype) + return self:section(config, stype) +end + +function set(self, config, section, option, value) + if value == nil then + local sname, err = self:section(config, option, section) + return (not not sname), err + else + local _, err = call("set", { + config = config, + section = section, + values = { [option] = value } + }) + return (err == nil), ERRSTR[err] + end +end + +function set_list(self, config, section, option, value) + if section == nil or option == nil then + return false + elseif value == nil or (type(value) == "table" and #value == 0) then + return self:delete(config, section, option) + elseif type(value) == "table" then + return self:set(config, section, option, value) + else + return self:set(config, section, option, { value }) + end +end + +function tset(self, config, section, values) + local _, err = call("set", { + config = config, + section = section, + values = values + }) + return (err == nil), ERRSTR[err] +end + +function reorder(self, config, section, index) + local sections + + if type(section) == "string" and type(index) == "number" then + local pos = 0 + + sections = { } + + self:foreach(config, nil, function(s) + if pos == index then + pos = pos + 1 end - if val ~= nil then - rv = val - return false + if s[".name"] ~= section then + pos = pos + 1 + sections[pos] = s[".name"] + else + sections[index + 1] = section end end) + elseif type(section) == "table" then + sections = section + else + return false, "Invalid argument" + end - return rv + local _, err = call("order", { + config = config, + sections = sections + }) + + return (err == nil), ERRSTR[err] end -function Cursor.set_list(self, config, section, option, value) - if config and section and option then - if not value or #value == 0 then - return self:delete(config, section, option) + +function delete(self, config, section, option) + local _, err = call("delete", { + config = config, + section = section, + option = option + }) + return (err == nil), ERRSTR[err] +end + +function delete_all(self, config, stype, comparator) + local _, err + if type(comparator) == "table" then + _, err = call("delete", { + config = config, + type = stype, + match = comparator + }) + elseif type(comparator) == "function" then + local rv = call("get", { + config = config, + type = stype + }) + + if type(rv) == "table" and type(rv.values) == "table" then + local sname, section + for sname, section in pairs(rv.values) do + if comparator(section) then + _, err = call("delete", { + config = config, + section = sname + }) + end + end end - return self:set( - config, section, option, - ( type(value) == "table" and value or { value } ) - ) + elseif comparator == nil then + _, err = call("delete", { + config = config, + type = stype + }) + else + return false, "Invalid argument" end - return false + + return (err == nil), ERRSTR[err] end --- Return a list of initscripts affected by configuration changes. -function Cursor._affected(self, configlist) - configlist = type(configlist) == "table" and configlist or {configlist} - local c = cursor() - c:load("ucitrack") +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 reloadlist = { } local function _resolve_deps(name) - local reload = {name} - local deps = {} + local reload = { name } + local deps = { } - c:foreach("ucitrack", name, + self:foreach("ucitrack", name, function(section) if section.affects then for i, aff in ipairs(section.affects) do @@ -173,7 +457,9 @@ function Cursor._affected(self, configlist) 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 @@ -183,7 +469,9 @@ function Cursor._affected(self, configlist) 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 @@ -193,44 +481,3 @@ function Cursor._affected(self, configlist) return reloadlist end - --- curser, means it the parent unloads or loads configs, the sub state will --- do so as well. -function Cursor.substate(self) - Cursor._substates = Cursor._substates or { } - Cursor._substates[self] = Cursor._substates[self] or cursor_state() - return Cursor._substates[self] -end - -local _load = Cursor.load -function Cursor.load(self, ...) - if Cursor._substates and Cursor._substates[self] then - _load(Cursor._substates[self], ...) - end - return _load(self, ...) -end - -local _unload = Cursor.unload -function Cursor.unload(self, ...) - if Cursor._substates and Cursor._substates[self] then - _unload(Cursor._substates[self], ...) - end - return _unload(self, ...) -end - - - - - - - - - - - - - - - - - diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc index 49093c7930..ef89d09b9e 100644 --- a/modules/luci-base/luasrc/model/uci.luadoc +++ b/modules/luci-base/luasrc/model/uci.luadoc @@ -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 ]] |