From: Jo-Philipp Wich Date: Tue, 12 Oct 2010 05:28:49 +0000 (+0000) Subject: libs: merge libs/uci into libs/core X-Git-Tag: 0.10.0~624 X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=commitdiff_plain;h=5c2000a1572afd0df74339775dfa8b61e287a53e;hp=2472cd5613cf5dfc5760c084c8094bead7ddb507 libs: merge libs/uci into libs/core --- diff --git a/contrib/package/luci/Makefile b/contrib/package/luci/Makefile index 70c76486a..4099e5148 100644 --- a/contrib/package/luci/Makefile +++ b/contrib/package/luci/Makefile @@ -158,17 +158,6 @@ endif ### Libraries ### -define Package/luci-uci - $(call Package/luci/libtemplate) - DEPENDS+=+libuci-lua - TITLE:=High-Level UCI API -endef - -define Package/luci-uci/install - $(call Package/luci/install/template,$(1),libs/uci) -endef - - define Package/luci-fastindex $(call Package/luci/libtemplate) TITLE:=Fastindex indexing module @@ -315,7 +304,7 @@ endef define Package/luci-web $(call Package/luci/libtemplate) - DEPENDS+=+luci-sys +luci-nixio +luci-uci \ + DEPENDS+=+luci-sys +luci-nixio +luci-core \ +luci-sgi-cgi +luci-lmo TITLE:=MVC Webframework $(call Config,luci.main.lang,string,auto,Default Language) @@ -332,7 +321,7 @@ endef define Package/luci-uvl $(call Package/luci/libtemplate) - DEPENDS+=+luci-sys +luci-uci +luci-core + DEPENDS+=+luci-sys +luci-core TITLE:=UVL - UCI Validation Layer endef @@ -1075,9 +1064,6 @@ endif ifneq ($(CONFIG_PACKAGE_luci-px5g),) PKG_SELECTED_MODULES+=libs/px5g endif -ifneq ($(CONFIG_PACKAGE_luci-uci),) - PKG_SELECTED_MODULES+=libs/uci -endif ifneq ($(CONFIG_PACKAGE_luci-sys),) PKG_SELECTED_MODULES+=libs/sys endif @@ -1292,7 +1278,6 @@ $(eval $(call BuildPackage,luci-luanet)) $(eval $(call BuildPackage,luci-lucid)) $(eval $(call BuildPackage,luci-nixio)) $(eval $(call BuildPackage,luci-px5g)) -$(eval $(call BuildPackage,luci-uci)) $(eval $(call BuildPackage,luci-sys)) $(eval $(call BuildPackage,luci-web)) $(eval $(call BuildPackage,luci-uvl)) diff --git a/libs/core/luasrc/model/network.lua b/libs/core/luasrc/model/network.lua index 8459463f1..53649dde5 100644 --- a/libs/core/luasrc/model/network.lua +++ b/libs/core/luasrc/model/network.lua @@ -483,3 +483,898 @@ function interface.get_network(self) end end +--[==[ +#!/usr/bin/lua + +local uci = require "luci.model.uci".cursor_state() +local utl = require "luci.util" +local sys = require "luci.sys" +local lip = require "luci.ip" +local nxo = require "nixio" +local nfs = require "nixio.fs" + +-- patch uci +local x = getmetatable(uci) + +function x:list(...) + local val = self:get(...) + local lst = { } + + if type(val) == "list" then + local _, v + for _, v in ipairs(val) do + local i + for i in v:gmatch("%S+") do + lst[#lst+1] = i + end + end + elseif type(val) == "string" then + local i + for i in val:gmatch("%S+") do + lst[#lst+1] = i + end + end + + return lst +end + + +system = utl.class() + +system._switches = { } +system._vlans = { } + +function system:__init__() + self._networks = { } + + uci:foreach("network2", "interface", + function(s) + self._networks[#self._networks+1] = system.network(s, self) + end) +end + +function system:networks() + local index = 0 + return function() + if index <= #self._networks then + index = index + 1 + return self._networks[index] + else + return nil + end + end +end + +function system:find_network(name) + local v + for _, v in ipairs(self._networks) do + if v:name() == name then + return v + end + end +end + +function system:find_interface(name) + local v + for _, v in ipairs(self._networks) do + local i + for i in v:interfaces() do + if i:is_bridge() then + local p + for p in i:interfaces() do + if p:name() == name then + return p + end + end + end + + if i:name() == name then + return i + end + end + end +end + +function system:delete_network(name) + local i + for i = 1, #self._networks do + if self._networks[i]:name() == name then + local x + + for x in self._networks[i]:aliases() do + uci:delete("network2", x:name()) + end + + for x in self._networks[i]:routes() do + uci:delete("network2", x:name()) + end + + uci:delete("network2", self._networks[i]) + table.remove(self._networks, i) + + return true + end + end + return false +end + +function system:print() + local v + for v in self:networks() do + print(v:name()) + v:print() + print("--") + end +end + +function system.ignore_iface(ifn) + return (nil ~= ( + ifn:match("^wlan%d") or + ifn:match("^ath%d") or + ifn:match("^wl%d") or + ifn:match("^imq%d") or + ifn:match("^br%-") or + ifn:match("^/dev/") + )) +end + +function system.find_wifi_networks(net) + local lst = { } + local cnt = 0 + + uci:foreach("wireless", "wifi-iface", + function(s) + if s.device and s.network == net then + lst[#lst+1] = { s.device, s['.name'], cnt } + end + cnt = cnt + 1 + end) + + return lst +end + +function system.find_iface_names(net) + local lst = { } + + local val = uci:list("network2", net, "device") + if #val == 0 or val[1]:match("^/dev/") then + val = uci:list("network2", net, "ifname") + end + + local ifn + for _, ifn in ipairs(val) do + if not system.ignore_iface(ifn) then + lst[#lst+1] = ifn + end + end + + return lst +end + +function system.find_switch(name) + local swname, swdev, swvlan + + -- find switch + uci:foreach("network2", "switch", + function(s) + swname = s.name or s['.name'] + + -- special: rtl8366s is eth0 (wan is eth1) + if swname == "rtl8366s" then + swdev = "eth0" + + -- special: rtl8366rb is eth0 (wan + lan) + elseif swname == "rtl8366rb" then + swdev = "eth0" + + -- treat swname as swdev + else + swdev = swname + end + + return false + end) + + -- find first vlan + if swdev then + uci:foreach("network2", "switch_vlan", + function(s) + if s.device == swname then + local vlan = tonumber(s.vlan) + if vlan and (not swvlan or vlan < swvlan) then + swvlan = vlan + end + end + end) + end + + + local veth, vlan = name:match("^(%S+)%.(%d+)$") + + -- have vlan id and matching switch + if vlan and veth == swdev then + return swname, swdev, vlan + + -- have no vlan id but matching switch, assume first switch vlan + elseif not vlan and name == swdev then + return swname, swdev, swvlan + + -- have vlan and no matching switch, assume software vlan + elseif vlan then + return nil, veth, vlan + end +end + + +system.network = utl.class() + +function system.network:__init__(s, sys) + self._name = s['.name'] + self._sys = sys + self._routes = { } + self._aliases = { } + + if s.type == "bridge" then + self._interfaces = { system.network.bridge(s['.name'], self) } + else + self._interfaces = { } + + local ifn + + -- find wired ifaces + for _, ifn in ipairs(system.find_iface_names(self._name)) do + self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self) + end + + -- find wifi networks + for _, ifn in ipairs(system.find_wifi_networks(self._name)) do + self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self) + end + end + + -- find ipv4 routes + uci:foreach("network2", "route", + function(s) + if s.interface == self._name and s.target then + self._routes[#self._routes+1] = system.network.route(s, self) + end + end) + + -- find ipv6 routes + uci:foreach("network2", "route6", + function(s) + if s.interface == self._name and s.target then + self._routes[#self._routes+1] = system.network.route(s, self) + end + end) + + -- find aliases + uci:foreach("network2", "alias", + function(s) + if s.interface == self._name and s.proto then + self._aliases[#self._aliases+1] = system.network.alias(s, self) + end + end) +end + +function system.network:name() + return self._name +end + +function system.network:system() + return self._sys +end + +function system.network:interfaces() + local index = 0 + return function() + if index <= #self._interfaces then + index = index + 1 + return self._interfaces[index] + else + return nil + end + end +end + +function system.network:interface() + return self._interfaces[1] +end + +function system.network:num_routes() + return #self._routes +end + +function system.network:routes() + local index = 0 + return function() + if index <= #self._routes then + index = index + 1 + return self._routes[index] + else + return nil + end + end +end + +function system.network:num_aliases() + return #self._aliases +end + +function system.network:aliases() + local index = 0 + return function() + if index <= #self._aliases then + index = index + 1 + return self._aliases[index] + else + return nil + end + end +end + +function system.network:delete_route(rt) + local i + for i = 1, #self._routes do + if self._routes[i]:name() == rt:name() then + uci:delete("network2", rt:name()) + table.remove(self._routes, i) + return true + end + end + return false +end + +function system.network:delete_alias(al) + local i + for i = 1, #self._aliases do + if self._aliases[i]:name() == al:name() then + uci:delete("network2", al:name()) + table.remove(self._aliases, i) + return true + end + end + return false +end + +function system.network:print() + self:interface():print() +end + + +system.network.iface = utl.class() + +function system.network.iface:__init__(ifn, net, parent) + self._net = net + self._parent = parent + + -- is a wifi iface + if type(ifn) == "table" then + local wifidev, network, index = unpack(ifn) + + self._name = "%s.%d" %{ wifidev, index } + self._wifidev = wifidev + self._wifinet = index + self._ifname = uci:get("wireless", network, "ifname") or self._name + + -- is a wired iface + else + self._name = ifn + self._ifname = ifn + + local switch, swdev, vlan = system.find_switch(self._ifname) + + if switch then + self._switch = system.switch(switch, swdev, self) + end + + if vlan then + self._vlan = system.vlan(vlan, self._switch, self) + end + end +end + +function system.network.iface:name() + return self._name +end + +function system.network.iface:parent() + return self._parent +end + +function system.network.iface:network() + return self._net +end + +function system.network.iface:is_managed() + return (self._net ~= nil) +end + +function system.network.iface:is_vlan() + return (self._vlan ~= nil) +end + +function system.network.iface:is_software_vlan() + return (not self._switch and self._vlan ~= nil) +end + +function system.network.iface:is_hardware_vlan() + return (self._switch ~= nil and self._vlan ~= nil) +end + +function system.network.iface:_sysfs(path, default) + path = "/sys/class/net/%s/%s" %{ self._ifname, path } + + local data = nfs.readfile(path) + + if type(default) == "number" then + return tonumber(data) or default + elseif data and #data > 0 then + return data and data:gsub("%s+$", "") or default + end + + return default +end + +function system.network.iface:rx_bytes() + return self:_sysfs("statistics/rx_bytes", 0) +end + +function system.network.iface:tx_bytes() + return self:_sysfs("statistics/tx_bytes", 0) +end + +function system.network.iface:rx_packets() + return self:_sysfs("statistics/rx_packets", 0) +end + +function system.network.iface:tx_packets() + return self:_sysfs("statistics/tx_packets", 0) +end + +function system.network.iface:macaddr() + return self:_sysfs("address") +end + +function system.network.iface:mtu() + return self:_sysfs("mtu", 1500) +end + +function system.network.iface:is_bridge() + return (self:_sysfs("bridge/max_age", 0) > 0) +end + +function system.network.iface:is_bridge_port() + return (self:_sysfs("brport/port_no", 0) > 0) +end + +function system.network.iface:delete() + if self._wifidev then + local cnt = 0 + uci:foreach("wireless", "wifi-iface", + function(s) + cnt = cnt + 1 + if s.device == self._wifidev and cnt == self._wifinet then + uci:delete("wireless", s['.name']) + return false + end + end) + end +end + +function system.network.iface:print() + if self._wifidev then + print(" wifi: ", self._wifidev, "net: ", self._wifinet) + else + print(" iface: ", self._name) + end + + print(" rx: ", self:rx_bytes(), self:rx_packets()) + print(" tx: ", self:tx_bytes(), self:tx_packets()) + print(" mtu: ", self:mtu()) + print(" mac: ", self:macaddr()) + print(" bridge? ", self:is_bridge()) + print(" port? ", self:is_bridge_port()) + print(" swvlan? ", self:is_software_vlan()) + print(" hwvlan? ", self:is_hardware_vlan()) + + if self._switch then + self._switch:print() + end + + if self._vlan then + self._vlan:print() + end +end + + +system.network.bridge = utl.class(system.network.iface) + +function system.network.bridge:__init__(brn, net) + self._net = net + self._name = "br-" .. brn + self._ifname = self._name + self._interfaces = { } + + local ifn + + -- find wired ifaces + for _, ifn in ipairs(system.find_iface_names(brn)) do + self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self) + end + + -- find wifi networks + for _, ifn in ipairs(system.find_wifi_networks(brn)) do + self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self) + end +end + +function system.network.bridge:interfaces() + local index = 0 + return function() + if index <= #self._interfaces then + index = index + 1 + return self._interfaces[index] + else + return nil + end + end +end + +function system.network.bridge:print() + local v + for v in self:interfaces() do + io.write(" port: ") + v:print() + end + print(" rx: ", self:rx_bytes(), self:rx_packets()) + print(" tx: ", self:tx_bytes(), self:tx_packets()) + print(" mtu: ", self:mtu()) + print(" mac: ", self:macaddr()) + print(" bridge? ", self:is_bridge()) + print(" port? ", self:is_bridge_port()) +end + + +system.network.route = utl.class() + +function system.network.route:__init__(rt, net) + self._net = net + self._name = rt['.name'] + self._ipv6 = (rt['.type'] == "route6") + self._mtu = tonumber(rt.mtu) or (net and net:interface():mtu() or 1500) + self._metric = tonumber(rt.metric) or 0 + + if self._ipv6 then + self._gateway = lip.IPv6(rt.gateway or "::") + self._target = lip.IPv6(rt.target or "::") + else + self._gateway = lip.IPv4(rt.gateway or "0.0.0.0") + self._target = lip.IPv4(rt.target or "0.0.0.0", rt.netmask or "0.0.0.0") + end +end + +function system.network.route:name() + return self._name +end + +function system.network.route:network() + return self._net +end + +function system.network.route:mtu() + return self._mtu +end + +function system.network.route:metric() + return self._metric +end + +function system.network.route:is_ipv4() + return not self._ipv6 +end + +function system.network.route:is_ipv6() + return self._ipv6 +end + +function system.network.route:target() + return self._target +end + +function system.network.route:gateway() + return self._gateway +end + + +system.network.alias = utl.class() + +function system.network.alias:__init__(a, net) + self._net = net + self._name = a['.name'] +end + + +system.switch = utl.class() + +function system.switch:__init__(switch, swdev, net) + self._name = switch + self._ifname = swdev + self._net = net + + if not system._switches[switch] then + local x = io.popen("swconfig dev %q help 2>/dev/null" % switch) + if x then + local desc = x:read("*l") + + if desc then + local name, num_ports, num_cpu, num_vlans = + desc:match("Switch %d: %S+%((.-)%), ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)") + + self._model = name + self._ports = tonumber(num_ports) + self._cpuport = tonumber(num_cpu) + self._vlans = tonumber(num_vlans) + end + + x:close() + + elseif nfs.access("/proc/switch/%s" % switch) then + self._model = self:_proc("driver", switch) + self._ports = self:_proc_count("port", 6) + self._vlans = self:_proc_count("vlan", 16) + end + + -- defaults + self._model = self._model or switch + self._ports = self._ports or 6 + self._vlans = self._vlans or 16 + self._cpuport = self._cpuport or 5 + + system._switches[switch] = self + else + self._model = system._switches[switch]._model + self._ports = system._switches[switch]._ports + self._vlans = system._switches[switch]._vlans + self._cpuport = system._switches[switch]._cpuport + end +end + +function system.switch:_proc(path, default) + local data = nfs.readfile("/proc/switch/%s/%s" %{ self._name, path }) + if data then + return data:gsub("%s+$", "") + end + return default +end + +function system.switch:_proc_count(path, default) + local cnt = 0 + for _ in nfs.dir("/proc/switch/%s/%s" %{ self._name, path }) do + cnt = cnt + 1 + end + return cnt > 0 and cnt or default +end + +function system.switch:name() + return self._name +end + +function system.switch:model() + return self._model +end + +function system.switch:num_possible_vlans() + return self._vlans +end + +function system.switch:num_active_vlans() + local cnt = 0 + uci:foreach("network2", "switch_vlan", + function(s) + if s.device == self._name then cnt = cnt + 1 end + end) + return cnt +end + +function system.switch:vlans() + local index = 0 + local vlans = { } + + uci:foreach("network2", "switch_vlan", + function(s) + if s.device == self._name and tonumber(s.vlan) then + vlans[#vlans+1] = tonumber(s.vlan) + end + end) + + return function() + if index <= #vlans then + index = index + 1 + return system.vlan(vlans[index], self) + else + return nil + end + end +end + +function system.switch:num_ports() + return self._ports +end + +function system.switch:delete_vlan(vlan) + local rv = false + + uci:foreach("network2", "switch_vlan", + function(s) + if s.device == self._name and tonumber(s.vlan) == vlan then + rv = true + uci:delete("network2", s['.name']) + + if system._vlans[s.device] and system._vlans[s.device][vlan] then + table.remove(system._vlans[s.device], vlan) + end + + return false + end + end) + + return rv +end + +function system.switch:print() + print("Switch:", self._model) + print(" Ports:", self._ports, "Cpu:", self._cpuport) + print(" Vlans:", self._vlans) +end + + +system.vlan = utl.class() + +function system.vlan:__init__(vlan, switch, iface) + self._vlan = vlan + self._switch = switch + self._iface = iface + + local swid = (switch and switch:name()) or (iface and iface:name()) or "" + + if not system._vlans[swid] or not system._vlans[swid][vlan] then + self._ports = { } + + if switch then + uci:foreach("network2", "switch_vlan", + function(s) + if s.device == switch:name() and tonumber(s.vlan) == vlan then + local p + for _, p in ipairs(uci:list("network2", s['.name'], "ports")) do + self._ports[#self._ports+1] = system.vlan.port(p, self) + end + self._name = s['.name'] + end + end) + else + self._ports[#self._ports+1] = system.vlan.port("0t", self) + end + + system._vlans[swid] = system._vlans[swid] or { } + system._vlans[swid][vlan] = self + else + self._ports = system._vlans[swid][vlan]._ports + end +end + +function system.vlan:name() + return self._name +end + +function system.vlan:number() + return self._vlan +end + +function system.vlan:switch() + return self._switch +end + +function system.vlan:interface() + return self._iface +end + +function system.vlan:is_software() + return (self._switch == nil) +end + +function system.vlan:is_hardware() + return not self:is_software() +end + +function system.vlan:num_ports() + return #self._ports +end + +function system.vlan:ports() + local index = 0 + return function() + if index <= #self._ports then + index = index + 1 + return self._ports[index] + else + return nil + end + end +end + +function system.vlan:_update() + local i + local ports = { } + + for i = 1, #self._ports do + ports[#ports+1] = self._ports[i]:string() + end + + uci:set("network2", self._name, "ports", table.concat(ports, " ")) +end + +function system.vlan:delete_port(port) + if self._switch then + local i + for i = 1, #self._ports do + if self._ports[i]:number() == port then + table.remove(self._ports, i) + self:_update() + return true + end + end + end + return false +end + +function system.vlan:print() + print(" Vlan:", self._vlan, "Software?", self:is_software()) + local p + for p in self:ports() do + p:print() + end +end + + +system.vlan.port = utl.class() + +function system.vlan.port:__init__(port, vlan) + local num, tag = port:match("^(%d+)([tu]?)") + + self._vlan = vlan + self._port = tonumber(num) + self._tagged = (tag == "t") +end + +function system.vlan.port:number() + return self._port +end + +function system.vlan.port:vlan() + return self._vlan +end + +function system.vlan.port:string() + return "%i%s" %{ self._port, self._tagged ? "t" : "" } +end + +function system.vlan.port:is_tagged() + return self._tagged +end + +function system.vlan.port:print() + print(" Port:", self._port, "Tagged:", self._tagged) +end + + +-- ------------------------------ + +local s = system() + +s:print() + +s:find_network("wan"):print() +s:find_interface("eth0"):parent():print() + +]==] diff --git a/libs/core/luasrc/model/uci.lua b/libs/core/luasrc/model/uci.lua new file mode 100644 index 000000000..66bd0a026 --- /dev/null +++ b/libs/core/luasrc/model/uci.lua @@ -0,0 +1,345 @@ +--[[ +LuCI - UCI model + +Description: +Generalized UCI model + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- +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 error, pairs, ipairs, tostring = error, pairs, ipairs, tostring +local require, getmetatable, type = require, getmetatable, type + +--- LuCI UCI model library. +-- The typical workflow for UCI is: Get a cursor instance from the +-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), +-- save the changes to the staging area via Cursor.save and finally +-- Cursor.commit the data to the actual config files. +-- LuCI then needs to Cursor.apply the changes so deamons etc. are +-- reloaded. +-- @cstyle instance +module "luci.model.uci" + +--- Create a new UCI-Cursor. +-- @class function +-- @name cursor +-- @return UCI-Cursor +cursor = uci.cursor + +APIVERSION = uci.APIVERSION + +--- Create a new Cursor initialized to the state directory. +-- @return UCI cursor +function cursor_state() + return cursor(nil, "/var/state") +end + + +inst = cursor() +inst_state = cursor_state() + +local Cursor = getmetatable(inst) + +--- Applies UCI configuration changes +-- @param configlist List of UCI configurations +-- @param command Don't apply only return the command +function Cursor.apply(self, configlist, command) + configlist = self:_affected(configlist) + local reloadcmd = "/sbin/luci-reload " .. table.concat(configlist, " ") + + return command and reloadcmd or os.execute(reloadcmd .. " >/dev/null 2>&1") +end + + +--- Delete all sections of a given type that match certain criteria. +-- @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) +function Cursor.delete_all(self, config, stype, comparator) + local del = {} + + 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 + end + end + return true + end + end + + local function helper (section) + + if not comparator or comparator(section) then + del[#del+1] = section[".name"] + end + end + + self:foreach(config, stype, helper) + + for i, j in ipairs(del) do + self:delete(config, j) + end +end + +--- Create a new section and initialize it with data. +-- @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 +function Cursor.section(self, config, type, name, values) + local stat = true + if name then + stat = self:set(config, name, type) + else + name = self:add(config, type) + stat = name and true + end + + if stat and values then + stat = self:tset(config, name, values) + end + + return stat and name +end + +--- Updated the data of a section using data from a table. +-- @param config UCI config +-- @param section UCI section name (optional) +-- @param values Table of key - value pairs to update the section with +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 + end + return stat +end + +--- Get a boolean option and return it's value as true or false. +-- @param config UCI config +-- @param section UCI section name +-- @param option UCI option +-- @return Boolean +function Cursor.get_bool(self, ...) + local val = self:get(...) + return ( val == "1" or val == "true" or val == "yes" or val == "on" ) +end + +--- Get an option or list and return values as table. +-- @param config UCI config +-- @param section UCI section name +-- @param option UCI option +-- @return UCI value +function Cursor.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 } ) + end + return nil +end + +--- Set given values as list. +-- @param config UCI config +-- @param section UCI section name +-- @param option UCI option +-- @param value UCI value +-- @return Boolean whether operation succeeded +function Cursor.set_list(self, config, section, option, value) + if config and section and option then + return self:set( + config, section, option, + ( type(value) == "table" and value or { value } ) + ) + end + return false +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") + + -- Resolve dependencies + local reloadlist = {} + + local function _resolve_deps(name) + local reload = {name} + local deps = {} + + c:foreach("ucitrack", name, + function(section) + if section.affects then + for i, aff in ipairs(section.affects) do + deps[#deps+1] = aff + end + end + end) + + for i, dep in ipairs(deps) do + for j, add in ipairs(_resolve_deps(dep)) do + reload[#reload+1] = add + end + end + + return reload + end + + -- Collect initscripts + for j, config in ipairs(configlist) do + 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 + + +--- Add an anonymous 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 + +--- Commit saved changes. +-- @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 + +--- 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 + +--- 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 + +--- 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 + +--- Manually load a config. +-- @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 + +--- 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 + +--- Set a value or create a named section. +-- @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 nil if you want to create a section +-- @return Boolean whether operation succeeded + +--- Get the 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 + +--- Set the configuration directory. +-- @class function +-- @name Cursor.set_confdir +-- @param directory UCI configuration directory +-- @return Boolean whether operation succeeded + +--- Set the directory for uncommited changes. +-- @class function +-- @name Cursor.set_savedir +-- @param directory UCI changes directory +-- @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 diff --git a/libs/core/luasrc/model/uci/bind.lua b/libs/core/luasrc/model/uci/bind.lua new file mode 100644 index 000000000..9472dabeb --- /dev/null +++ b/libs/core/luasrc/model/uci/bind.lua @@ -0,0 +1,173 @@ +--[[ +LuCI - UCI utilities for model classes + +Copyright 2009 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- + +local assert, pairs, type = assert, pairs, type +local utl = require "luci.util" + +module "luci.model.uci.bind" + +bind = utl.class() + +function bind.__init__(self, config, cursor) + assert(config, "call to bind() without config file") + self.cfg = config + self.uci = cursor +end + +function bind.init(self, cursor) + assert(cursor, "call to init() without uci cursor") + self.uci = cursor +end + +function bind.section(self, stype) + local x = utl.class(bsection) + x.__init__ = function(inst, sid) + assert(self.uci:get(self.cfg, sid) == stype, + "attempt to instantiate bsection(%q) of wrong type, expected %q" + % { sid, stype }) + + inst.bind = self + inst.stype = stype + inst.sid = sid + + if inst._init then + inst:_init(sid) + end + end + return x +end + +function bind.usection(self, stype) + local x = utl.class(bsection) + x.__init__ = function(inst) + inst.bind = self + inst.stype = stype + inst.sid = true + end + return x() +end + +function bind.list(self, list, add, rem) + local lookup = { } + + if type(list) == "string" then + local item + for item in list:gmatch("%S+") do + lookup[item] = true + end + + elseif type(list) == "table" then + local item + for _, item in pairs(list) do + lookup[item] = true + end + end + + if add then lookup[add] = true end + if rem then lookup[rem] = nil end + + return utl.keys(lookup) +end + +function bind.bool(self, v) + return ( v == "1" or v == "true" or v == "yes" or v == "on" ) +end + + +bsection = utl.class() + +function bsection.uciop(self, op, ...) + assert(self.bind and self.bind.uci, + "attempt to use unitialized binding") + + if op then + return self.bind.uci[op](self.bind.uci, self.bind.cfg, ...) + else + return self.bind.uci + end +end + +function bsection.get(self, k, c) + local v + if type(c) == "string" then + v = self:uciop("get", c, k) + else + self:uciop("foreach", self.stype, + function(s) + if type(c) == "table" then + local ck, cv + for ck, cv in pairs(c) do + if s[ck] ~= cv then return true end + end + end + if k ~= nil then + v = s[k] + else + v = s + end + return false + end) + end + return v +end + +function bsection.set(self, k, v, c) + local stat + if type(c) == "string" then + if type(v) == "table" and #v == 0 then + stat = self:uciop("delete", c, k) + else + stat = self:uciop("set", c, k, v) + end + else + self:uciop("foreach", self.stype, + function(s) + if type(c) == "table" then + local ck, cv + for ck, cv in pairs(c) do + if s[ck] ~= cv then return true end + end + end + stat = self:uciop("set", c, k, v) + return false + end) + end + return stat or false +end + +function bsection.property(self, k, n) + self[n or k] = function(c, val) + if val == nil then + return c:get(k, c.sid) + else + return c:set(k, val, c.sid) + end + end +end + +function bsection.property_bool(self, k, n) + self[n or k] = function(c, val) + if val == nil then + return bind:bool(c:get(k, c.sid)) + else + return c:set(k, bind:bool(val) and "1" or "0", c.sid) + end + end +end + diff --git a/libs/core/root/etc/config/ucitrack b/libs/core/root/etc/config/ucitrack new file mode 100644 index 000000000..7b47cc8d5 --- /dev/null +++ b/libs/core/root/etc/config/ucitrack @@ -0,0 +1,48 @@ +config network + option init network + list affects dhcp + +config wireless + list affects network + +config firewall + option init firewall + list affects luci-splash + list affects qos + +config olsr + option init olsrd + +config dhcp + option init dnsmasq + +config dropbear + option init dropbear + +config httpd + option init httpd + +config fstab + option init fstab + +config qos + option init qos + +config system + option init led + list affects luci_statistics + +config luci_splash + option init luci_splash + +config upnpd + option init miniupnpd + +config ntpclient + option init ntpclient + +config samba + option init samba + +config tinyproxy + option init tinyproxy diff --git a/libs/core/root/sbin/luci-reload b/libs/core/root/sbin/luci-reload new file mode 100755 index 000000000..24cf76064 --- /dev/null +++ b/libs/core/root/sbin/luci-reload @@ -0,0 +1,40 @@ +#!/bin/sh +. /etc/functions.sh + +apply_config() { + config_get init "$1" init + config_get exec "$1" exec + config_get test "$1" test + + echo "$2" > "/var/run/luci-reload-status" + + [ -n "$init" ] && reload_init "$2" "$init" "$test" + [ -n "$exec" ] && reload_exec "$2" "$exec" "$test" +} + +reload_exec() { + [ -x $2 ] && { + echo "Reloading $1... " + $2 >/dev/null 2>&1 + [ -n "$3" -a "$?" != "$3" ] && echo '!!! Failed to reload' $1 '!!!' + } +} + +reload_init() { + [ -x /etc/init.d/$2 ] && /etc/init.d/$2 enabled && { + echo "Reloading $1... " + /etc/init.d/$2 reload >/dev/null 2>&1 + [ -n "$3" -a "$?" != "$3" ] && echo '!!! Failed to reload' $1 '!!!' + } +} + +lock "/var/run/luci-reload" + +config_load ucitrack + +for i in $*; do + config_foreach apply_config $i $i +done + +rm -f "/var/run/luci-reload-status" +lock -u "/var/run/luci-reload" diff --git a/libs/uci/luasrc/model/uci.lua b/libs/uci/luasrc/model/uci.lua deleted file mode 100644 index 66bd0a026..000000000 --- a/libs/uci/luasrc/model/uci.lua +++ /dev/null @@ -1,345 +0,0 @@ ---[[ -LuCI - UCI model - -Description: -Generalized UCI model - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- -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 error, pairs, ipairs, tostring = error, pairs, ipairs, tostring -local require, getmetatable, type = require, getmetatable, type - ---- LuCI UCI model library. --- The typical workflow for UCI is: Get a cursor instance from the --- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), --- save the changes to the staging area via Cursor.save and finally --- Cursor.commit the data to the actual config files. --- LuCI then needs to Cursor.apply the changes so deamons etc. are --- reloaded. --- @cstyle instance -module "luci.model.uci" - ---- Create a new UCI-Cursor. --- @class function --- @name cursor --- @return UCI-Cursor -cursor = uci.cursor - -APIVERSION = uci.APIVERSION - ---- Create a new Cursor initialized to the state directory. --- @return UCI cursor -function cursor_state() - return cursor(nil, "/var/state") -end - - -inst = cursor() -inst_state = cursor_state() - -local Cursor = getmetatable(inst) - ---- Applies UCI configuration changes --- @param configlist List of UCI configurations --- @param command Don't apply only return the command -function Cursor.apply(self, configlist, command) - configlist = self:_affected(configlist) - local reloadcmd = "/sbin/luci-reload " .. table.concat(configlist, " ") - - return command and reloadcmd or os.execute(reloadcmd .. " >/dev/null 2>&1") -end - - ---- Delete all sections of a given type that match certain criteria. --- @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) -function Cursor.delete_all(self, config, stype, comparator) - local del = {} - - 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 - end - end - return true - end - end - - local function helper (section) - - if not comparator or comparator(section) then - del[#del+1] = section[".name"] - end - end - - self:foreach(config, stype, helper) - - for i, j in ipairs(del) do - self:delete(config, j) - end -end - ---- Create a new section and initialize it with data. --- @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 -function Cursor.section(self, config, type, name, values) - local stat = true - if name then - stat = self:set(config, name, type) - else - name = self:add(config, type) - stat = name and true - end - - if stat and values then - stat = self:tset(config, name, values) - end - - return stat and name -end - ---- Updated the data of a section using data from a table. --- @param config UCI config --- @param section UCI section name (optional) --- @param values Table of key - value pairs to update the section with -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 - end - return stat -end - ---- Get a boolean option and return it's value as true or false. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @return Boolean -function Cursor.get_bool(self, ...) - local val = self:get(...) - return ( val == "1" or val == "true" or val == "yes" or val == "on" ) -end - ---- Get an option or list and return values as table. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @return UCI value -function Cursor.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 } ) - end - return nil -end - ---- Set given values as list. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @param value UCI value --- @return Boolean whether operation succeeded -function Cursor.set_list(self, config, section, option, value) - if config and section and option then - return self:set( - config, section, option, - ( type(value) == "table" and value or { value } ) - ) - end - return false -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") - - -- Resolve dependencies - local reloadlist = {} - - local function _resolve_deps(name) - local reload = {name} - local deps = {} - - c:foreach("ucitrack", name, - function(section) - if section.affects then - for i, aff in ipairs(section.affects) do - deps[#deps+1] = aff - end - end - end) - - for i, dep in ipairs(deps) do - for j, add in ipairs(_resolve_deps(dep)) do - reload[#reload+1] = add - end - end - - return reload - end - - -- Collect initscripts - for j, config in ipairs(configlist) do - 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 - - ---- Add an anonymous 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 - ---- Commit saved changes. --- @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 - ---- 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 - ---- 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 - ---- 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 - ---- Manually load a config. --- @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 - ---- 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 - ---- Set a value or create a named section. --- @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 nil if you want to create a section --- @return Boolean whether operation succeeded - ---- Get the 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 - ---- Set the configuration directory. --- @class function --- @name Cursor.set_confdir --- @param directory UCI configuration directory --- @return Boolean whether operation succeeded - ---- Set the directory for uncommited changes. --- @class function --- @name Cursor.set_savedir --- @param directory UCI changes directory --- @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 diff --git a/libs/uci/luasrc/model/uci/bind.lua b/libs/uci/luasrc/model/uci/bind.lua deleted file mode 100644 index 9472dabeb..000000000 --- a/libs/uci/luasrc/model/uci/bind.lua +++ /dev/null @@ -1,173 +0,0 @@ ---[[ -LuCI - UCI utilities for model classes - -Copyright 2009 Jo-Philipp Wich - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- - -local assert, pairs, type = assert, pairs, type -local utl = require "luci.util" - -module "luci.model.uci.bind" - -bind = utl.class() - -function bind.__init__(self, config, cursor) - assert(config, "call to bind() without config file") - self.cfg = config - self.uci = cursor -end - -function bind.init(self, cursor) - assert(cursor, "call to init() without uci cursor") - self.uci = cursor -end - -function bind.section(self, stype) - local x = utl.class(bsection) - x.__init__ = function(inst, sid) - assert(self.uci:get(self.cfg, sid) == stype, - "attempt to instantiate bsection(%q) of wrong type, expected %q" - % { sid, stype }) - - inst.bind = self - inst.stype = stype - inst.sid = sid - - if inst._init then - inst:_init(sid) - end - end - return x -end - -function bind.usection(self, stype) - local x = utl.class(bsection) - x.__init__ = function(inst) - inst.bind = self - inst.stype = stype - inst.sid = true - end - return x() -end - -function bind.list(self, list, add, rem) - local lookup = { } - - if type(list) == "string" then - local item - for item in list:gmatch("%S+") do - lookup[item] = true - end - - elseif type(list) == "table" then - local item - for _, item in pairs(list) do - lookup[item] = true - end - end - - if add then lookup[add] = true end - if rem then lookup[rem] = nil end - - return utl.keys(lookup) -end - -function bind.bool(self, v) - return ( v == "1" or v == "true" or v == "yes" or v == "on" ) -end - - -bsection = utl.class() - -function bsection.uciop(self, op, ...) - assert(self.bind and self.bind.uci, - "attempt to use unitialized binding") - - if op then - return self.bind.uci[op](self.bind.uci, self.bind.cfg, ...) - else - return self.bind.uci - end -end - -function bsection.get(self, k, c) - local v - if type(c) == "string" then - v = self:uciop("get", c, k) - else - self:uciop("foreach", self.stype, - function(s) - if type(c) == "table" then - local ck, cv - for ck, cv in pairs(c) do - if s[ck] ~= cv then return true end - end - end - if k ~= nil then - v = s[k] - else - v = s - end - return false - end) - end - return v -end - -function bsection.set(self, k, v, c) - local stat - if type(c) == "string" then - if type(v) == "table" and #v == 0 then - stat = self:uciop("delete", c, k) - else - stat = self:uciop("set", c, k, v) - end - else - self:uciop("foreach", self.stype, - function(s) - if type(c) == "table" then - local ck, cv - for ck, cv in pairs(c) do - if s[ck] ~= cv then return true end - end - end - stat = self:uciop("set", c, k, v) - return false - end) - end - return stat or false -end - -function bsection.property(self, k, n) - self[n or k] = function(c, val) - if val == nil then - return c:get(k, c.sid) - else - return c:set(k, val, c.sid) - end - end -end - -function bsection.property_bool(self, k, n) - self[n or k] = function(c, val) - if val == nil then - return bind:bool(c:get(k, c.sid)) - else - return c:set(k, bind:bool(val) and "1" or "0", c.sid) - end - end -end - diff --git a/libs/uci/root/etc/config/ucitrack b/libs/uci/root/etc/config/ucitrack deleted file mode 100644 index 7b47cc8d5..000000000 --- a/libs/uci/root/etc/config/ucitrack +++ /dev/null @@ -1,48 +0,0 @@ -config network - option init network - list affects dhcp - -config wireless - list affects network - -config firewall - option init firewall - list affects luci-splash - list affects qos - -config olsr - option init olsrd - -config dhcp - option init dnsmasq - -config dropbear - option init dropbear - -config httpd - option init httpd - -config fstab - option init fstab - -config qos - option init qos - -config system - option init led - list affects luci_statistics - -config luci_splash - option init luci_splash - -config upnpd - option init miniupnpd - -config ntpclient - option init ntpclient - -config samba - option init samba - -config tinyproxy - option init tinyproxy diff --git a/libs/uci/root/sbin/luci-reload b/libs/uci/root/sbin/luci-reload deleted file mode 100755 index 24cf76064..000000000 --- a/libs/uci/root/sbin/luci-reload +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh -. /etc/functions.sh - -apply_config() { - config_get init "$1" init - config_get exec "$1" exec - config_get test "$1" test - - echo "$2" > "/var/run/luci-reload-status" - - [ -n "$init" ] && reload_init "$2" "$init" "$test" - [ -n "$exec" ] && reload_exec "$2" "$exec" "$test" -} - -reload_exec() { - [ -x $2 ] && { - echo "Reloading $1... " - $2 >/dev/null 2>&1 - [ -n "$3" -a "$?" != "$3" ] && echo '!!! Failed to reload' $1 '!!!' - } -} - -reload_init() { - [ -x /etc/init.d/$2 ] && /etc/init.d/$2 enabled && { - echo "Reloading $1... " - /etc/init.d/$2 reload >/dev/null 2>&1 - [ -n "$3" -a "$?" != "$3" ] && echo '!!! Failed to reload' $1 '!!!' - } -} - -lock "/var/run/luci-reload" - -config_load ucitrack - -for i in $*; do - config_foreach apply_config $i $i -done - -rm -f "/var/run/luci-reload-status" -lock -u "/var/run/luci-reload"