summaryrefslogtreecommitdiffhomepage
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/luci-lib-base/Makefile14
-rw-r--r--libs/luci-lib-base/luasrc/debug.lua37
-rw-r--r--libs/luci-lib-base/luasrc/http.lua554
-rw-r--r--libs/luci-lib-base/luasrc/http.luadoc260
-rw-r--r--libs/luci-lib-base/luasrc/ltn12.lua316
-rw-r--r--libs/luci-lib-base/luasrc/util.lua782
-rw-r--r--libs/luci-lib-base/luasrc/util.luadoc419
-rw-r--r--libs/luci-lib-httpclient/Makefile2
-rw-r--r--libs/luci-lib-httpprotoutils/Makefile2
-rw-r--r--libs/luci-lib-ip/src/ip.c6
-rw-r--r--libs/luci-lib-nixio/src/Makefile2
-rw-r--r--libs/rpcd-mod-luci/Makefile5
-rw-r--r--libs/rpcd-mod-luci/src/luci.c623
-rw-r--r--libs/rpcd-mod-rad2-enc/Makefile3
-rw-r--r--libs/rpcd-mod-rrdns/Makefile3
15 files changed, 2791 insertions, 237 deletions
diff --git a/libs/luci-lib-base/Makefile b/libs/luci-lib-base/Makefile
new file mode 100644
index 0000000000..35b1836ecd
--- /dev/null
+++ b/libs/luci-lib-base/Makefile
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=basic libraries for luci
+LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +luci-lib-jsonc +liblucihttp-lua
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/libs/luci-lib-base/luasrc/debug.lua b/libs/luci-lib-base/luasrc/debug.lua
new file mode 100644
index 0000000000..8ff1bb6981
--- /dev/null
+++ b/libs/luci-lib-base/luasrc/debug.lua
@@ -0,0 +1,37 @@
+local debug = require "debug"
+local io = require "io"
+local collectgarbage, floor = collectgarbage, math.floor
+
+module "luci.debug"
+__file__ = debug.getinfo(1, 'S').source:sub(2)
+
+-- Enables the memory tracer with given flags and returns a function to disable the tracer again
+function trap_memtrace(flags, dest)
+ flags = flags or "clr"
+ local tracefile = io.open(dest or "/tmp/memtrace", "w")
+ local peak = 0
+
+ local function trap(what, line)
+ local info = debug.getinfo(2, "Sn")
+ local size = floor(collectgarbage("count"))
+ if size > peak then
+ peak = size
+ end
+ if tracefile then
+ tracefile:write(
+ "[", what, "] ", info.source, ":", (line or "?"), "\t",
+ (info.namewhat or ""), "\t",
+ (info.name or ""), "\t",
+ size, " (", peak, ")\n"
+ )
+ end
+ end
+
+ debug.sethook(trap, flags)
+
+ return function()
+ debug.sethook()
+ tracefile:close()
+ end
+end
+
diff --git a/libs/luci-lib-base/luasrc/http.lua b/libs/luci-lib-base/luasrc/http.lua
new file mode 100644
index 0000000000..20b55f2854
--- /dev/null
+++ b/libs/luci-lib-base/luasrc/http.lua
@@ -0,0 +1,554 @@
+-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
+-- Licensed to the public under the Apache License 2.0.
+
+local util = require "luci.util"
+local coroutine = require "coroutine"
+local table = require "table"
+local lhttp = require "lucihttp"
+local nixio = require "nixio"
+local ltn12 = require "luci.ltn12"
+
+local table, ipairs, pairs, type, tostring, tonumber, error =
+ table, ipairs, pairs, type, tostring, tonumber, error
+
+module "luci.http"
+
+HTTP_MAX_CONTENT = 1024*100 -- 100 kB maximum content size
+
+context = util.threadlocal()
+
+Request = util.class()
+function Request.__init__(self, env, sourcein, sinkerr)
+ self.input = sourcein
+ self.error = sinkerr
+
+
+ -- File handler nil by default to let .content() work
+ self.filehandler = nil
+
+ -- HTTP-Message table
+ self.message = {
+ env = env,
+ headers = {},
+ params = urldecode_params(env.QUERY_STRING or ""),
+ }
+
+ self.parsed_input = false
+end
+
+function Request.formvalue(self, name, noparse)
+ if not noparse and not self.parsed_input then
+ self:_parse_input()
+ end
+
+ if name then
+ return self.message.params[name]
+ else
+ return self.message.params
+ end
+end
+
+function Request.formvaluetable(self, prefix)
+ local vals = {}
+ prefix = prefix and prefix .. "." or "."
+
+ if not self.parsed_input then
+ self:_parse_input()
+ end
+
+ local void = self.message.params[nil]
+ for k, v in pairs(self.message.params) do
+ if k:find(prefix, 1, true) == 1 then
+ vals[k:sub(#prefix + 1)] = tostring(v)
+ end
+ end
+
+ return vals
+end
+
+function Request.content(self)
+ if not self.parsed_input then
+ self:_parse_input()
+ end
+
+ return self.message.content, self.message.content_length
+end
+
+function Request.getcookie(self, name)
+ return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name)
+end
+
+function Request.getenv(self, name)
+ if name then
+ return self.message.env[name]
+ else
+ return self.message.env
+ end
+end
+
+function Request.setfilehandler(self, callback)
+ self.filehandler = callback
+
+ if not self.parsed_input then
+ return
+ end
+
+ -- If input has already been parsed then uploads are stored as unlinked
+ -- temporary files pointed to by open file handles in the parameter
+ -- value table. Loop all params, and invoke the file callback for any
+ -- param with an open file handle.
+ local name, value
+ for name, value in pairs(self.message.params) do
+ if type(value) == "table" then
+ while value.fd do
+ local data = value.fd:read(1024)
+ local eof = (not data or data == "")
+
+ callback(value, data, eof)
+
+ if eof then
+ value.fd:close()
+ value.fd = nil
+ end
+ end
+ end
+ end
+end
+
+function Request._parse_input(self)
+ parse_message_body(
+ self.input,
+ self.message,
+ self.filehandler
+ )
+ self.parsed_input = true
+end
+
+function close()
+ if not context.eoh then
+ context.eoh = true
+ coroutine.yield(3)
+ end
+
+ if not context.closed then
+ context.closed = true
+ coroutine.yield(5)
+ end
+end
+
+function content()
+ return context.request:content()
+end
+
+function formvalue(name, noparse)
+ return context.request:formvalue(name, noparse)
+end
+
+function formvaluetable(prefix)
+ return context.request:formvaluetable(prefix)
+end
+
+function getcookie(name)
+ return context.request:getcookie(name)
+end
+
+-- or the environment table itself.
+function getenv(name)
+ return context.request:getenv(name)
+end
+
+function setfilehandler(callback)
+ return context.request:setfilehandler(callback)
+end
+
+function header(key, value)
+ if not context.headers then
+ context.headers = {}
+ end
+ context.headers[key:lower()] = value
+ coroutine.yield(2, key, value)
+end
+
+function prepare_content(mime)
+ if not context.headers or not context.headers["content-type"] then
+ if mime == "application/xhtml+xml" then
+ if not getenv("HTTP_ACCEPT") or
+ not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
+ mime = "text/html; charset=UTF-8"
+ end
+ header("Vary", "Accept")
+ end
+ header("Content-Type", mime)
+ end
+end
+
+function source()
+ return context.request.input
+end
+
+function status(code, message)
+ code = code or 200
+ message = message or "OK"
+ context.status = code
+ coroutine.yield(1, code, message)
+end
+
+-- This function is as a valid LTN12 sink.
+-- If the content chunk is nil this function will automatically invoke close.
+function write(content, src_err)
+ if not content then
+ if src_err then
+ error(src_err)
+ else
+ close()
+ end
+ return true
+ elseif #content == 0 then
+ return true
+ else
+ if not context.eoh then
+ if not context.status then
+ status()
+ end
+ if not context.headers or not context.headers["content-type"] then
+ header("Content-Type", "text/html; charset=utf-8")
+ end
+ if not context.headers["cache-control"] then
+ header("Cache-Control", "no-cache")
+ header("Expires", "0")
+ end
+ if not context.headers["x-frame-options"] then
+ header("X-Frame-Options", "SAMEORIGIN")
+ end
+ if not context.headers["x-xss-protection"] then
+ header("X-XSS-Protection", "1; mode=block")
+ end
+ if not context.headers["x-content-type-options"] then
+ header("X-Content-Type-Options", "nosniff")
+ end
+
+ context.eoh = true
+ coroutine.yield(3)
+ end
+ coroutine.yield(4, content)
+ return true
+ end
+end
+
+function splice(fd, size)
+ coroutine.yield(6, fd, size)
+end
+
+function redirect(url)
+ if url == "" then url = "/" end
+ status(302, "Found")
+ header("Location", url)
+ close()
+end
+
+function build_querystring(q)
+ local s, n, k, v = {}, 1, nil, nil
+
+ for k, v in pairs(q) do
+ s[n+0] = (n == 1) and "?" or "&"
+ s[n+1] = util.urlencode(k)
+ s[n+2] = "="
+ s[n+3] = util.urlencode(v)
+ n = n + 4
+ end
+
+ return table.concat(s, "")
+end
+
+urldecode = util.urldecode
+
+urlencode = util.urlencode
+
+function write_json(x)
+ util.serialize_json(x, write)
+end
+
+-- from given url or string. Returns a table with urldecoded values.
+-- Simple parameters are stored as string values associated with the parameter
+-- name within the table. Parameters with multiple values are stored as array
+-- containing the corresponding values.
+function urldecode_params(url, tbl)
+ local parser, name
+ local params = tbl or { }
+
+ parser = lhttp.urlencoded_parser(function (what, buffer, length)
+ if what == parser.TUPLE then
+ name, value = nil, nil
+ elseif what == parser.NAME then
+ name = lhttp.urldecode(buffer)
+ elseif what == parser.VALUE and name then
+ params[name] = lhttp.urldecode(buffer) or ""
+ end
+
+ return true
+ end)
+
+ if parser then
+ parser:parse((url or ""):match("[^?]*$"))
+ parser:parse(nil)
+ end
+
+ return params
+end
+
+-- separated by "&". Tables are encoded as parameters with multiple values by
+-- repeating the parameter name with each value.
+function urlencode_params(tbl)
+ local k, v
+ local n, enc = 1, {}
+ for k, v in pairs(tbl) do
+ if type(v) == "table" then
+ local i, v2
+ for i, v2 in ipairs(v) do
+ if enc[1] then
+ enc[n] = "&"
+ n = n + 1
+ end
+
+ enc[n+0] = lhttp.urlencode(k)
+ enc[n+1] = "="
+ enc[n+2] = lhttp.urlencode(v2)
+ n = n + 3
+ end
+ else
+ if enc[1] then
+ enc[n] = "&"
+ n = n + 1
+ end
+
+ enc[n+0] = lhttp.urlencode(k)
+ enc[n+1] = "="
+ enc[n+2] = lhttp.urlencode(v)
+ n = n + 3
+ end
+ end
+
+ return table.concat(enc, "")
+end
+
+-- Content-Type. Stores all extracted data associated with its parameter name
+-- in the params table within the given message object. Multiple parameter
+-- values are stored as tables, ordinary ones as strings.
+-- If an optional file callback function is given then it is fed with the
+-- file contents chunk by chunk and only the extracted file name is stored
+-- within the params table. The callback function will be called subsequently
+-- with three arguments:
+-- o Table containing decoded (name, file) and raw (headers) mime header data
+-- o String value containing a chunk of the file data
+-- o Boolean which indicates whether the current chunk is the last one (eof)
+function mimedecode_message_body(src, msg, file_cb)
+ local parser, header, field
+ local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
+
+ parser, err = lhttp.multipart_parser(msg.env.CONTENT_TYPE, function (what, buffer, length)
+ if what == parser.PART_INIT then
+ field = { }
+
+ elseif what == parser.HEADER_NAME then
+ header = buffer:lower()
+
+ elseif what == parser.HEADER_VALUE and header then
+ if header:lower() == "content-disposition" and
+ lhttp.header_attribute(buffer, nil) == "form-data"
+ then
+ field.name = lhttp.header_attribute(buffer, "name")
+ field.file = lhttp.header_attribute(buffer, "filename")
+ field[1] = field.file
+ end
+
+ if field.headers then
+ field.headers[header] = buffer
+ else
+ field.headers = { [header] = buffer }
+ end
+
+ elseif what == parser.PART_BEGIN then
+ return not field.file
+
+ elseif what == parser.PART_DATA and field.name and length > 0 then
+ if field.file then
+ if file_cb then
+ file_cb(field, buffer, false)
+ msg.params[field.name] = msg.params[field.name] or field
+ else
+ if not field.fd then
+ field.fd = nixio.mkstemp(field.name)
+ end
+
+ if field.fd then
+ field.fd:write(buffer)
+ msg.params[field.name] = msg.params[field.name] or field
+ end
+ end
+ else
+ field.value = buffer
+ end
+
+ elseif what == parser.PART_END and field.name then
+ if field.file and msg.params[field.name] then
+ if file_cb then
+ file_cb(field, "", true)
+ elseif field.fd then
+ field.fd:seek(0, "set")
+ end
+ else
+ local val = msg.params[field.name]
+
+ if type(val) == "table" then
+ val[#val+1] = field.value or ""
+ elseif val ~= nil then
+ msg.params[field.name] = { val, field.value or "" }
+ else
+ msg.params[field.name] = field.value or ""
+ end
+ end
+
+ field = nil
+
+ elseif what == parser.ERROR then
+ err = buffer
+ end
+
+ return true
+ end, HTTP_MAX_CONTENT)
+
+ return ltn12.pump.all(src, function (chunk)
+ len = len + (chunk and #chunk or 0)
+
+ if maxlen and len > maxlen + 2 then
+ return nil, "Message body size exceeds Content-Length"
+ end
+
+ if not parser or not parser:parse(chunk) then
+ return nil, err
+ end
+
+ return true
+ end)
+end
+
+-- Content-Type. Stores all extracted data associated with its parameter name
+-- in the params table within the given message object. Multiple parameter
+-- values are stored as tables, ordinary ones as strings.
+function urldecode_message_body(src, msg)
+ local err, name, value, parser
+ local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
+
+ parser = lhttp.urlencoded_parser(function (what, buffer, length)
+ if what == parser.TUPLE then
+ name, value = nil, nil
+ elseif what == parser.NAME then
+ name = lhttp.urldecode(buffer, lhttp.DECODE_PLUS)
+ elseif what == parser.VALUE and name then
+ local val = msg.params[name]
+
+ if type(val) == "table" then
+ val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
+ elseif val ~= nil then
+ msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" }
+ else
+ msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
+ end
+ elseif what == parser.ERROR then
+ err = buffer
+ end
+
+ return true
+ end, HTTP_MAX_CONTENT)
+
+ return ltn12.pump.all(src, function (chunk)
+ len = len + (chunk and #chunk or 0)
+
+ if maxlen and len > maxlen + 2 then
+ return nil, "Message body size exceeds Content-Length"
+ elseif len > HTTP_MAX_CONTENT then
+ return nil, "Message body size exceeds maximum allowed length"
+ end
+
+ if not parser or not parser:parse(chunk) then
+ return nil, err
+ end
+
+ return true
+ end)
+end
+
+-- This function will examine the Content-Type within the given message object
+-- to select the appropriate content decoder.
+-- Currently the application/x-www-urlencoded and application/form-data
+-- mime types are supported. If the encountered content encoding can't be
+-- handled then the whole message body will be stored unaltered as "content"
+-- property within the given message object.
+function parse_message_body(src, msg, filecb)
+ if msg.env.CONTENT_LENGTH or msg.env.REQUEST_METHOD == "POST" then
+ local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
+
+ -- Is it multipart/mime ?
+ if ctype == "multipart/form-data" then
+ return mimedecode_message_body(src, msg, filecb)
+
+ -- Is it application/x-www-form-urlencoded ?
+ elseif ctype == "application/x-www-form-urlencoded" then
+ return urldecode_message_body(src, msg)
+
+ end
+
+ -- Unhandled encoding
+ -- If a file callback is given then feed it chunk by chunk, else
+ -- store whole buffer in message.content
+ local sink
+
+ -- If we have a file callback then feed it
+ if type(filecb) == "function" then
+ local meta = {
+ name = "raw",
+ encoding = msg.env.CONTENT_TYPE
+ }
+ sink = function( chunk )
+ if chunk then
+ return filecb(meta, chunk, false)
+ else
+ return filecb(meta, nil, true)
+ end
+ end
+ -- ... else append to .content
+ else
+ msg.content = ""
+ msg.content_length = 0
+
+ sink = function( chunk )
+ if chunk then
+ if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
+ msg.content = msg.content .. chunk
+ msg.content_length = msg.content_length + #chunk
+ return true
+ else
+ return nil, "POST data exceeds maximum allowed length"
+ end
+ end
+ return true
+ end
+ end
+
+ -- Pump data...
+ while true do
+ local ok, err = ltn12.pump.step( src, sink )
+
+ if not ok and err then
+ return nil, err
+ elseif not ok then -- eof
+ return true
+ end
+ end
+
+ return true
+ end
+
+ return false
+end
diff --git a/libs/luci-lib-base/luasrc/http.luadoc b/libs/luci-lib-base/luasrc/http.luadoc
new file mode 100644
index 0000000000..8f6f380d8b
--- /dev/null
+++ b/libs/luci-lib-base/luasrc/http.luadoc
@@ -0,0 +1,260 @@
+---[[
+LuCI Web Framework high-level HTTP functions.
+]]
+module "luci.http"
+
+---[[
+Close the HTTP-Connection.
+
+@class function
+@name close
+]]
+
+---[[
+Return the request content if the request was of unknown type.
+
+@class function
+@name content
+@return HTTP request body
+@return HTTP request body length
+]]
+
+---[[
+Get a certain HTTP input value or a table of all input values.
+
+@class function
+@name formvalue
+@param name Name of the GET or POST variable to fetch
+@param noparse Don't parse POST data before getting the value
+@return HTTP input value or table of all input value
+]]
+
+---[[
+Get a table of all HTTP input values with a certain prefix.
+
+@class function
+@name formvaluetable
+@param prefix Prefix
+@return Table of all HTTP input values with given prefix
+]]
+
+---[[
+Get the value of a certain HTTP-Cookie.
+
+@class function
+@name getcookie
+@param name Cookie Name
+@return String containing cookie data
+]]
+
+---[[
+Get the value of a certain HTTP environment variable
+or the environment table itself.
+
+@class function
+@name getenv
+@param name Environment variable
+@return HTTP environment value or environment table
+]]
+
+---[[
+Set a handler function for incoming user file uploads.
+
+@class function
+@name setfilehandler
+@param callback Handler function
+]]
+
+---[[
+Send a HTTP-Header.
+
+@class function
+@name header
+@param key Header key
+@param value Header value
+]]
+
+---[[
+Set the mime type of following content data.
+
+@class function
+@name prepare_content
+@param mime Mimetype of following content
+]]
+
+---[[
+Get the RAW HTTP input source
+
+@class function
+@name source
+@return HTTP LTN12 source
+]]
+
+---[[
+Set the HTTP status code and status message.
+
+@class function
+@name status
+@param code Status code
+@param message Status message
+]]
+
+---[[
+Send a chunk of content data to the client.
+
+This function is as a valid LTN12 sink.
+If the content chunk is nil this function will automatically invoke close.
+
+@class function
+@name write
+@param content Content chunk
+@param src_err Error object from source (optional)
+@see close
+]]
+
+---[[
+Splice data from a filedescriptor to the client.
+
+@class function
+@name splice
+@param fp File descriptor
+@param size Bytes to splice (optional)
+]]
+
+---[[
+Redirects the client to a new URL and closes the connection.
+
+@class function
+@name redirect
+@param url Target URL
+]]
+
+---[[
+Create a querystring out of a table of key - value pairs.
+
+@class function
+@name build_querystring
+@param table Query string source table
+@return Encoded HTTP query string
+]]
+
+---[[
+Return the URL-decoded equivalent of a string.
+
+@class function
+@name urldecode
+@param str URL-encoded string
+@param no_plus Don't decode + to " "
+@return URL-decoded string
+@see urlencode
+]]
+
+---[[
+Return the URL-encoded equivalent of a string.
+
+@class function
+@name urlencode
+@param str Source string
+@return URL-encoded string
+@see urldecode
+]]
+
+---[[
+Send the given data as JSON encoded string.
+
+@class function
+@name write_json
+@param data Data to send
+]]
+
+---[[
+Extract and split urlencoded data pairs, separated bei either "&" or ";"
+from given url or string. Returns a table with urldecoded values.
+
+Simple parameters are stored as string values associated with the parameter
+name within the table. Parameters with multiple values are stored as array
+containing the corresponding values.
+
+@class function
+@name urldecode_params
+@param url The url or string which contains x-www-urlencoded form data
+@param tbl Use the given table for storing values (optional)
+@return Table containing the urldecoded parameters
+@see urlencode_params
+]]
+
+---[[
+Encode each key-value-pair in given table to x-www-urlencoded format,
+separated by "&".
+
+Tables are encoded as parameters with multiple values by repeating the
+parameter name with each value.
+
+@class function
+@name urlencode_params
+@param tbl Table with the values
+@return String containing encoded values
+@see urldecode_params
+]]
+
+---[[
+Decode a mime encoded http message body with multipart/form-data Content-Type.
+
+Stores all extracted data associated with its parameter name
+in the params table within the given message object. Multiple parameter
+values are stored as tables, ordinary ones as strings.
+
+If an optional file callback function is given then it is fed with the
+file contents chunk by chunk and only the extracted file name is stored
+within the params table. The callback function will be called subsequently
+with three arguments:
+ o Table containing decoded (name, file) and raw (headers) mime header data
+ o String value containing a chunk of the file data
+ o Boolean which indicates whether the current chunk is the last one (eof)
+
+@class function
+@name mimedecode_message_body
+@param src Ltn12 source function
+@param msg HTTP message object
+@param filecb File callback function (optional)
+@return Value indicating successful operation (not nil means "ok")
+@return String containing the error if unsuccessful
+@see parse_message_header
+]]
+
+---[[
+Decode an urlencoded http message body with application/x-www-urlencoded
+Content-Type.
+
+Stores all extracted data associated with its parameter name in the params
+table within the given message object. Multiple parameter values are stored
+as tables, ordinary ones as strings.
+
+@class function
+@name urldecode_message_body
+@param src Ltn12 source function
+@param msg HTTP message object
+@return Value indicating successful operation (not nil means "ok")
+@return String containing the error if unsuccessful
+@see parse_message_header
+]]
+
+---[[
+Try to extract and decode a http message body from the given ltn12 source.
+This function will examine the Content-Type within the given message object
+to select the appropriate content decoder.
+
+Currently the application/x-www-urlencoded and application/form-data
+mime types are supported. If the encountered content encoding can't be
+handled then the whole message body will be stored unaltered as "content"
+property within the given message object.
+
+@class function
+@name parse_message_body
+@param src Ltn12 source function
+@param msg HTTP message object
+@param filecb File data callback (optional, see mimedecode_message_body())
+@return Value indicating successful operation (not nil means "ok")
+@return String containing the error if unsuccessful
+@see parse_message_header
+]]
diff --git a/libs/luci-lib-base/luasrc/ltn12.lua b/libs/luci-lib-base/luasrc/ltn12.lua
new file mode 100644
index 0000000000..3a7268ccae
--- /dev/null
+++ b/libs/luci-lib-base/luasrc/ltn12.lua
@@ -0,0 +1,316 @@
+--[[
+LuaSocket 2.0.2 license
+Copyright � 2004-2007 Diego Nehab
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+]]--
+--[[
+ Changes made by LuCI project:
+ * Renamed to luci.ltn12 to avoid collisions with luasocket
+ * Added inline documentation
+]]--
+-----------------------------------------------------------------------------
+-- LTN12 - Filters, sources, sinks and pumps.
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-- RCS ID: $Id$
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module
+-----------------------------------------------------------------------------
+local string = require("string")
+local table = require("table")
+local base = _G
+
+-- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts
+module("luci.ltn12")
+
+filter = {}
+source = {}
+sink = {}
+pump = {}
+
+-- 2048 seems to be better in windows...
+BLOCKSIZE = 2048
+_VERSION = "LTN12 1.0.1"
+
+-----------------------------------------------------------------------------
+-- Filter stuff
+-----------------------------------------------------------------------------
+
+
+-- by passing it each chunk and updating a context between calls.
+function filter.cycle(low, ctx, extra)
+ base.assert(low)
+ return function(chunk)
+ local ret
+ ret, ctx = low(ctx, chunk, extra)
+ return ret
+ end
+end
+
+-- (thanks to Wim Couwenberg)
+function filter.chain(...)
+ local n = table.getn(arg)
+ local top, index = 1, 1
+ local retry = ""
+ return function(chunk)
+ retry = chunk and retry
+ while true do
+ if index == top then
+ chunk = arg[index](chunk)
+ if chunk == "" or top == n then return chunk
+ elseif chunk then index = index + 1
+ else
+ top = top+1
+ index = top
+ end
+ else
+ chunk = arg[index](chunk or "")
+ if chunk == "" then
+ index = index - 1
+ chunk = retry
+ elseif chunk then
+ if index == n then return chunk
+ else index = index + 1 end
+ else base.error("filter returned inappropriate nil") end
+ end
+ end
+ end
+end
+
+-----------------------------------------------------------------------------
+-- Source stuff
+-----------------------------------------------------------------------------
+
+
+-- create an empty source
+local function empty()
+ return nil
+end
+
+function source.empty()
+ return empty
+end
+
+function source.error(err)
+ return function()
+ return nil, err
+ end
+end
+
+function source.file(handle, io_err)
+ if handle then
+ return function()
+ local chunk = handle:read(BLOCKSIZE)
+ if chunk and chunk:len() == 0 then chunk = nil end
+ if not chunk then handle:close() end
+ return chunk
+ end
+ else return source.error(io_err or "unable to open file") end
+end
+
+function source.simplify(src)
+ base.assert(src)
+ return function()
+ local chunk, err_or_new = src()
+ src = err_or_new or src
+ if not chunk then return nil, err_or_new
+ else return chunk end
+ end
+end
+
+function source.string(s)
+ if s then
+ local i = 1
+ return function()
+ local chunk = string.sub(s, i, i+BLOCKSIZE-1)
+ i = i + BLOCKSIZE
+ if chunk ~= "" then return chunk
+ else return nil end
+ end
+ else return source.empty() end
+end
+
+function source.rewind(src)
+ base.assert(src)
+ local t = {}
+ return function(chunk)
+ if not chunk then
+ chunk = table.remove(t)
+ if not chunk then return src()
+ else return chunk end
+ else
+ t[#t+1] = chunk
+ end
+ end
+end
+
+function source.chain(src, f)
+ base.assert(src and f)
+ local last_in, last_out = "", ""
+ local state = "feeding"
+ local err
+ return function()
+ if not last_out then
+ base.error('source is empty!', 2)
+ end
+ while true do
+ if state == "feeding" then
+ last_in, err = src()
+ if err then return nil, err end
+ last_out = f(last_in)
+ if not last_out then
+ if last_in then
+ base.error('filter returned inappropriate nil')
+ else
+ return nil
+ end
+ elseif last_out ~= "" then
+ state = "eating"
+ if last_in then last_in = "" end
+ return last_out
+ end
+ else
+ last_out = f(last_in)
+ if last_out == "" then
+ if last_in == "" then
+ state = "feeding"
+ else
+ base.error('filter returned ""')
+ end
+ elseif not last_out then
+ if last_in then
+ base.error('filter returned inappropriate nil')
+ else
+ return nil
+ end
+ else
+ return last_out
+ end
+ end
+ end
+ end
+end
+
+-- Sources will be used one after the other, as if they were concatenated
+-- (thanks to Wim Couwenberg)
+function source.cat(...)
+ local src = table.remove(arg, 1)
+ return function()
+ while src do
+ local chunk, err = src()
+ if chunk then return chunk end
+ if err then return nil, err end
+ src = table.remove(arg, 1)
+ end
+ end
+end
+
+-----------------------------------------------------------------------------
+-- Sink stuff
+-----------------------------------------------------------------------------
+
+
+function sink.table(t)
+ t = t or {}
+ local f = function(chunk, err)
+ if chunk then t[#t+1] = chunk end
+ return 1
+ end
+ return f, t
+end
+
+function sink.simplify(snk)
+ base.assert(snk)
+ return function(chunk, err)
+ local ret, err_or_new = snk(chunk, err)
+ if not ret then return nil, err_or_new end
+ snk = err_or_new or snk
+ return 1
+ end
+end
+
+function sink.file(handle, io_err)
+ if handle then
+ return function(chunk, err)
+ if not chunk then
+ handle:close()
+ return 1
+ else return handle:write(chunk) end
+ end
+ else return sink.error(io_err or "unable to open file") end
+end
+
+-- creates a sink that discards data
+local function null()
+ return 1
+end
+
+function sink.null()
+ return null
+end
+
+function sink.error(err)
+ return function()
+ return nil, err
+ end
+end
+
+function sink.chain(f, snk)
+ base.assert(f and snk)
+ return function(chunk, err)
+ if chunk ~= "" then
+ local filtered = f(chunk)
+ local done = chunk and ""
+ while true do
+ local ret, snkerr = snk(filtered, err)
+ if not ret then return nil, snkerr end
+ if filtered == done then return 1 end
+ filtered = f(done)
+ end
+ else return 1 end
+ end
+end
+
+-----------------------------------------------------------------------------
+-- Pump stuff
+-----------------------------------------------------------------------------
+
+
+function pump.step(src, snk)
+ local chunk, src_err = src()
+ local ret, snk_err = snk(chunk, src_err)
+ if chunk and ret then return 1
+ else return nil, src_err or snk_err end
+end
+
+function pump.all(src, snk, step)
+ base.assert(src and snk)
+ step = step or pump.step
+ while true do
+ local ret, err = step(src, snk)
+ if not ret then
+ if err then return nil, err
+ else return 1 end
+ end
+ end
+end
+
diff --git a/libs/luci-lib-base/luasrc/util.lua b/libs/luci-lib-base/luasrc/util.lua
new file mode 100644
index 0000000000..89757917ff
--- /dev/null
+++ b/libs/luci-lib-base/luasrc/util.lua
@@ -0,0 +1,782 @@
+-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Licensed to the public under the Apache License 2.0.
+
+local io = require "io"
+local math = require "math"
+local table = require "table"
+local debug = require "debug"
+local ldebug = require "luci.debug"
+local string = require "string"
+local coroutine = require "coroutine"
+local tparser = require "luci.template.parser"
+local json = require "luci.jsonc"
+local lhttp = require "lucihttp"
+
+local _ubus = require "ubus"
+local _ubus_connection = nil
+
+local getmetatable, setmetatable = getmetatable, setmetatable
+local rawget, rawset, unpack, select = rawget, rawset, unpack, select
+local tostring, type, assert, error = tostring, type, assert, error
+local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
+local require, pcall, xpcall = require, pcall, xpcall
+local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
+
+module "luci.util"
+
+--
+-- Pythonic string formatting extension
+--
+getmetatable("").__mod = function(a, b)
+ local ok, res
+
+ if not b then
+ return a
+ elseif type(b) == "table" then
+ local k, _
+ for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
+
+ ok, res = pcall(a.format, a, unpack(b))
+ if not ok then
+ error(res, 2)
+ end
+ return res
+ else
+ if type(b) == "userdata" then b = tostring(b) end
+
+ ok, res = pcall(a.format, a, b)
+ if not ok then
+ error(res, 2)
+ end
+ return res
+ end
+end
+
+
+--
+-- Class helper routines
+--
+
+-- Instantiates a class
+local function _instantiate(class, ...)
+ local inst = setmetatable({}, {__index = class})
+
+ if inst.__init__ then
+ inst:__init__(...)
+ end
+
+ return inst
+end
+
+-- The class object can be instantiated by calling itself.
+-- Any class functions or shared parameters can be attached to this object.
+-- Attaching a table to the class object makes this table shared between
+-- all instances of this class. For object parameters use the __init__ function.
+-- Classes can inherit member functions and values from a base class.
+-- Class can be instantiated by calling them. All parameters will be passed
+-- to the __init__ function of this class - if such a function exists.
+-- The __init__ function must be used to set any object parameters that are not shared
+-- with other objects of this class. Any return values will be ignored.
+function class(base)
+ return setmetatable({}, {
+ __call = _instantiate,
+ __index = base
+ })
+end
+
+function instanceof(object, class)
+ local meta = getmetatable(object)
+ while meta and meta.__index do
+ if meta.__index == class then
+ return true
+ end
+ meta = getmetatable(meta.__index)
+ end
+ return false
+end
+
+
+--
+-- Scope manipulation routines
+--
+
+coxpt = setmetatable({}, { __mode = "kv" })
+
+local tl_meta = {
+ __mode = "k",
+
+ __index = function(self, key)
+ local t = rawget(self, coxpt[coroutine.running()]
+ or coroutine.running() or 0)
+ return t and t[key]
+ end,
+
+ __newindex = function(self, key, value)
+ local c = coxpt[coroutine.running()] or coroutine.running() or 0
+ local r = rawget(self, c)
+ if not r then
+ rawset(self, c, { [key] = value })
+ else
+ r[key] = value
+ end
+ end
+}
+
+-- the current active coroutine. A thread local store is private a table object
+-- whose values can't be accessed from outside of the running coroutine.
+function threadlocal(tbl)
+ return setmetatable(tbl or {}, tl_meta)
+end
+
+
+--
+-- Debugging routines
+--
+
+function perror(obj)
+ return io.stderr:write(tostring(obj) .. "\n")
+end
+
+function dumptable(t, maxdepth, i, seen)
+ i = i or 0
+ seen = seen or setmetatable({}, {__mode="k"})
+
+ for k,v in pairs(t) do
+ perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
+ if type(v) == "table" and (not maxdepth or i < maxdepth) then
+ if not seen[v] then
+ seen[v] = true
+ dumptable(v, maxdepth, i+1, seen)
+ else
+ perror(string.rep("\t", i) .. "*** RECURSION ***")
+ end
+ end
+ end
+end
+
+
+--
+-- String and data manipulation routines
+--
+
+-- compatibility wrapper for xml.pcdata
+function pcdata(value)
+ local xml = require "luci.xml"
+
+ perror("luci.util.pcdata() has been replaced by luci.xml.pcdata() - Please update your code.")
+ return xml.pcdata(value)
+end
+
+function urlencode(value)
+ if value ~= nil then
+ local str = tostring(value)
+ return lhttp.urlencode(str, lhttp.ENCODE_IF_NEEDED + lhttp.ENCODE_FULL)
+ or str
+ end
+ return nil
+end
+
+function urldecode(value, decode_plus)
+ if value ~= nil then
+ local flag = decode_plus and lhttp.DECODE_PLUS or 0
+ local str = tostring(value)
+ return lhttp.urldecode(str, lhttp.DECODE_IF_NEEDED + flag)
+ or str
+ end
+ return nil
+end
+
+-- compatibility wrapper for xml.striptags
+function striptags(value)
+ local xml = require "luci.xml"
+
+ perror("luci.util.striptags() has been replaced by luci.xml.striptags() - Please update your code.")
+ return xml.striptags(value)
+end
+
+function shellquote(value)
+ return string.format("'%s'", string.gsub(value or "", "'", "'\\''"))
+end
+
+-- for bash, ash and similar shells single-quoted strings are taken
+-- literally except for single quotes (which terminate the string)
+-- (and the exception noted below for dash (-) at the start of a
+-- command line parameter).
+function shellsqescape(value)
+ local res
+ res, _ = string.gsub(value, "'", "'\\''")
+ return res
+end
+
+-- bash, ash and other similar shells interpret a dash (-) at the start
+-- of a command-line parameters as an option indicator regardless of
+-- whether it is inside a single-quoted string. It must be backlash
+-- escaped to resolve this. This requires in some funky special-case
+-- handling. It may actually be a property of the getopt function
+-- rather than the shell proper.
+function shellstartsqescape(value)
+ res, _ = string.gsub(value, "^%-", "\\-")
+ return shellsqescape(res)
+end
+
+-- containing the resulting substrings. The optional max parameter specifies
+-- the number of bytes to process, regardless of the actual length of the given
+-- string. The optional last parameter, regex, specifies whether the separator
+-- sequence is interpreted as regular expression.
+-- pattern as regular expression (optional, default is false)
+function split(str, pat, max, regex)
+ pat = pat or "\n"
+ max = max or #str
+
+ local t = {}
+ local c = 1
+
+ if #str == 0 then
+ return {""}
+ end
+
+ if #pat == 0 then
+ return nil
+ end
+
+ if max == 0 then
+ return str
+ end
+
+ repeat
+ local s, e = str:find(pat, c, not regex)
+ max = max - 1
+ if s and max < 0 then
+ t[#t+1] = str:sub(c)
+ else
+ t[#t+1] = str:sub(c, s and s - 1)
+ end
+ c = e and e + 1 or #str + 1
+ until not s or max < 0
+
+ return t
+end
+
+function trim(str)
+ return (str:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function cmatch(str, pat)
+ local count = 0
+ for _ in str:gmatch(pat) do count = count + 1 end
+ return count
+end
+
+-- one token per invocation, the tokens are separated by whitespace. If the
+-- input value is a table, it is transformed into a string first. A nil value
+-- will result in a valid iterator which aborts with the first invocation.
+function imatch(v)
+ if type(v) == "table" then
+ local k = nil
+ return function()
+ k = next(v, k)
+ return v[k]
+ end
+
+ elseif type(v) == "number" or type(v) == "boolean" then
+ local x = true
+ return function()
+ if x then
+ x = false
+ return tostring(v)
+ end
+ end
+
+ elseif type(v) == "userdata" or type(v) == "string" then
+ return tostring(v):gmatch("%S+")
+ end
+
+ return function() end
+end
+
+-- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
+-- Recognized units are:
+-- o "y" - one year (60*60*24*366)
+-- o "m" - one month (60*60*24*31)
+-- o "w" - one week (60*60*24*7)
+-- o "d" - one day (60*60*24)
+-- o "h" - one hour (60*60)
+-- o "min" - one minute (60)
+-- o "kb" - one kilobyte (1024)
+-- o "mb" - one megabyte (1024*1024)
+-- o "gb" - one gigabyte (1024*1024*1024)
+-- o "kib" - one si kilobyte (1000)
+-- o "mib" - one si megabyte (1000*1000)
+-- o "gib" - one si gigabyte (1000*1000*1000)
+function parse_units(ustr)
+
+ local val = 0
+
+ -- unit map
+ local map = {
+ -- date stuff
+ y = 60 * 60 * 24 * 366,
+ m = 60 * 60 * 24 * 31,
+ w = 60 * 60 * 24 * 7,
+ d = 60 * 60 * 24,
+ h = 60 * 60,
+ min = 60,
+
+ -- storage sizes
+ kb = 1024,
+ mb = 1024 * 1024,
+ gb = 1024 * 1024 * 1024,
+
+ -- storage sizes (si)
+ kib = 1000,
+ mib = 1000 * 1000,
+ gib = 1000 * 1000 * 1000
+ }
+
+ -- parse input string
+ for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
+
+ local num = spec:gsub("[^0-9%.]+$","")
+ local spn = spec:gsub("^[0-9%.]+", "")
+
+ if map[spn] or map[spn:sub(1,1)] then
+ val = val + num * ( map[spn] or map[spn:sub(1,1)] )
+ else
+ val = val + num
+ end
+ end
+
+
+ return val
+end
+
+-- also register functions above in the central string class for convenience
+string.split = split
+string.trim = trim
+string.cmatch = cmatch
+string.parse_units = parse_units
+
+
+function append(src, ...)
+ for i, a in ipairs({...}) do
+ if type(a) == "table" then
+ for j, v in ipairs(a) do
+ src[#src+1] = v
+ end
+ else
+ src[#src+1] = a
+ end
+ end
+ return src
+end
+
+function combine(...)
+ return append({}, ...)
+end
+
+function contains(table, value)
+ for k, v in pairs(table) do
+ if value == v then
+ return k
+ end
+ end
+ return false
+end
+
+-- Both table are - in fact - merged together.
+function update(t, updates)
+ for k, v in pairs(updates) do
+ t[k] = v
+ end
+end
+
+function keys(t)
+ local keys = { }
+ if t then
+ for k, _ in kspairs(t) do
+ keys[#keys+1] = k
+ end
+ end
+ return keys
+end
+
+function clone(object, deep)
+ local copy = {}
+
+ for k, v in pairs(object) do
+ if deep and type(v) == "table" then
+ v = clone(v, deep)
+ end
+ copy[k] = v
+ end
+
+ return setmetatable(copy, getmetatable(object))
+end
+
+
+-- Serialize the contents of a table value.
+function _serialize_table(t, seen)
+ assert(not seen[t], "Recursion detected.")
+ seen[t] = true
+
+ local data = ""
+ local idata = ""
+ local ilen = 0
+
+ for k, v in pairs(t) do
+ if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
+ k = serialize_data(k, seen)
+ v = serialize_data(v, seen)
+ data = data .. ( #data > 0 and ", " or "" ) ..
+ '[' .. k .. '] = ' .. v
+ elseif k > ilen then
+ ilen = k
+ end
+ end
+
+ for i = 1, ilen do
+ local v = serialize_data(t[i], seen)
+ idata = idata .. ( #idata > 0 and ", " or "" ) .. v
+ end
+
+ return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
+end
+
+-- with loadstring().
+function serialize_data(val, seen)
+ seen = seen or setmetatable({}, {__mode="k"})
+
+ if val == nil then
+ return "nil"
+ elseif type(val) == "number" then
+ return val
+ elseif type(val) == "string" then
+ return "%q" % val
+ elseif type(val) == "boolean" then
+ return val and "true" or "false"
+ elseif type(val) == "function" then
+ return "loadstring(%q)" % get_bytecode(val)
+ elseif type(val) == "table" then
+ return "{ " .. _serialize_table(val, seen) .. " }"
+ else
+ return '"[unhandled data type:' .. type(val) .. ']"'
+ end
+end
+
+function restore_data(str)
+ return loadstring("return " .. str)()
+end
+
+
+--
+-- Byte code manipulation routines
+--
+
+-- will be stripped before it is returned.
+function get_bytecode(val)
+ local code
+
+ if type(val) == "function" then
+ code = string.dump(val)
+ else
+ code = string.dump( loadstring( "return " .. serialize_data(val) ) )
+ end
+
+ return code -- and strip_bytecode(code)
+end
+
+-- numbers and debugging numbers will be discarded. Original version by
+-- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+function strip_bytecode(code)
+ local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
+ local subint
+ if endian == 1 then
+ subint = function(code, i, l)
+ local val = 0
+ for n = l, 1, -1 do
+ val = val * 256 + code:byte(i + n - 1)
+ end
+ return val, i + l
+ end
+ else
+ subint = function(code, i, l)
+ local val = 0
+ for n = 1, l, 1 do
+ val = val * 256 + code:byte(i + n - 1)
+ end
+ return val, i + l
+ end
+ end
+
+ local function strip_function(code)
+ local count, offset = subint(code, 1, size)
+ local stripped = { string.rep("\0", size) }
+ local dirty = offset + count
+ offset = offset + count + int * 2 + 4
+ offset = offset + int + subint(code, offset, int) * ins
+ count, offset = subint(code, offset, int)
+ for n = 1, count do
+ local t
+ t, offset = subint(code, offset, 1)
+ if t == 1 then
+ offset = offset + 1
+ elseif t == 4 then
+ offset = offset + size + subint(code, offset, size)
+ elseif t == 3 then
+ offset = offset + num
+ elseif t == 254 or t == 9 then
+ offset = offset + lnum
+ end
+ end
+ count, offset = subint(code, offset, int)
+ stripped[#stripped+1] = code:sub(dirty, offset - 1)
+ for n = 1, count do
+ local proto, off = strip_function(code:sub(offset, -1))
+ stripped[#stripped+1] = proto
+ offset = offset + off - 1
+ end
+ offset = offset + subint(code, offset, int) * int + int
+ count, offset = subint(code, offset, int)
+ for n = 1, count do
+ offset = offset + subint(code, offset, size) + size + int * 2
+ end
+ count, offset = subint(code, offset, int)
+ for n = 1, count do
+ offset = offset + subint(code, offset, size) + size
+ end
+ stripped[#stripped+1] = string.rep("\0", int * 3)
+ return table.concat(stripped), offset
+ end
+
+ return code:sub(1,12) .. strip_function(code:sub(13,-1))
+end
+
+
+--
+-- Sorting iterator functions
+--
+
+function _sortiter( t, f )
+ local keys = { }
+
+ local k, v
+ for k, v in pairs(t) do
+ keys[#keys+1] = k
+ end
+
+ local _pos = 0
+
+ table.sort( keys, f )
+
+ return function()
+ _pos = _pos + 1
+ if _pos <= #keys then
+ return keys[_pos], t[keys[_pos]], _pos
+ end
+ end
+end
+
+-- the provided callback function.
+function spairs(t,f)
+ return _sortiter( t, f )
+end
+
+-- The table pairs are sorted by key.
+function kspairs(t)
+ return _sortiter( t )
+end
+
+-- The table pairs are sorted by value.
+function vspairs(t)
+ return _sortiter( t, function (a,b) return t[a] < t[b] end )
+end
+
+
+--
+-- System utility functions
+--
+
+function bigendian()
+ return string.byte(string.dump(function() end), 7) == 0
+end
+
+function exec(command)
+ local pp = io.popen(command)
+ local data = pp:read("*a")
+ pp:close()
+
+ return data
+end
+
+function execi(command)
+ local pp = io.popen(command)
+
+ return pp and function()
+ local line = pp:read()
+
+ if not line then
+ pp:close()
+ end
+
+ return line
+ end
+end
+
+-- Deprecated
+function execl(command)
+ local pp = io.popen(command)
+ local line = ""
+ local data = {}
+
+ while true do
+ line = pp:read()
+ if (line == nil) then break end
+ data[#data+1] = line
+ end
+ pp:close()
+
+ return data
+end
+
+
+local ubus_codes = {
+ "INVALID_COMMAND",
+ "INVALID_ARGUMENT",
+ "METHOD_NOT_FOUND",
+ "NOT_FOUND",
+ "NO_DATA",
+ "PERMISSION_DENIED",
+ "TIMEOUT",
+ "NOT_SUPPORTED",
+ "UNKNOWN_ERROR",
+ "CONNECTION_FAILED"
+}
+
+local function ubus_return(...)
+ if select('#', ...) == 2 then
+ local rv, err = select(1, ...), select(2, ...)
+ if rv == nil and type(err) == "number" then
+ return nil, err, ubus_codes[err]
+ end
+ end
+
+ return ...
+end
+
+function ubus(object, method, data, path, timeout)
+ if not _ubus_connection then
+ _ubus_connection = _ubus.connect(path, timeout)
+ assert(_ubus_connection, "Unable to establish ubus connection")
+ end
+
+ if object and method then
+ if type(data) ~= "table" then
+ data = { }
+ end
+ return ubus_return(_ubus_connection:call(object, method, data))
+ elseif object then
+ return _ubus_connection:signatures(object)
+ else
+ return _ubus_connection:objects()
+ end
+end
+
+function serialize_json(x, cb)
+ local js = json.stringify(x)
+ if type(cb) == "function" then
+ cb(js)
+ else
+ return js
+ end
+end
+
+
+function libpath()
+ return require "nixio.fs".dirname(ldebug.__file__)
+end
+
+function checklib(fullpathexe, wantedlib)
+ local fs = require "nixio.fs"
+ local haveldd = fs.access('/usr/bin/ldd')
+ local haveexe = fs.access(fullpathexe)
+ if not haveldd or not haveexe then
+ return false
+ end
+ local libs = exec(string.format("/usr/bin/ldd %s", shellquote(fullpathexe)))
+ if not libs then
+ return false
+ end
+ for k, v in ipairs(split(libs)) do
+ if v:find(wantedlib) then
+ return true
+ end
+ end
+ return false
+end
+
+-------------------------------------------------------------------------------
+-- Coroutine safe xpcall and pcall versions
+--
+-- Encapsulates the protected calls with a coroutine based loop, so errors can
+-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
+-- yielding inside the call to pcall or xpcall.
+--
+-- Authors: Roberto Ierusalimschy and Andre Carregal
+-- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
+--
+-- Copyright 2005 - Kepler Project
+--
+-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+-- Implements xpcall with coroutines
+-------------------------------------------------------------------------------
+local coromap = setmetatable({}, { __mode = "k" })
+
+local function handleReturnValue(err, co, status, ...)
+ if not status then
+ return false, err(debug.traceback(co, (...)), ...)
+ end
+ if coroutine.status(co) == 'suspended' then
+ return performResume(err, co, coroutine.yield(...))
+ else
+ return true, ...
+ end
+end
+
+function performResume(err, co, ...)
+ return handleReturnValue(err, co, coroutine.resume(co, ...))
+end
+
+local function id(trace, ...)
+ return trace
+end
+
+function coxpcall(f, err, ...)
+ local current = coroutine.running()
+ if not current then
+ if err == id then
+ return pcall(f, ...)
+ else
+ if select("#", ...) > 0 then
+ local oldf, params = f, { ... }
+ f = function() return oldf(unpack(params)) end
+ end
+ return xpcall(f, err)
+ end
+ else
+ local res, co = pcall(coroutine.create, f)
+ if not res then
+ local newf = function(...) return f(...) end
+ co = coroutine.create(newf)
+ end
+ coromap[co] = current
+ coxpt[co] = coxpt[current] or current or 0
+ return performResume(err, co, ...)
+ end
+end
+
+function copcall(f, ...)
+ return coxpcall(f, id, ...)
+end
diff --git a/libs/luci-lib-base/luasrc/util.luadoc b/libs/luci-lib-base/luasrc/util.luadoc
new file mode 100644
index 0000000000..3168d9c220
--- /dev/null
+++ b/libs/luci-lib-base/luasrc/util.luadoc
@@ -0,0 +1,419 @@
+---[[
+LuCI utility functions.
+]]
+module "luci.util"
+
+---[[
+Create a Class object (Python-style object model).
+
+The class object can be instantiated by calling itself.
+Any class functions or shared parameters can be attached to this object.
+Attaching a table to the class object makes this table shared between
+all instances of this class. For object parameters use the __init__ function.
+Classes can inherit member functions and values from a base class.
+Class can be instantiated by calling them. All parameters will be passed
+to the __init__ function of this class - if such a function exists.
+The __init__ function must be used to set any object parameters that are not shared
+with other objects of this class. Any return values will be ignored.
+
+@class function
+@name class
+@param base The base class to inherit from (optional)
+@return A class object
+@see instanceof
+@see clone
+]]
+
+---[[
+Test whether the given object is an instance of the given class.
+
+@class function
+@name instanceof
+@param object Object instance
+@param class Class object to test against
+@return Boolean indicating whether the object is an instance
+@see class
+@see clone
+]]
+
+---[[
+Create a new or get an already existing thread local store associated with
+the current active coroutine.
+
+A thread local store is private a table object
+whose values can't be accessed from outside of the running coroutine.
+
+@class function
+@name threadlocal
+@return Table value representing the corresponding thread local store
+]]
+
+---[[
+Write given object to stderr.
+
+@class function
+@name perror
+@param obj Value to write to stderr
+@return Boolean indicating whether the write operation was successful
+]]
+
+---[[
+Recursively dumps a table to stdout, useful for testing and debugging.
+
+@class function
+@name dumptable
+@param t Table value to dump
+@param maxdepth Maximum depth
+@return Always nil
+]]
+
+---[[
+Create valid XML PCDATA from given string.
+
+This is just a compatibility wrapper for luci.xml.padata()
+
+@class function
+@name pcdata
+@param value String value containing the data to escape
+@return String value containing the escaped data
+@see luci.xml.pcdata
+]]
+
+---[[
+Decode an URL-encoded string - optionally decoding the "+" sign to space.
+
+@class function
+@name urldecode
+@param str Input string in x-www-urlencoded format
+@param decode_plus Decode "+" signs to spaces if true (optional)
+@return The decoded string
+@see urlencode
+]]
+
+---[[
+URL-encode given string.
+
+@class function
+@name urlencode
+@param str String to encode
+@return String containing the encoded data
+@see urldecode
+]]
+
+---[[
+Strip HTML tags from given string.
+
+This is just a compatibility wrapper for luci.xml.striptags()
+
+@class function
+@name striptags
+@param value String containing the HTML text
+@return String with HTML tags stripped of
+@see luci.xml.striptags
+]]
+
+---[[
+Safely quote value for use in shell commands.
+
+@class function
+@name shellquote
+@param value String containing the value to quote
+@return Single-quote enclosed string with embedded quotes escaped
+]]
+
+---[[
+Splits given string on a defined separator sequence and return a table
+containing the resulting substrings.
+
+The optional max parameter specifies the number of bytes to process,
+regardless of the actual length of the given string. The optional last
+parameter, regex, specifies whether the separator sequence is
+nterpreted as regular expression.
+
+@class function
+@name split
+@param str String value containing the data to split up
+@param pat String with separator pattern (optional, defaults to "\n")
+@param max Maximum times to split (optional)
+@param regex Boolean indicating whether to interpret the separator
+-- pattern as regular expression (optional, default is false)
+@return Table containing the resulting substrings
+]]
+
+---[[
+Remove leading and trailing whitespace from given string value.
+
+@class function
+@name trim
+@param str String value containing whitespace padded data
+@return String value with leading and trailing space removed
+]]
+
+---[[
+Count the occurrences of given substring in given string.
+
+@class function
+@name cmatch
+@param str String to search in
+@param pattern String containing pattern to find
+@return Number of found occurrences
+]]
+
+---[[
+Return a matching iterator for the given value.
+
+The iterator will return one token per invocation, the tokens are separated by
+whitespace. If the input value is a table, it is transformed into a string first.
+A nil value will result in a valid iterator which aborts with the first invocation.
+
+@class function
+@name imatch
+@param val The value to scan (table, string or nil)
+@return Iterator which returns one token per call
+]]
+
+---[[
+Parse certain units from the given string and return the canonical integer
+value or 0 if the unit is unknown.
+
+Upper- or lower case is irrelevant.
+Recognized units are:
+
+-- o "y" - one year (60*60*24*366)
+ o "m" - one month (60*60*24*31)
+ o "w" - one week (60*60*24*7)
+ o "d" - one day (60*60*24)
+ o "h" - one hour (60*60)
+ o "min" - one minute (60)
+ o "kb" - one kilobyte (1024)
+ o "mb" - one megabyte (1024*1024)
+ o "gb" - one gigabyte (1024*1024*1024)
+ o "kib" - one si kilobyte (1000)
+ o "mib" - one si megabyte (1000*1000)
+ o "gib" - one si gigabyte (1000*1000*1000)
+
+@class function
+@name parse_units
+@param ustr String containing a numerical value with trailing unit
+@return Number containing the canonical value
+]]
+
+---[[
+Appends numerically indexed tables or single objects to a given table.
+
+@class function
+@name append
+@param src Target table
+@param ... Objects to insert
+@return Target table
+]]
+
+---[[
+Combines two or more numerically indexed tables and single objects into one table.
+
+@class function
+@name combine
+@param tbl1 Table value to combine
+@param tbl2 Table value to combine
+@param ... More tables to combine
+@return Table value containing all values of given tables
+]]
+
+---[[
+Checks whether the given table contains the given value.
+
+@class function
+@name contains
+@param table Table value
+@param value Value to search within the given table
+@return Number indicating the first index at which the given value occurs
+-- within table or false.
+]]
+
+---[[
+Update values in given table with the values from the second given table.
+
+Both table are - in fact - merged together.
+
+@class function
+@name update
+@param t Table which should be updated
+@param updates Table containing the values to update
+@return Always nil
+]]
+
+---[[
+Retrieve all keys of given associative table.
+
+@class function
+@name keys
+@param t Table to extract keys from
+@return Sorted table containing the keys
+]]
+
+---[[
+Clones the given object and return it's copy.
+
+@class function
+@name clone
+@param object Table value to clone
+@param deep Boolean indicating whether to do recursive cloning
+@return Cloned table value
+]]
+
+---[[
+Recursively serialize given data to lua code, suitable for restoring
+with loadstring().
+
+@class function
+@name serialize_data
+@param val Value containing the data to serialize
+@return String value containing the serialized code
+@see restore_data
+@see get_bytecode
+]]
+
+---[[
+Restore data previously serialized with serialize_data().
+
+@class function
+@name restore_data
+@param str String containing the data to restore
+@return Value containing the restored data structure
+@see serialize_data
+@see get_bytecode
+]]
+
+---[[
+Return the current runtime bytecode of the given data. The byte code
+will be stripped before it is returned.
+
+@class function
+@name get_bytecode
+@param val Value to return as bytecode
+@return String value containing the bytecode of the given data
+]]
+
+---[[
+Strips unnecessary lua bytecode from given string.
+
+Information like line numbers and debugging numbers will be discarded.
+Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+
+@class function
+@name strip_bytecode
+@param code String value containing the original lua byte code
+@return String value containing the stripped lua byte code
+]]
+
+---[[
+Return a key, value iterator which returns the values sorted according to
+the provided callback function.
+
+@class function
+@name spairs
+@param t The table to iterate
+@param f A callback function to decide the order of elements
+@return Function value containing the corresponding iterator
+]]
+
+---[[
+Return a key, value iterator for the given table.
+
+The table pairs are sorted by key.
+
+@class function
+@name kspairs
+@param t The table to iterate
+@return Function value containing the corresponding iterator
+]]
+
+---[[
+Return a key, value iterator for the given table.
+
+The table pairs are sorted by value.
+
+@class function
+@name vspairs
+@param t The table to iterate
+@return Function value containing the corresponding iterator
+]]
+
+---[[
+Test whether the current system is operating in big endian mode.
+
+@class function
+@name bigendian
+@return Boolean value indicating whether system is big endian
+]]
+
+---[[
+Execute given commandline and gather stdout.
+
+@class function
+@name exec
+@param command String containing command to execute
+@return String containing the command's stdout
+]]
+
+---[[
+Return a line-buffered iterator over the output of given command.
+
+@class function
+@name execi
+@param command String containing the command to execute
+@return Iterator
+]]
+
+---[[
+Issue an ubus call.
+
+@class function
+@name ubus
+@param object String containing the ubus object to call
+@param method String containing the ubus method to call
+@param values Table containing the values to pass
+@return Table containin the ubus result
+]]
+
+---[[
+Convert data structure to JSON
+
+@class function
+@name serialize_json
+@param data The data to serialize
+@param writer A function to write a chunk of JSON data (optional)
+@return String containing the JSON if called without write callback
+]]
+
+---[[
+Returns the absolute path to LuCI base directory.
+
+@class function
+@name libpath
+@return String containing the directory path
+]]
+
+---[[
+This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
+
+@class function
+@name coxpcall
+@param f Lua function to be called protected
+@param err Custom error handler
+@param ... Parameters passed to the function
+@return A boolean whether the function call succeeded and the return
+-- values of either the function or the error handler
+]]
+
+---[[
+This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
+
+@class function
+@name copcall
+@param f Lua function to be called protected
+@param ... Parameters passed to the function
+@return A boolean whether the function call succeeded and the returns
+-- values of the function or the error object
+]]
+
diff --git a/libs/luci-lib-httpclient/Makefile b/libs/luci-lib-httpclient/Makefile
index b8bd428b28..9c28c35d81 100644
--- a/libs/luci-lib-httpclient/Makefile
+++ b/libs/luci-lib-httpclient/Makefile
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=HTTP(S) client library
-LUCI_DEPENDS:=+luci-base +luci-lib-nixio +luci-lib-httpprotoutils
+LUCI_DEPENDS:=+luci-lib-base +luci-lib-nixio +luci-lib-httpprotoutils
include ../../luci.mk
diff --git a/libs/luci-lib-httpprotoutils/Makefile b/libs/luci-lib-httpprotoutils/Makefile
index 851a362eb7..95f45d2002 100644
--- a/libs/luci-lib-httpprotoutils/Makefile
+++ b/libs/luci-lib-httpprotoutils/Makefile
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=HTTP protocol utility functions
-LUCI_DEPENDS:=+luci-base
+LUCI_DEPENDS:=+luci-lib-base
include ../../luci.mk
diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c
index 10d11a365b..9b157da265 100644
--- a/libs/luci-lib-ip/src/ip.c
+++ b/libs/luci-lib-ip/src/ip.c
@@ -837,6 +837,12 @@ static int cidr_tolinklocal(lua_State *L)
p2->bits = AF_BITS(AF_INET6);
p2->addr.u8[0] = 0xFE;
p2->addr.u8[1] = 0x80;
+ p2->addr.u8[2] = 0x00;
+ p2->addr.u8[3] = 0x00;
+ p2->addr.u8[4] = 0x00;
+ p2->addr.u8[5] = 0x00;
+ p2->addr.u8[6] = 0x00;
+ p2->addr.u8[7] = 0x00;
p2->addr.u8[8] = p1->addr.u8[0] ^ 0x02;
p2->addr.u8[9] = p1->addr.u8[1];
p2->addr.u8[10] = p1->addr.u8[2];
diff --git a/libs/luci-lib-nixio/src/Makefile b/libs/luci-lib-nixio/src/Makefile
index a7e9a77d99..893656c256 100644
--- a/libs/luci-lib-nixio/src/Makefile
+++ b/libs/luci-lib-nixio/src/Makefile
@@ -51,7 +51,7 @@ ifeq ($(OS),SunOS)
endif
ifneq (,$(findstring MINGW,$(OS))$(findstring mingw,$(OS))$(findstring Windows,$(OS)))
- NIXIO_CROSS_CC:=$(shell which i586-mingw32msvc-cc)
+ NIXIO_CROSS_CC:=$(shell command -v i586-mingw32msvc-cc)
ifneq (,$(NIXIO_CROSS_CC))
CC:=$(NIXIO_CROSS_CC)
endif
diff --git a/libs/rpcd-mod-luci/Makefile b/libs/rpcd-mod-luci/Makefile
index 63b6faac73..7909d4a7b3 100644
--- a/libs/rpcd-mod-luci/Makefile
+++ b/libs/rpcd-mod-luci/Makefile
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rpcd-mod-luci
-PKG_VERSION:=20191114
+PKG_VERSION:=20210614
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
PKG_LICENSE:=Apache-2.0
@@ -48,8 +48,7 @@ endef
define Package/rpcd-mod-luci/postinst
#!/bin/sh
-killall -HUP rpcd 2>/dev/null
-exit 0
+[ -n "$$IPKG_INSTROOT" ] || /etc/init.d/rpcd reload
endef
$(eval $(call BuildPackage,rpcd-mod-luci))
diff --git a/libs/rpcd-mod-luci/src/luci.c b/libs/rpcd-mod-luci/src/luci.c
index 1a1524d49e..eb6f58803e 100644
--- a/libs/rpcd-mod-luci/src/luci.c
+++ b/libs/rpcd-mod-luci/src/luci.c
@@ -31,6 +31,7 @@
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/types.h>
+#include <netinet/in.h>
#include <netinet/ether.h>
#include <linux/rtnetlink.h>
#include <linux/if_packet.h>
@@ -222,6 +223,14 @@ readstr(const char *fmt, ...)
return data;
}
+static bool
+ea_empty(struct ether_addr *ea)
+{
+ struct ether_addr empty = { 0 };
+
+ return !memcmp(ea, &empty, sizeof(empty));
+}
+
static char *
ea2str(struct ether_addr *ea)
{
@@ -285,7 +294,7 @@ duid2ea(const char *duid)
switch (len) {
case 28:
if (!strncmp(duid, "00010001", 8))
- p = duid + 8;
+ p = duid + 16;
break;
@@ -315,9 +324,12 @@ duid2ea(const char *duid)
static struct {
- FILE *dnsmasq_file;
- FILE *odhcpd_file;
time_t now;
+ size_t num, off;
+ struct {
+ FILE *fh;
+ bool odhcpd;
+ } *files;
} lease_state = { };
struct lease_entry {
@@ -333,13 +345,41 @@ struct lease_entry {
} addr[10];
};
-static char *
-find_leasefile(struct uci_context *uci, const char *section)
+static bool
+add_leasefile(const char *path, bool is_odhcpd)
+{
+ void *ptr;
+ FILE *fh;
+
+ fh = fopen(path, "r");
+
+ if (!fh)
+ return false;
+
+ ptr = realloc(lease_state.files, sizeof(*lease_state.files) * (lease_state.num + 1));
+
+ if (!ptr) {
+ fclose(fh);
+
+ return false;
+ }
+
+ lease_state.files = ptr;
+ lease_state.files[lease_state.num].fh = fh;
+ lease_state.files[lease_state.num].odhcpd = is_odhcpd;
+ lease_state.num++;
+
+ return true;
+}
+
+static bool
+find_leasefiles(struct uci_context *uci, bool is_odhcpd)
{
struct uci_ptr ptr = { .package = "dhcp" };
struct uci_package *pkg = NULL;
struct uci_section *s;
struct uci_element *e;
+ bool found = false;
pkg = uci_lookup_package(uci, ptr.package);
@@ -353,7 +393,7 @@ find_leasefile(struct uci_context *uci, const char *section)
uci_foreach_element(&pkg->sections, e) {
s = uci_to_section(e);
- if (strcmp(s->type, section))
+ if (strcmp(s->type, is_odhcpd ? "odhcpd" : "dnsmasq"))
continue;
ptr.flags = 0;
@@ -370,31 +410,30 @@ find_leasefile(struct uci_context *uci, const char *section)
if (ptr.o->type != UCI_TYPE_STRING)
continue;
- return ptr.o->v.string;
+ if (add_leasefile(ptr.o->v.string, is_odhcpd))
+ found = true;
}
- return NULL;
+ return found;
}
static void
lease_close(void)
{
- if (lease_state.dnsmasq_file) {
- fclose(lease_state.dnsmasq_file);
- lease_state.dnsmasq_file = NULL;
- }
+ while (lease_state.num > 0)
+ fclose(lease_state.files[--lease_state.num].fh);
- if (lease_state.odhcpd_file) {
- fclose(lease_state.odhcpd_file);
- lease_state.odhcpd_file = NULL;
- }
+ free(lease_state.files);
+
+ lease_state.files = NULL;
+ lease_state.num = 0;
+ lease_state.off = 0;
}
static void
lease_open(void)
{
struct uci_context *uci;
- char *p;
lease_close();
@@ -405,11 +444,11 @@ lease_open(void)
lease_state.now = time(NULL);
- p = find_leasefile(uci, "dnsmasq");
- lease_state.dnsmasq_file = fopen(p ? p : "/tmp/dhcp.leases", "r");
+ if (!find_leasefiles(uci, false))
+ add_leasefile("/tmp/dhcp.leases", false);
- p = find_leasefile(uci, "odhcpd");
- lease_state.odhcpd_file = fopen(p ? p : "/tmp/hosts/odhcpd", "r");
+ if (!find_leasefiles(uci, true))
+ add_leasefile("/tmp/hosts/odhcpd", true);
uci_free_context(uci);
}
@@ -422,135 +461,128 @@ lease_next(void)
char *p;
int n;
- memset(&e, 0, sizeof(e));
+ while (lease_state.off < lease_state.num) {
+ memset(&e, 0, sizeof(e));
- if (lease_state.dnsmasq_file) {
- while (fgets(e.buf, sizeof(e.buf), lease_state.dnsmasq_file)) {
- p = strtok(e.buf, " \t\n");
+ while (fgets(e.buf, sizeof(e.buf), lease_state.files[lease_state.off].fh)) {
+ if (lease_state.files[lease_state.off].odhcpd) {
+ strtok(e.buf, " \t\n"); /* # */
+ strtok(NULL, " \t\n"); /* iface */
- if (!p)
- continue;
+ e.duid = strtok(NULL, " \t\n"); /* duid */
- n = strtol(p, NULL, 10);
+ if (!e.duid)
+ continue;
- if (n > lease_state.now)
- e.expire = n - lease_state.now;
- else if (n > 0)
- e.expire = 0;
- else
- e.expire = -1;
+ p = strtok(NULL, " \t\n"); /* iaid */
- p = strtok(NULL, " \t\n");
+ if (p)
+ e.af = strcmp(p, "ipv4") ? AF_INET6 : AF_INET;
+ else
+ continue;
- if (!p)
- continue;
+ e.hostname = strtok(NULL, " \t\n"); /* name */
- ea = ether_aton(p);
+ if (!e.hostname)
+ continue;
- p = strtok(NULL, " \t\n");
+ p = strtok(NULL, " \t\n"); /* ts */
- if (p && inet_pton(AF_INET6, p, &e.addr[0].in6)) {
- e.af = AF_INET6;
- e.n_addr = 1;
- }
- else if (p && inet_pton(AF_INET, p, &e.addr[0].in)) {
- e.af = AF_INET;
- e.n_addr = 1;
- }
- else {
- continue;
- }
-
- if (!ea && e.af != AF_INET6)
- continue;
+ if (!p)
+ continue;
- e.hostname = strtok(NULL, " \t\n");
- e.duid = strtok(NULL, " \t\n");
+ n = strtol(p, NULL, 10);
- if (!e.hostname || !e.duid)
- continue;
+ if (n > lease_state.now)
+ e.expire = n - lease_state.now;
+ else if (n >= 0)
+ e.expire = 0;
+ else
+ e.expire = -1;
- if (!strcmp(e.hostname, "*"))
- e.hostname = NULL;
+ strtok(NULL, " \t\n"); /* id */
+ strtok(NULL, " \t\n"); /* length */
- if (!strcmp(e.duid, "*"))
- e.duid = NULL;
+ for (e.n_addr = 0, p = strtok(NULL, "/ \t\n");
+ e.n_addr < ARRAY_SIZE(e.addr) && p != NULL;
+ p = strtok(NULL, "/ \t\n")) {
+ if (inet_pton(e.af, p, &e.addr[e.n_addr].in6))
+ e.n_addr++;
+ }
- if (!ea && e.duid)
ea = duid2ea(e.duid);
- if (ea)
- e.mac = *ea;
-
- return &e;
- }
+ if (ea)
+ e.mac = *ea;
- fclose(lease_state.dnsmasq_file);
- lease_state.dnsmasq_file = NULL;
- }
+ if (!strcmp(e.hostname, "-"))
+ e.hostname = NULL;
- if (lease_state.odhcpd_file) {
- while (fgets(e.buf, sizeof(e.buf), lease_state.odhcpd_file)) {
- strtok(e.buf, " \t\n"); /* # */
- strtok(NULL, " \t\n"); /* iface */
-
- e.duid = strtok(NULL, " \t\n"); /* duid */
+ if (!strcmp(e.duid, "-"))
+ e.duid = NULL;
+ }
+ else {
+ p = strtok(e.buf, " \t\n");
- if (!e.duid)
- continue;
+ if (!p)
+ continue;
- p = strtok(NULL, " \t\n"); /* iaid */
+ n = strtol(p, NULL, 10);
- if (p)
- e.af = strcmp(p, "ipv4") ? AF_INET6 : AF_INET;
- else
- continue;
+ if (n > lease_state.now)
+ e.expire = n - lease_state.now;
+ else if (n > 0)
+ e.expire = 0;
+ else
+ e.expire = -1;
- e.hostname = strtok(NULL, " \t\n"); /* name */
+ p = strtok(NULL, " \t\n");
- if (!e.hostname)
- continue;
+ if (!p)
+ continue;
- p = strtok(NULL, " \t\n"); /* ts */
+ ea = ether_aton(p);
- if (!p)
- continue;
+ p = strtok(NULL, " \t\n");
- n = strtol(p, NULL, 10);
+ if (p && inet_pton(AF_INET6, p, &e.addr[0].in6)) {
+ e.af = AF_INET6;
+ e.n_addr = 1;
+ }
+ else if (p && inet_pton(AF_INET, p, &e.addr[0].in)) {
+ e.af = AF_INET;
+ e.n_addr = 1;
+ }
+ else {
+ continue;
+ }
- if (n > lease_state.now)
- e.expire = n - lease_state.now;
- else if (n >= 0)
- e.expire = 0;
- else
- e.expire = -1;
+ if (!ea && e.af != AF_INET6)
+ continue;
- strtok(NULL, " \t\n"); /* id */
- strtok(NULL, " \t\n"); /* length */
+ e.hostname = strtok(NULL, " \t\n");
+ e.duid = strtok(NULL, " \t\n");
- for (e.n_addr = 0, p = strtok(NULL, "/ \t\n");
- e.n_addr < ARRAY_SIZE(e.addr) && p != NULL;
- p = strtok(NULL, "/ \t\n")) {
- if (inet_pton(e.af, p, &e.addr[e.n_addr].in6))
- e.n_addr++;
- }
+ if (!e.hostname || !e.duid)
+ continue;
- ea = duid2ea(e.duid);
+ if (!strcmp(e.hostname, "*"))
+ e.hostname = NULL;
- if (ea)
- e.mac = *ea;
+ if (!strcmp(e.duid, "*"))
+ e.duid = NULL;
- if (!strcmp(e.hostname, "-"))
- e.hostname = NULL;
+ if (!ea && e.duid)
+ ea = duid2ea(e.duid);
- if (!strcmp(e.duid, "-"))
- e.duid = NULL;
+ if (ea)
+ e.mac = *ea;
+ }
return &e;
}
- fclose(lease_state.odhcpd_file);
- lease_state.odhcpd_file = NULL;
+ lease_state.off++;
}
return NULL;
@@ -636,6 +668,20 @@ rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
if (*p)
blobmsg_add_string(&blob, "master", p);
+ p = strstr(readstr("/sys/class/net/%s/uevent", name), "DEVTYPE=");
+ if (p) {
+ for (n = 0, p += strlen("DEVTYPE=");; n++) {
+ if (p[n] == '\0' || p[n] == '\n') {
+ p[n] = 0;
+ blobmsg_add_string(&blob, "devtype", p);
+ break;
+ }
+ }
+ }
+ else {
+ blobmsg_add_string(&blob, "devtype", "ethernet");
+ }
+
for (af = AF_INET; af != 0; af = (af == AF_INET) ? AF_INET6 : 0) {
a = blobmsg_open_array(&blob,
(af == AF_INET) ? "ipaddrs" : "ip6addrs");
@@ -686,6 +732,24 @@ rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
blobmsg_add_u32(&blob, "ifindex", sll->sll_ifindex);
ifa_flags |= ifa->ifa_flags;
+
+ n = atoi(readstr("/sys/class/net/%s/iflink", name));
+
+ if (n != sll->sll_ifindex) {
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
+ continue;
+
+ sll = (struct sockaddr_ll *)ifa->ifa_addr;
+
+ if (sll->sll_ifindex != n)
+ continue;
+
+ blobmsg_add_string(&blob, "parent", ifa->ifa_name);
+ break;
+ }
+ }
+
break;
}
@@ -710,6 +774,30 @@ rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
blobmsg_add_u8(&blob, "pointtopoint", ifa_flags & IFF_POINTOPOINT);
blobmsg_close_table(&blob, o2);
+ o2 = blobmsg_open_table(&blob, "link");
+
+ p = readstr("/sys/class/net/%s/speed", name);
+ if (*p)
+ blobmsg_add_u32(&blob, "speed", atoi(p));
+
+ p = readstr("/sys/class/net/%s/duplex", name);
+ if (*p)
+ blobmsg_add_string(&blob, "duplex", p);
+
+ n = atoi(readstr("/sys/class/net/%s/carrier", name));
+ blobmsg_add_u8(&blob, "carrier", n == 1);
+
+ n = atoi(readstr("/sys/class/net/%s/carrier_changes", name));
+ blobmsg_add_u32(&blob, "changes", n);
+
+ n = atoi(readstr("/sys/class/net/%s/carrier_up_count", name));
+ blobmsg_add_u32(&blob, "up_count", n);
+
+ n = atoi(readstr("/sys/class/net/%s/carrier_down_count", name));
+ blobmsg_add_u32(&blob, "down_count", n);
+
+ blobmsg_close_table(&blob, o2);
+
blobmsg_close_table(&blob, o);
}
@@ -779,11 +867,19 @@ static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
struct iwinfo_crypto_entry crypto = {};
struct iwinfo_hardware_id ids = {};
const struct iwinfo_ops *iw;
- void *iwlib, *o, *o2, *a;
- int nret;
+ void *iwlib = NULL;
+ void *o, *o2, *a;
+ glob_t paths;
+ int nret, i;
if (!iw_backend || !iw_close || !iw_modenames) {
- iwlib = dlopen("libiwinfo.so", RTLD_LAZY | RTLD_LOCAL);
+ if (glob("/usr/lib/libiwinfo.so*", 0, NULL, &paths) != 0)
+ return false;
+
+ for (i = 0; i < paths.gl_pathc && !iwlib; i++)
+ iwlib = dlopen(paths.gl_pathv[i], RTLD_LAZY | RTLD_LOCAL);
+
+ globfree(&paths);
if (!iwlib)
return false;
@@ -816,6 +912,9 @@ static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
if (!iw->hwmodelist(devname, &nret)) {
a = blobmsg_open_array(buf, "hwmodes");
+ if (nret & IWINFO_80211_AX)
+ blobmsg_add_string(buf, NULL, "ax");
+
if (nret & IWINFO_80211_AC)
blobmsg_add_string(buf, NULL, "ac");
@@ -858,6 +957,18 @@ static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
if (nret & IWINFO_HTMODE_VHT160)
blobmsg_add_string(buf, NULL, "VHT160");
+ if (nret & IWINFO_HTMODE_HE20)
+ blobmsg_add_string(buf, NULL, "HE20");
+
+ if (nret & IWINFO_HTMODE_HE40)
+ blobmsg_add_string(buf, NULL, "HE40");
+
+ if (nret & IWINFO_HTMODE_HE80)
+ blobmsg_add_string(buf, NULL, "HE80");
+
+ if (nret & IWINFO_HTMODE_HE160)
+ blobmsg_add_string(buf, NULL, "HE160");
+
blobmsg_close_array(buf, a);
}
@@ -1072,10 +1183,47 @@ rpc_luci_get_wireless_devices(struct ubus_context *ctx,
struct host_hint {
struct avl_node avl;
char *hostname;
- struct in_addr ip;
- struct in6_addr ip6;
+ struct avl_tree ipaddrs;
+ struct avl_tree ip6addrs;
};
+/* used to ignore priority with avl_find_element */
+#define HOST_HINT_PRIO_IGNORE -1
+
+/* higher (larger) priority addresses are listed first */
+#define HOST_HINT_PRIO_NL 10 /* neighbor table */
+#define HOST_HINT_PRIO_ETHER 50 /* /etc/ethers */
+#define HOST_HINT_PRIO_LEASEFILE 100 /* dhcp leasefile */
+#define HOST_HINT_PRIO_IFADDRS 200 /* getifaddrs() */
+#define HOST_HINT_PRIO_STATIC_LEASE 250 /* uci static leases */
+
+struct host_hint_addr {
+ struct avl_node avl;
+ int af;
+ int prio;
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } addr;
+};
+
+static int
+host_hint_addr_avl_cmp(const void *k1, const void *k2, void *ptr)
+{
+ struct host_hint_addr *a1 = (struct host_hint_addr *)k1;
+ struct host_hint_addr *a2 = (struct host_hint_addr *)k2;
+
+ if (a1->prio != a2->prio &&
+ a1->prio != HOST_HINT_PRIO_IGNORE &&
+ a2->prio != HOST_HINT_PRIO_IGNORE)
+ return a1->prio < a2->prio ? 1 : -1;
+
+ if (a1->af != a2->af)
+ return a1->af < a2->af ? -1 : 1;
+
+ return memcmp(&a1->addr, &a2->addr, sizeof(a1->addr));
+}
+
static int
nl_cb_done(struct nl_msg *msg, void *arg)
{
@@ -1111,12 +1259,69 @@ rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
return NULL;
hint->avl.key = strcpy(p, mac);
+ avl_init(&hint->ipaddrs, host_hint_addr_avl_cmp, false, NULL);
+ avl_init(&hint->ip6addrs, host_hint_addr_avl_cmp, false, NULL);
avl_insert(&rctx->avl, &hint->avl);
}
return hint;
}
+static void
+rpc_luci_add_host_hint_addr(struct host_hint *hint, int af, int prio, void *addr)
+{
+ struct host_hint_addr e, *a;
+ struct avl_tree *addrs = af == AF_INET ? &hint->ipaddrs : &hint->ip6addrs;
+
+ if (!addr)
+ return;
+
+ memset(&e, 0, sizeof(e));
+ e.af = af;
+ /* ignore prio when comparing against existing addresses */
+ e.prio = HOST_HINT_PRIO_IGNORE;
+
+ if (af == AF_INET)
+ memcpy(&e.addr.in, (struct in_addr *)addr, sizeof(e.addr.in));
+ else
+ memcpy(&e.addr.in6, (struct in6_addr *)addr, sizeof(e.addr.in6));
+
+ a = avl_find_element(addrs, &e, a, avl);
+
+ if (a) {
+ /* update prio of existing address if higher */
+ if (prio <= a->prio)
+ return;
+
+ avl_delete(addrs, &a->avl);
+ a->prio = prio;
+ avl_insert(addrs, &a->avl);
+ return;
+ }
+
+ a = calloc(1, sizeof(*a));
+
+ if (!a)
+ return;
+
+ memcpy(a, &e, sizeof(*a));
+ a->prio = prio;
+ a->avl.key = a;
+ avl_insert(addrs, &a->avl);
+}
+
+static void
+rpc_luci_add_host_hint_ipaddr(struct host_hint *hint, int prio, struct in_addr *addr)
+{
+ return rpc_luci_add_host_hint_addr(hint, AF_INET, prio, (void *)addr);
+}
+
+static void
+rpc_luci_add_host_hint_ip6addr(struct host_hint *hint, int prio, struct in6_addr *addr)
+{
+ return rpc_luci_add_host_hint_addr(hint, AF_INET6, prio, (void *)addr);
+}
+
static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
{
struct reply_context *rctx = arg;
@@ -1150,9 +1355,9 @@ static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
return NL_SKIP;
if (nd->ndm_family == AF_INET)
- hint->ip = *(struct in_addr *)dst;
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_NL, (struct in_addr *)dst);
else
- hint->ip6 = *(struct in6_addr *)dst;
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_NL, (struct in6_addr *)dst);
return NL_SKIP;
}
@@ -1235,8 +1440,7 @@ rpc_luci_get_host_hints_ether(struct reply_context *rctx)
continue;
if (inet_pton(AF_INET, p, &in) == 1) {
- if (hint->ip.s_addr == 0)
- hint->ip = in;
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_ETHER, &in);
}
else if (*p && !hint->hostname) {
hint->hostname = strdup(p);
@@ -1252,13 +1456,13 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx)
struct uci_ptr ptr = { .package = "dhcp" };
struct uci_context *uci = NULL;
struct uci_package *pkg = NULL;
- struct in6_addr empty = {};
struct lease_entry *lease;
struct host_hint *hint;
struct uci_element *e, *l;
struct uci_section *s;
struct in_addr in;
char *p, *n;
+ int i;
uci = uci_alloc_context();
@@ -1316,8 +1520,8 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx)
if (!hint)
continue;
- if (hint->ip.s_addr == 0 && in.s_addr != 0)
- hint->ip = in;
+ if (in.s_addr != 0)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_STATIC_LEASE, &in);
if (n && !hint->hostname)
hint->hostname = strdup(n);
@@ -1330,8 +1534,8 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx)
if (!hint)
continue;
- if (hint->ip.s_addr == 0 && in.s_addr != 0)
- hint->ip = in;
+ if (in.s_addr != 0)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_STATIC_LEASE, &in);
if (n && !hint->hostname)
hint->hostname = strdup(n);
@@ -1342,16 +1546,20 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx)
lease_open();
while ((lease = lease_next()) != NULL) {
+ if (ea_empty(&lease->mac))
+ continue;
+
hint = rpc_luci_get_host_hint(rctx, &lease->mac);
if (!hint)
continue;
- if (lease->af == AF_INET && lease->n_addr && hint->ip.s_addr == 0)
- hint->ip = lease->addr[0].in;
- else if (lease->af == AF_INET6 && lease->n_addr &&
- !memcmp(&hint->ip6, &empty, sizeof(empty)))
- hint->ip6 = lease->addr[0].in6;
+ for (i = 0; i < lease->n_addr; i++) {
+ if (lease->af == AF_INET)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_LEASEFILE, &lease->addr[i].in);
+ else if (lease->af == AF_INET6)
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_LEASEFILE, &lease->addr[i].in6);
+ }
if (lease->hostname && !hint->hostname)
hint->hostname = strdup(lease->hostname);
@@ -1367,8 +1575,6 @@ out:
static void
rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
{
- struct ether_addr empty_ea = {};
- struct in6_addr empty_in6 = {};
struct ifaddrs *ifaddr, *ifa;
struct sockaddr_ll *sll;
struct avl_tree devices;
@@ -1424,18 +1630,17 @@ rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
freeifaddrs(ifaddr);
avl_remove_all_elements(&devices, device, avl, nextdevice) {
- if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
- (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
+ if (!ea_empty(&device->ea) &&
+ (!IN6_IS_ADDR_UNSPECIFIED(&device->in6) ||
device->in.s_addr != 0)) {
hint = rpc_luci_get_host_hint(rctx, &device->ea);
if (hint) {
- if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
- hint->ip = device->in;
+ if (device->in.s_addr != 0)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_IFADDRS, &device->in);
- if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
- memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
- hint->ip6 = device->in6;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&device->in6))
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_IFADDRS, &device->in6);
}
}
@@ -1451,6 +1656,7 @@ rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
struct blob_attr *msg)
{
struct reply_context *rctx = req->priv;
+ struct host_hint_addr *addr;
struct host_hint *hint;
struct blob_attr *cur;
struct in6_addr in6;
@@ -1464,23 +1670,23 @@ rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
avl_for_each_element(&rctx->avl, hint, avl) {
- if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
- if (hint->hostname)
+ avl_for_each_element(&hint->ip6addrs, addr, avl) {
+ if (!memcmp(&addr->addr.in6, &in6, sizeof(in6))) {
free(hint->hostname);
-
- hint->hostname = strdup(blobmsg_get_string(cur));
- break;
+ hint->hostname = strdup(blobmsg_get_string(cur));
+ break;
+ }
}
}
}
else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
avl_for_each_element(&rctx->avl, hint, avl) {
- if (!memcmp(&hint->ip, &in, sizeof(in))) {
- if (hint->hostname)
+ avl_for_each_element(&hint->ipaddrs, addr, avl) {
+ if (addr->addr.in.s_addr == in.s_addr) {
free(hint->hostname);
-
- hint->hostname = strdup(blobmsg_get_string(cur));
- break;
+ hint->hostname = strdup(blobmsg_get_string(cur));
+ break;
+ }
}
}
}
@@ -1493,10 +1699,10 @@ rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
static void
rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
{
- struct in6_addr empty_in6 = {};
char buf[INET6_ADDRSTRLEN];
struct blob_buf req = {};
struct host_hint *hint;
+ struct host_hint_addr *addr;
int n = 0;
void *a;
@@ -1505,15 +1711,19 @@ rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
a = blobmsg_open_array(&req, "addrs");
avl_for_each_element(&rctx->avl, hint, avl) {
- if (hint->ip.s_addr != 0) {
- inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
- blobmsg_add_string(&req, NULL, buf);
- n++;
+ avl_for_each_element(&hint->ipaddrs, addr, avl) {
+ if (addr->addr.in.s_addr != 0) {
+ inet_ntop(AF_INET, &addr->addr.in, buf, sizeof(buf));
+ blobmsg_add_string(&req, NULL, buf);
+ n++;
+ }
}
- else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
- inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
- blobmsg_add_string(&req, NULL, buf);
- n++;
+ avl_for_each_element(&hint->ip6addrs, addr, avl) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6)) {
+ inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf));
+ blobmsg_add_string(&req, NULL, buf);
+ n++;
+ }
}
}
@@ -1538,24 +1748,40 @@ static int
rpc_luci_get_host_hints_finish(struct reply_context *rctx)
{
struct host_hint *hint, *nexthint;
+ struct host_hint_addr *addr, *nextaddr;
char buf[INET6_ADDRSTRLEN];
struct in6_addr in6 = {};
- struct in_addr in = {};
- void *o;
+ void *o, *a;
avl_remove_all_elements(&rctx->avl, hint, avl, nexthint) {
o = blobmsg_open_table(&rctx->blob, hint->avl.key);
- if (memcmp(&hint->ip, &in, sizeof(in))) {
- inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
- blobmsg_add_string(&rctx->blob, "ipv4", buf);
+ a = blobmsg_open_array(&rctx->blob, "ipaddrs");
+
+ avl_remove_all_elements(&hint->ipaddrs, addr, avl, nextaddr) {
+ if (addr->addr.in.s_addr != 0) {
+ inet_ntop(AF_INET, &addr->addr.in, buf, sizeof(buf));
+ blobmsg_add_string(&rctx->blob, NULL, buf);
+ }
+
+ free(addr);
}
- if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
- inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
- blobmsg_add_string(&rctx->blob, "ipv6", buf);
+ blobmsg_close_array(&rctx->blob, a);
+
+ a = blobmsg_open_array(&rctx->blob, "ip6addrs");
+
+ avl_remove_all_elements(&hint->ip6addrs, addr, avl, nextaddr) {
+ if (memcmp(&addr->addr.in6, &in6, sizeof(in6))) {
+ inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf));
+ blobmsg_add_string(&rctx->blob, NULL, buf);
+ }
+
+ free(addr);
}
+ blobmsg_close_array(&rctx->blob, a);
+
if (hint->hostname)
blobmsg_add_string(&rctx->blob, "name", hint->hostname);
@@ -1596,7 +1822,6 @@ rpc_luci_get_duid_hints(struct ubus_context *ctx, struct ubus_object *obj,
{
struct { struct avl_node avl; } *e, *next;
char s[INET6_ADDRSTRLEN], *p;
- struct ether_addr empty = {};
struct lease_entry *lease;
struct avl_tree avl;
void *o, *a;
@@ -1638,7 +1863,7 @@ rpc_luci_get_duid_hints(struct ubus_context *ctx, struct ubus_object *obj,
if (lease->hostname)
blobmsg_add_string(&blob, "hostname", lease->hostname);
- if (memcmp(&lease->mac, &empty, sizeof(empty)))
+ if (!ea_empty(&lease->mac))
blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
blobmsg_close_table(&blob, o);
@@ -1672,60 +1897,6 @@ rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
return UBUS_STATUS_OK;
}
-static int
-rpc_luci_get_dsl_status(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
-{
- char line[128], *p, *s;
- FILE *cmd;
-
- cmd = popen("/etc/init.d/dsl_control lucistat", "r");
-
- if (!cmd)
- return UBUS_STATUS_NOT_FOUND;
-
- blob_buf_init(&blob, 0);
-
- while (fgets(line, sizeof(line), cmd)) {
- if (strncmp(line, "dsl.", 4))
- continue;
-
- p = strchr(line, '=');
-
- if (!p)
- continue;
-
- s = p + strlen(p) - 1;
-
- while (s >= p && isspace(*s))
- *s-- = 0;
-
- *p++ = 0;
-
- if (!strcmp(p, "nil"))
- continue;
-
- if (isdigit(*p)) {
- blobmsg_add_u32(&blob, line + 4, strtoul(p, NULL, 0));
- }
- else if (*p == '"') {
- s = p + strlen(p) - 1;
-
- if (s >= p && *s == '"')
- *s = 0;
-
- blobmsg_add_string(&blob, line + 4, p + 1);
- }
- }
-
- fclose(cmd);
-
- ubus_send_reply(ctx, req, blob.head);
- return UBUS_STATUS_OK;
-}
-
-
enum {
RPC_L_FAMILY,
__RPC_L_MAX,
@@ -1741,7 +1912,6 @@ rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
struct blob_attr *msg)
{
struct blob_attr *tb[__RPC_L_MAX];
- struct ether_addr emptymac = {};
struct lease_entry *lease;
char s[INET6_ADDRSTRLEN];
int af, family = 0;
@@ -1793,7 +1963,7 @@ rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
if (lease->hostname)
blobmsg_add_string(&blob, "hostname", lease->hostname);
- if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
+ if (!ea_empty(&lease->mac))
blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
if (lease->duid)
@@ -1836,7 +2006,6 @@ rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints),
UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
- UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status),
UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
};
diff --git a/libs/rpcd-mod-rad2-enc/Makefile b/libs/rpcd-mod-rad2-enc/Makefile
index 121e3a274f..1dd61476aa 100644
--- a/libs/rpcd-mod-rad2-enc/Makefile
+++ b/libs/rpcd-mod-rad2-enc/Makefile
@@ -41,8 +41,7 @@ endef
define Package/rpcd-mod-rad2-enc/postinst
#!/bin/sh
-killall -HUP rpcd 2>/dev/null
-exit 0
+[ -n "$$IPKG_INSTROOT" ] || /etc/init.d/rpcd reload
endef
$(eval $(call BuildPackage,rpcd-mod-rad2-enc))
diff --git a/libs/rpcd-mod-rrdns/Makefile b/libs/rpcd-mod-rrdns/Makefile
index f0bf140a87..37f0e04466 100644
--- a/libs/rpcd-mod-rrdns/Makefile
+++ b/libs/rpcd-mod-rrdns/Makefile
@@ -40,8 +40,7 @@ endef
define Package/rpcd-mod-rrdns/postinst
#!/bin/sh
-killall -HUP rpcd 2>/dev/null
-exit 0
+[ -n "$$IPKG_INSTROOT" ] || /etc/init.d/rpcd reload
endef
$(eval $(call BuildPackage,rpcd-mod-rrdns))