From 6a1fee5115cdf047a29812dcf81b03edeefccb15 Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Tue, 6 May 2008 14:28:51 +0000 Subject: [PATCH] * Rewrote and optimized ffluci.model.uci * Use optimized UCI code for ffluci.cbi and ffluci.controller.admin.index if possible --- .../package/ffluci-splash/src/luci-splash.lua | 22 +-- core/src/ffluci/cbi.lua | 40 ++--- core/src/ffluci/model/uci.lua | 4 +- core/src/ffluci/model/uci/libuci.lua | 137 +++++++++------- core/src/ffluci/model/uci/wrapper.lua | 48 +++--- .../admin-core/src/controller/admin/index.lua | 150 ++++++++++-------- .../admin-core/src/controller/admin/uci.lua | 5 +- 7 files changed, 233 insertions(+), 173 deletions(-) diff --git a/contrib/package/ffluci-splash/src/luci-splash.lua b/contrib/package/ffluci-splash/src/luci-splash.lua index 1520491e0..2fa6bdd4a 100644 --- a/contrib/package/ffluci-splash/src/luci-splash.lua +++ b/contrib/package/ffluci-splash/src/luci-splash.lua @@ -72,7 +72,7 @@ end function remove_lease(mac) mac = mac:lower() - for k, v in pairs(uci:show("luci_splash").luci_splash) do + for k, v in pairs(uci:sections("luci_splash")) do if v[".type"] == "lease" and v.mac:lower() == mac then remove_rule(mac) uci:del("luci_splash", k) @@ -97,7 +97,7 @@ end function haslease(mac) mac = mac:lower() - for k, v in pairs(uci:show("luci_splash").luci_splash) do + for k, v in pairs(uci:sections("luci_splash")) do if v[".type"] == "lease" and v.mac and v.mac:lower() == mac then return true end @@ -111,7 +111,7 @@ end function iswhitelisted(mac) mac = mac:lower() - for k, v in pairs(uci:show("luci_splash").luci_splash) do + for k, v in pairs(uci:sections("luci_splash")) do if v[".type"] == "whitelist" and v.mac and v.mac:lower() == mac then return true end @@ -134,14 +134,16 @@ function sync() local written = {} local time = os.time() + uci:t_load("luci_splash") + -- Current leases in state files - local leases = uci:show("luci_splash").luci_splash + local leases = uci:t_sections("luci_splash") -- Convert leasetime to seconds - local leasetime = tonumber(uci:get("luci_splash", "general", "leasetime")) * 3600 + local leasetime = tonumber(uci:t_get("luci_splash", "general", "leasetime")) * 3600 -- Clean state file - uci:revert("luci_splash") + uci:t_revert("luci_splash") -- For all leases @@ -152,9 +154,9 @@ function sync() remove_rule(v.mac) else -- Rewrite state - local n = uci:add("luci_splash", "lease") - uci:set("luci_splash", n, "mac", v.mac) - uci:set("luci_splash", n, "start", v.start) + local n = uci:t_add("luci_splash", "lease") + uci:t_set("luci_splash", n, "mac", v.mac) + uci:t_set("luci_splash", n, "start", v.start) written[v.mac:lower()] = 1 end end @@ -167,6 +169,8 @@ function sync() remove_rule(r) end end + + uci:t_save("luci_splash") end main(arg) \ No newline at end of file diff --git a/core/src/ffluci/cbi.lua b/core/src/ffluci/cbi.lua index 99d742b99..4d6d25c39 100644 --- a/core/src/ffluci/cbi.lua +++ b/core/src/ffluci/cbi.lua @@ -119,12 +119,19 @@ function Map.__init__(self, config, ...) self.config = config self.template = "cbi/map" self.uci = ffluci.model.uci.Session() - self.ucidata = self.uci:sections(self.config) - if not self.ucidata then + self.ucidata, self.uciorder = self.uci:sections(self.config) + if not self.ucidata or not self.uciorder then error("Unable to read UCI data: " .. self.config) end end +-- Use optimized UCI writing +function Map.parse(self, ...) + self.uci:t_load(self.config) + Node.parse(self, ...) + self.uci:t_save(self.config) +end + -- Creates a child section function Map.section(self, class, ...) if instanceof(class, AbstractSection) then @@ -138,21 +145,20 @@ end -- UCI add function Map.add(self, sectiontype) - local name = self.uci:add(self.config, sectiontype) + local name = self.uci:t_add(self.config, sectiontype) if name then self.ucidata[name] = {} self.ucidata[name][".type"] = sectiontype - self.ucidata[".order"] = self.ucidata[".order"] or {} - table.insert(self.ucidata[".order"], name) + table.insert(self.uciorder, name) end return name end -- UCI set function Map.set(self, section, option, value) - local stat = self.uci:set(self.config, section, option, value) + local stat = self.uci:t_set(self.config, section, option, value) if stat then - local val = self.uci:get(self.config, section, option) + local val = self.uci:t_get(self.config, section, option) if option then self.ucidata[section][option] = val else @@ -160,8 +166,7 @@ function Map.set(self, section, option, value) self.ucidata[section] = {} end self.ucidata[section][".type"] = val - self.ucidata[".order"] = self.ucidata[".order"] or {} - table.insert(self.ucidata[".order"], section) + table.insert(self.uciorder, section) end end return stat @@ -169,15 +174,15 @@ end -- UCI del function Map.del(self, section, option) - local stat = self.uci:del(self.config, section, option) + local stat = self.uci:t_del(self.config, section, option) if stat then if option then self.ucidata[section][option] = nil else self.ucidata[section] = nil - for i, k in ipairs(self.ucidata[".order"]) do + for i, k in ipairs(self.uciorder) do if section == k then - table.remove(self.ucidata[".order"], i) + table.remove(self.uciorder, i) end end end @@ -188,7 +193,7 @@ end -- UCI get (cached) function Map.get(self, section, option) if not section then - return self.ucidata + return self.ucidata, self.uciorder elseif option and self.ucidata[section] then return self.ucidata[section][option] else @@ -362,19 +367,16 @@ end -- Return all matching UCI sections for this TypedSection function TypedSection.cfgsections(self) local sections = {} + local map, order = self.map:get() - local map = self.map:get() - if not map[".order"] then - return sections - end - - for i, k in pairs(map[".order"]) do + for i, k in ipairs(order) do if map[k][".type"] == self.sectiontype then if self:checkscope(k) then table.insert(sections, k) end end end + return sections end diff --git a/core/src/ffluci/model/uci.lua b/core/src/ffluci/model/uci.lua index 511c97433..ca5b232eb 100644 --- a/core/src/ffluci/model/uci.lua +++ b/core/src/ffluci/model/uci.lua @@ -25,6 +25,9 @@ limitations under the License. ]]-- module("ffluci.model.uci", package.seeall) +-- Default savedir +savedir = "/tmp/.uci" + -- Test whether to load libuci-Wrapper or /sbin/uci-Wrapper if pcall(require, "uci") then Session = require("ffluci.model.uci.libuci").Session @@ -53,7 +56,6 @@ function changes(...) return default:changes(...) end - -- Wrapper for "uci commit" function commit(...) return default:commit(...) diff --git a/core/src/ffluci/model/uci/libuci.lua b/core/src/ffluci/model/uci/libuci.lua index 8414fadc4..b160dc10a 100644 --- a/core/src/ffluci/model/uci/libuci.lua +++ b/core/src/ffluci/model/uci/libuci.lua @@ -26,24 +26,17 @@ limitations under the License. module("ffluci.model.uci.libuci", package.seeall) +require("uci") require("ffluci.util") -require("ffluci.fs") require("ffluci.sys") --- The OS uci command -ucicmd = "uci" - -- Session class Session = ffluci.util.class() -- Session constructor -function Session.__init__(self, path, uci) - uci = uci or ucicmd - if path then - self.ucicmd = uci .. " -P " .. path - else - self.ucicmd = uci - end +function Session.__init__(self, savedir) + self.ucicmd = savedir and "uci -P " .. savedir or "uci" + self.savedir = savedir or ffluci.model.uci.savedir end function Session.add(self, config, section_type) @@ -55,7 +48,8 @@ function Session.changes(self, config) end function Session.commit(self, config) - return self:_uci2("commit " .. _path(config)) + self:t_load(config) + return self:t_commit(config) end function Session.del(self, config, section, option) @@ -63,31 +57,96 @@ function Session.del(self, config, section, option) end function Session.get(self, config, section, option) - return self:_uci("get " .. _path(config, section, option)) + self:t_load(config) + return self:t_get(config, section, option) end function Session.revert(self, config) - return self:_uci2("revert " .. _path(config)) + self:t_load(config) + return self:t_revert(config) end -function Session.sections(self, config) - if not config then - return nil - end - - local r1, r2 = self:_uci3("show " .. _path(config)) - if type(r1) == "table" then - return r1[config] +function Session.sections(self, config) + self:t_load(config) + return self:t_sections(config) +end + +function Session.set(self, config, section, option, value) + self:t_load(config) + return self:t_set(config, section, option, value) and self:t_save(config) +end + +function Session.synchronize(self) + return uci.set_savedir(self.savedir) +end + + +-- UCI-Transactions + +function Session.t_load(self, config) + return self:synchronize() and uci.load(config) +end + +function Session.t_save(self, config) + return uci.save(config) +end + +function Session.t_add(self, config, type) + self:t_save(config) + local r = self:add(config, type) + self:t_load(config) + return r +end + +function Session.t_commit(self, config) + return uci.commit(config) +end + +function Session.t_del(self, config, section, option) + self:t_save(config) + local r = self:del(config, section, option) + self:t_load(config) + return r +end + +function Session.t_get(self, config, section, option) + if option then + return uci.get(config, section, option) else - return nil, r2 + return uci.get(config, section) end end -function Session.set(self, config, section, option, value) - return self:_uci2("set " .. _path(config, section, option, value)) +function Session.t_revert(self, config) + return uci.revert(config) end +function Session.t_sections(self, config) + local raw = uci.get_all(config) + if not raw then + return nil + end + + local s = {} + local o = {} + + for i, sec in ipairs(raw) do + table.insert(o, sec.name) + + s[sec.name] = sec.options + s[sec.name][".type"] = sec.type + end + + return s, o +end +function Session.t_set(self, config, section, option, value) + if option then + return uci.set(config.."."..section.."."..option.."="..value) + else + return uci.set(config.."."..section.."="..value) + end +end -- Internal functions -- @@ -112,34 +171,6 @@ function Session._uci2(self, cmd) end end -function Session._uci3(self, cmd) - local res = ffluci.sys.execl(self.ucicmd .. " 2>&1 " .. cmd) - if res[1] and res[1]:sub(1, self.ucicmd:len()+1) == self.ucicmd..":" then - return nil, res[1] - end - - tbl = {} - - for k,line in pairs(res) do - c, s, t = line:match("^([^.]-)%.([^.]-)=(.-)$") - if c then - tbl[c] = tbl[c] or {} - tbl[c][".order"] = tbl[c][".order"] or {} - - tbl[c][s] = {} - table.insert(tbl[c][".order"], s) - tbl[c][s][".type"] = t - end - - c, s, o, v = line:match("^([^.]-)%.([^.]-)%.([^.]-)=(.-)$") - if c then - tbl[c][s][o] = v - end - end - - return tbl -end - -- Build path (config.section.option=value) and prevent command injection function _path(...) local result = "" diff --git a/core/src/ffluci/model/uci/wrapper.lua b/core/src/ffluci/model/uci/wrapper.lua index 142e71958..3aa3b5fd1 100644 --- a/core/src/ffluci/model/uci/wrapper.lua +++ b/core/src/ffluci/model/uci/wrapper.lua @@ -30,23 +30,14 @@ limitations under the License. module("ffluci.model.uci.wrapper", package.seeall) require("ffluci.util") -require("ffluci.fs") require("ffluci.sys") --- The OS uci command -ucicmd = "uci" - -- Session class Session = ffluci.util.class() -- Session constructor -function Session.__init__(self, path, uci) - uci = uci or ucicmd - if path then - self.ucicmd = uci .. " -P " .. path - else - self.ucicmd = uci - end +function Session.__init__(self, savedir) + self.ucicmd = savedir and "uci -P " .. savedir or "uci" end function Session.add(self, config, section_type) @@ -80,7 +71,7 @@ function Session.sections(self, config) local r1, r2 = self:_uci3("show " .. _path(config)) if type(r1) == "table" then - return r1[config] + return r1, r2 else return nil, r2 end @@ -90,6 +81,23 @@ function Session.set(self, config, section, option, value) return self:_uci2("set " .. _path(config, section, option, value)) end +function Session.synchronize(self) end + +-- Dummy transaction functions + +function Session.t_load(self) end +function Session.t_save(self) end + +Session.t_add = Session.add +Session.t_commit = Session.commit +Session.t_del = Session.del +Session.t_get = Session.get +Session.t_revert = Session.revert +Session.t_sections = Session.sections +Session.t_set = Session.set + + + -- Internal functions -- @@ -121,26 +129,24 @@ function Session._uci3(self, cmd) return nil, res[1] end - tbl = {} + local tbl = {} + local ord = {} for k,line in pairs(res) do c, s, t = line:match("^([^.]-)%.([^.]-)=(.-)$") if c then - tbl[c] = tbl[c] or {} - tbl[c][".order"] = tbl[c][".order"] or {} - - tbl[c][s] = {} - table.insert(tbl[c][".order"], s) - tbl[c][s][".type"] = t + tbl[s] = {} + table.insert(ord, s) + tbl[s][".type"] = t end c, s, o, v = line:match("^([^.]-)%.([^.]-)%.([^.]-)=(.-)$") if c then - tbl[c][s][o] = v + tbl[s][o] = v end end - return tbl + return tbl, ord end -- Build path (config.section.option=value) and prevent command injection diff --git a/module/admin-core/src/controller/admin/index.lua b/module/admin-core/src/controller/admin/index.lua index 09d5aa1f5..edca5721e 100644 --- a/module/admin-core/src/controller/admin/index.lua +++ b/module/admin-core/src/controller/admin/index.lua @@ -23,133 +23,149 @@ function configure_freifunk() local ip = ffluci.http.formvalue("ip") local uci = ffluci.model.uci.Session() + -- Load UCI + uci:t_load("network") + uci:t_load("dhcp") + uci:t_load("freifunk") + uci:t_load("luci_splash") + uci:t_load("olsr") + uci:t_load("wireless") + + -- Configure FF-Interface - uci:del("network", "ff") - uci:del("network", "ffdhcp") + uci:t_del("network", "ff") + uci:t_del("network", "ffdhcp") - uci:set("network", "ff", nil, "interface") - uci:set("network", "ff", "type", "bridge") - uci:set("network", "ff", "proto", "static") - uci:set("network", "ff", "ipaddr", ip) - uci:set("network", "ff", "netmask", uci:get("freifunk", "community", "mask")) - uci:set("network", "ff", "dns", uci:get("freifunk", "community", "dns")) + uci:t_set("network", "ff", nil, "interface") + uci:t_set("network", "ff", "type", "bridge") + uci:t_set("network", "ff", "proto", "static") + uci:t_set("network", "ff", "ipaddr", ip) + uci:t_set("network", "ff", "netmask", uci:t_get("freifunk", "community", "mask")) + uci:t_set("network", "ff", "dns", uci:t_get("freifunk", "community", "dns")) -- Enable internal routing - uci:set("freifunk", "routing", "internal", "1") + uci:t_set("freifunk", "routing", "internal", "1") -- Enable internet routing if ffluci.http.formvalue("shareinet") then - uci:set("freifunk", "routing", "internet", "1") + uci:t_set("freifunk", "routing", "internet", "1") else - uci:set("freifunk", "routing", "internet", "0") + uci:t_set("freifunk", "routing", "internet", "0") end -- Configure DHCP if ffluci.http.formvalue("dhcp") then - local dhcpnet = uci:get("freifunk", "community", "dhcp"):match("^([0-9]+)") + local dhcpnet = uci:t_get("freifunk", "community", "dhcp"):match("^([0-9]+)") local dhcpip = ip:gsub("^[0-9]+", dhcpnet) - uci:set("network", "ffdhcp", nil, "interface") - uci:set("network", "ffdhcp", "proto", "static") - uci:set("network", "ffdhcp", "ifname", "br-ff:dhcp") - uci:set("network", "ffdhcp", "ipaddr", dhcpip) - uci:set("network", "ffdhcp", "netmask", uci:get("freifunk", "community", "dhcpmask")) + uci:t_set("network", "ffdhcp", nil, "interface") + uci:t_set("network", "ffdhcp", "proto", "static") + uci:t_set("network", "ffdhcp", "ifname", "br-ff:dhcp") + uci:t_set("network", "ffdhcp", "ipaddr", dhcpip) + uci:t_set("network", "ffdhcp", "netmask", uci:t_get("freifunk", "community", "dhcpmask")) - local dhcp = uci:sections("dhcp") + local dhcp = uci:t_sections("dhcp") if dhcp then for k, v in pairs(dhcp) do if v[".type"] == "dhcp" and v.interface == "ffdhcp" then - uci:del("dhcp", k) + uci:t_del("dhcp", k) end end local dhcpbeg = 48 + tonumber(ip:match("[0-9]+$")) * 4 - local sk = uci:add("dhcp", "dhcp") - uci:set("dhcp", sk, "interface", "ffdhcp") - uci:set("dhcp", sk, "start", dhcpbeg) - uci:set("dhcp", sk, "limit", (dhcpbeg < 252) and 3 or 2) - uci:set("dhcp", sk, "leasetime", "30m") + local sk = uci:t_add("dhcp", "dhcp") + uci:t_set("dhcp", sk, "interface", "ffdhcp") + uci:t_set("dhcp", sk, "start", dhcpbeg) + uci:t_set("dhcp", sk, "limit", (dhcpbeg < 252) and 3 or 2) + uci:t_set("dhcp", sk, "leasetime", "30m") end - local splash = uci:sections("luci_splash") + local splash = uci:t_sections("luci_splash") if splash then for k, v in pairs(splash) do if v[".type"] == "iface" then - uci:del("luci_splash", k) + uci:t_del("luci_splash", k) end end - local sk = uci:add("luci_splash", "iface") - uci:set("luci_splash", sk, "network", "ffdhcp") + local sk = uci:t_add("luci_splash", "iface") + uci:t_set("luci_splash", sk, "network", "ffdhcp") end end -- Configure OLSR - if ffluci.http.formvalue("olsr") and uci:sections("olsr") then - for k, v in pairs(uci:sections("olsr")) do + if ffluci.http.formvalue("olsr") and uci:t_sections("olsr") then + for k, v in pairs(uci:t_sections("olsr")) do if v[".type"] == "Interface" or v[".type"] == "LoadPlugin" then - uci:del("olsr", k) + uci:t_del("olsr", k) end end if ffluci.http.formvalue("shareinet") then - uci:set("olsr", "dyn_gw", nil, "LoadPlugin") - uci:set("olsr", "dyn_gw", "Library", "olsrd_dyn_gw.so.0.4") + uci:t_set("olsr", "dyn_gw", nil, "LoadPlugin") + uci:t_set("olsr", "dyn_gw", "Library", "olsrd_dyn_gw.so.0.4") end - uci:set("olsr", "nameservice", nil, "LoadPlugin") - uci:set("olsr", "nameservice", "Library", "olsrd_nameservice.so.0.3") - uci:set("olsr", "nameservice", "name", ip:gsub("%.", "-")) - uci:set("olsr", "nameservice", "hosts_file", "/var/etc/hosts") - uci:set("olsr", "nameservice", "suffix", ".olsr") - uci:set("olsr", "nameservice", "latlon_infile", "/tmp/latlon.txt") + uci:t_set("olsr", "nameservice", nil, "LoadPlugin") + uci:t_set("olsr", "nameservice", "Library", "olsrd_nameservice.so.0.3") + uci:t_set("olsr", "nameservice", "name", ip:gsub("%.", "-")) + uci:t_set("olsr", "nameservice", "hosts_file", "/var/etc/hosts") + uci:t_set("olsr", "nameservice", "suffix", ".olsr") + uci:t_set("olsr", "nameservice", "latlon_infile", "/tmp/latlon.txt") - uci:set("olsr", "txtinfo", nil, "LoadPlugin") - uci:set("olsr", "txtinfo", "Library", "olsrd_txtinfo.so.0.1") - uci:set("olsr", "txtinfo", "Accept", "127.0.0.1") + uci:t_set("olsr", "txtinfo", nil, "LoadPlugin") + uci:t_set("olsr", "txtinfo", "Library", "olsrd_txtinfo.so.0.1") + uci:t_set("olsr", "txtinfo", "Accept", "127.0.0.1") - local oif = uci:add("olsr", "Interface") - uci:set("olsr", oif, "Interface", "ff") - uci:set("olsr", oif, "HelloInterval", "6.0") - uci:set("olsr", oif, "HelloValidityTime", "108.0") - uci:set("olsr", oif, "TcInterval", "4.0") - uci:set("olsr", oif, "TcValidityTime", "324.0") - uci:set("olsr", oif, "MidInterval", "18.0") - uci:set("olsr", oif, "MidValidityTime", "324.0") - uci:set("olsr", oif, "HnaInterval", "18.0") - uci:set("olsr", oif, "HnaValidityTime", "108.0") + local oif = uci:t_add("olsr", "Interface") + uci:t_set("olsr", oif, "Interface", "ff") + uci:t_set("olsr", oif, "HelloInterval", "6.0") + uci:t_set("olsr", oif, "HelloValidityTime", "108.0") + uci:t_set("olsr", oif, "TcInterval", "4.0") + uci:t_set("olsr", oif, "TcValidityTime", "324.0") + uci:t_set("olsr", oif, "MidInterval", "18.0") + uci:t_set("olsr", oif, "MidValidityTime", "324.0") + uci:t_set("olsr", oif, "HnaInterval", "18.0") + uci:t_set("olsr", oif, "HnaValidityTime", "108.0") end -- Configure Wifi - local wcfg = uci:sections("wireless") + local wcfg = uci:t_sections("wireless") if wcfg then for iface, v in pairs(wcfg) do if v[".type"] == "wifi-device" and ffluci.http.formvalue("wifi."..iface) then -- Cleanup for k, j in pairs(wcfg) do if j[".type"] == "wifi-iface" and j.device == iface then - uci:del("wireless", k) + uci:t_del("wireless", k) end end - uci:set("wireless", iface, "disabled", "0") - uci:set("wireless", iface, "mode", "11g") - uci:set("wireless", iface, "txantenna", 1) - uci:set("wireless", iface, "rxantenna", 1) - uci:set("wireless", iface, "channel", uci:get("freifunk", "community", "channel")) + uci:t_set("wireless", iface, "disabled", "0") + uci:t_set("wireless", iface, "mode", "11g") + uci:t_set("wireless", iface, "txantenna", 1) + uci:t_set("wireless", iface, "rxantenna", 1) + uci:t_set("wireless", iface, "channel", uci:t_get("freifunk", "community", "channel")) - local wif = uci:add("wireless", "wifi-iface") - uci:set("wireless", wif, "device", iface) - uci:set("wireless", wif, "network", "ff") - uci:set("wireless", wif, "mode", "adhoc") - uci:set("wireless", wif, "ssid", uci:get("freifunk", "community", "essid")) - uci:set("wireless", wif, "bssid", uci:get("freifunk", "community", "bssid")) - uci:set("wireless", wif, "txpower", 13) + local wif = uci:t_add("wireless", "wifi-iface") + uci:t_set("wireless", wif, "device", iface) + uci:t_set("wireless", wif, "network", "ff") + uci:t_set("wireless", wif, "mode", "adhoc") + uci:t_set("wireless", wif, "ssid", uci:t_get("freifunk", "community", "essid")) + uci:t_set("wireless", wif, "bssid", uci:t_get("freifunk", "community", "bssid")) + uci:t_set("wireless", wif, "txpower", 13) end end end - + + -- Save UCI + uci:t_save("network") + uci:t_save("dhcp") + uci:t_save("freifunk") + uci:t_save("luci_splash") + uci:t_save("olsr") + uci:t_save("wireless") ffluci.http.redirect(ffluci.dispatcher.build_url("admin", "uci", "changes")) end \ No newline at end of file diff --git a/module/admin-core/src/controller/admin/uci.lua b/module/admin-core/src/controller/admin/uci.lua index 251107dfe..5c34e56d3 100644 --- a/module/admin-core/src/controller/admin/uci.lua +++ b/module/admin-core/src/controller/admin/uci.lua @@ -10,16 +10,15 @@ function action_apply() if changes then local apply = {} - -- Collect files to be applied + -- Collect files to be applied and commit changes for i, line in ipairs(ffluci.util.split(changes)) do local r = line:match("^-?([^.]+)") if r and not ffluci.util.contains(apply, ffluci.config.uci_oncommit[r]) then table.insert(apply, ffluci.config.uci_oncommit[r]) + ffluci.model.uci.commit(r) end end - -- Commit changes - ffluci.model.uci.commit() -- Search for post-commit commands if ffluci.config.uci_oncommit then -- 2.25.1