94cba8034b
libifconfig_sfp.h provides an API in libifconfig for querying SFP module properties, operational status, and vendor strings, as well as descriptions of the various fields, string conversions, and other useful helpers for implementing user interfaces. SFP module status is obtained by reading registers via an I2C interface. Descriptions of these registers and the values therein have been collected in a Lua table which is used to generate all the boilerplace C headers and source files for accessing these values, their names, and descriptions. The generated code is fully commented and readable. This is the first use of libifconfig in ifconfig itself. For now, the scope remains very limited. Over time, more of ifconfig will be replaced with libifconfig. Some minor changes to the formatting of ifconfig output have been made: - Module memory hex dumps are indented one extra space as a result of using hexdump(3) instead of a bespoke hex dump function. - Media descriptions have an added two-character short-name in parenthesis. - QSFP modules were incorrectly displaying TX bias current as power. Now TX channels display bias current, and this change has been made for both SFP and QSFP modules for consistency. A Lua binding for libifconfig including this functionality is implemented but has not been included in this commit. The plan is for it to be committed after dynamic module loading has been enabled in flua. Reviewed by: kp, melifaro Relnotes: yes Differential Revision: https://reviews.freebsd.org/D25494
653 lines
22 KiB
Lua
653 lines
22 KiB
Lua
-- From lua-resty-template (modified to remove external dependencies)
|
|
--[[
|
|
Copyright (c) 2014 - 2020 Aapo Talvensaari
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
|
list of conditions and the following disclaimer in the documentation and/or
|
|
other materials provided with the distribution.
|
|
|
|
* Neither the name of the {organization} nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
]]--
|
|
-- $FreeBSD$
|
|
|
|
local setmetatable = setmetatable
|
|
local loadstring = loadstring
|
|
local tostring = tostring
|
|
local setfenv = setfenv
|
|
local require = require
|
|
local concat = table.concat
|
|
local assert = assert
|
|
local write = io.write
|
|
local pcall = pcall
|
|
local phase
|
|
local open = io.open
|
|
local load = load
|
|
local type = type
|
|
local dump = string.dump
|
|
local find = string.find
|
|
local gsub = string.gsub
|
|
local byte = string.byte
|
|
local null
|
|
local sub = string.sub
|
|
local var
|
|
|
|
local _VERSION = _VERSION
|
|
local _ENV = _ENV -- luacheck: globals _ENV
|
|
local _G = _G
|
|
|
|
local HTML_ENTITIES = {
|
|
["&"] = "&",
|
|
["<"] = "<",
|
|
[">"] = ">",
|
|
['"'] = """,
|
|
["'"] = "'",
|
|
["/"] = "/"
|
|
}
|
|
|
|
local CODE_ENTITIES = {
|
|
["{"] = "{",
|
|
["}"] = "}",
|
|
["&"] = "&",
|
|
["<"] = "<",
|
|
[">"] = ">",
|
|
['"'] = """,
|
|
["'"] = "'",
|
|
["/"] = "/"
|
|
}
|
|
|
|
local VAR_PHASES
|
|
|
|
local ESC = byte("\27")
|
|
local NUL = byte("\0")
|
|
local HT = byte("\t")
|
|
local VT = byte("\v")
|
|
local LF = byte("\n")
|
|
local SOL = byte("/")
|
|
local BSOL = byte("\\")
|
|
local SP = byte(" ")
|
|
local AST = byte("*")
|
|
local NUM = byte("#")
|
|
local LPAR = byte("(")
|
|
local LSQB = byte("[")
|
|
local LCUB = byte("{")
|
|
local MINUS = byte("-")
|
|
local PERCNT = byte("%")
|
|
|
|
local EMPTY = ""
|
|
|
|
local VIEW_ENV
|
|
if _VERSION == "Lua 5.1" then
|
|
VIEW_ENV = { __index = function(t, k)
|
|
return t.context[k] or t.template[k] or _G[k]
|
|
end }
|
|
else
|
|
VIEW_ENV = { __index = function(t, k)
|
|
return t.context[k] or t.template[k] or _ENV[k]
|
|
end }
|
|
end
|
|
|
|
local newtab
|
|
do
|
|
local ok
|
|
ok, newtab = pcall(require, "table.new")
|
|
if not ok then newtab = function() return {} end end
|
|
end
|
|
|
|
local function enabled(val)
|
|
if val == nil then return true end
|
|
return val == true or (val == "1" or val == "true" or val == "on")
|
|
end
|
|
|
|
local function trim(s)
|
|
return gsub(gsub(s, "^%s+", EMPTY), "%s+$", EMPTY)
|
|
end
|
|
|
|
local function rpos(view, s)
|
|
while s > 0 do
|
|
local c = byte(view, s, s)
|
|
if c == SP or c == HT or c == VT or c == NUL then
|
|
s = s - 1
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return s
|
|
end
|
|
|
|
local function escaped(view, s)
|
|
if s > 1 and byte(view, s - 1, s - 1) == BSOL then
|
|
if s > 2 and byte(view, s - 2, s - 2) == BSOL then
|
|
return false, 1
|
|
else
|
|
return true, 1
|
|
end
|
|
end
|
|
return false, 0
|
|
end
|
|
|
|
local function read_file(path)
|
|
local file, err = open(path, "rb")
|
|
if not file then return nil, err end
|
|
local content
|
|
content, err = file:read "*a"
|
|
file:close()
|
|
return content, err
|
|
end
|
|
|
|
local function load_view(template)
|
|
return function(view, plain)
|
|
if plain == true then return view end
|
|
local path, root = view, template.root
|
|
if root and root ~= EMPTY then
|
|
if byte(root, -1) == SOL then root = sub(root, 1, -2) end
|
|
if byte(view, 1) == SOL then path = sub(view, 2) end
|
|
path = root .. "/" .. path
|
|
end
|
|
return plain == false and assert(read_file(path)) or read_file(path) or view
|
|
end
|
|
end
|
|
|
|
local function load_file(func)
|
|
return function(view) return func(view, false) end
|
|
end
|
|
|
|
local function load_string(func)
|
|
return function(view) return func(view, true) end
|
|
end
|
|
|
|
local function loader(template)
|
|
return function(view)
|
|
return assert(load(view, nil, nil, setmetatable({ template = template }, VIEW_ENV)))
|
|
end
|
|
end
|
|
|
|
local function visit(visitors, content, tag, name)
|
|
if not visitors then
|
|
return content
|
|
end
|
|
|
|
for i = 1, visitors.n do
|
|
content = visitors[i](content, tag, name)
|
|
end
|
|
|
|
return content
|
|
end
|
|
|
|
local function new(template, safe)
|
|
template = template or newtab(0, 26)
|
|
|
|
template._VERSION = "2.0"
|
|
template.cache = {}
|
|
template.load = load_view(template)
|
|
template.load_file = load_file(template.load)
|
|
template.load_string = load_string(template.load)
|
|
template.print = write
|
|
|
|
local load_chunk = loader(template)
|
|
|
|
local caching
|
|
if VAR_PHASES and VAR_PHASES[phase()] then
|
|
caching = enabled(var.template_cache)
|
|
else
|
|
caching = true
|
|
end
|
|
|
|
local visitors
|
|
function template.visit(func)
|
|
if not visitors then
|
|
visitors = { func, n = 1 }
|
|
return
|
|
end
|
|
visitors.n = visitors.n + 1
|
|
visitors[visitors.n] = func
|
|
end
|
|
|
|
function template.caching(enable)
|
|
if enable ~= nil then caching = enable == true end
|
|
return caching
|
|
end
|
|
|
|
function template.output(s)
|
|
if s == nil or s == null then return EMPTY end
|
|
if type(s) == "function" then return template.output(s()) end
|
|
return tostring(s)
|
|
end
|
|
|
|
function template.escape(s, c)
|
|
if type(s) == "string" then
|
|
if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end
|
|
return gsub(s, "[\">/<'&]", HTML_ENTITIES)
|
|
end
|
|
return template.output(s)
|
|
end
|
|
|
|
function template.new(view, layout)
|
|
local vt = type(view)
|
|
|
|
if vt == "boolean" then return new(nil, view) end
|
|
if vt == "table" then return new(view, safe) end
|
|
if vt == "nil" then return new(nil, safe) end
|
|
|
|
local render
|
|
local process
|
|
if layout then
|
|
if type(layout) == "table" then
|
|
render = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
layout.blocks = context.blocks or {}
|
|
layout.view = context.view or EMPTY
|
|
layout:render()
|
|
end
|
|
process = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
layout.blocks = context.blocks or {}
|
|
layout.view = context.view
|
|
return tostring(layout)
|
|
end
|
|
else
|
|
render = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
template.render(layout, context)
|
|
end
|
|
process = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
return template.process(layout, context)
|
|
end
|
|
end
|
|
else
|
|
render = function(self, context)
|
|
return template.render(view, context or self)
|
|
end
|
|
process = function(self, context)
|
|
return template.process(view, context or self)
|
|
end
|
|
end
|
|
|
|
if safe then
|
|
return setmetatable({
|
|
render = function(...)
|
|
local ok, err = pcall(render, ...)
|
|
if not ok then
|
|
return nil, err
|
|
end
|
|
end,
|
|
process = function(...)
|
|
local ok, output = pcall(process, ...)
|
|
if not ok then
|
|
return nil, output
|
|
end
|
|
return output
|
|
end,
|
|
}, {
|
|
__tostring = function(...)
|
|
local ok, output = pcall(process, ...)
|
|
if not ok then
|
|
return ""
|
|
end
|
|
return output
|
|
end })
|
|
end
|
|
|
|
return setmetatable({
|
|
render = render,
|
|
process = process
|
|
}, {
|
|
__tostring = process
|
|
})
|
|
end
|
|
|
|
function template.precompile(view, path, strip, plain)
|
|
local chunk = dump(template.compile(view, nil, plain), strip ~= false)
|
|
if path then
|
|
local file = open(path, "wb")
|
|
file:write(chunk)
|
|
file:close()
|
|
end
|
|
return chunk
|
|
end
|
|
|
|
function template.precompile_string(view, path, strip)
|
|
return template.precompile(view, path, strip, true)
|
|
end
|
|
|
|
function template.precompile_file(view, path, strip)
|
|
return template.precompile(view, path, strip, false)
|
|
end
|
|
|
|
function template.compile(view, cache_key, plain)
|
|
assert(view, "view was not provided for template.compile(view, cache_key, plain)")
|
|
if cache_key == "no-cache" then
|
|
return load_chunk(template.parse(view, plain)), false
|
|
end
|
|
cache_key = cache_key or view
|
|
local cache = template.cache
|
|
if cache[cache_key] then return cache[cache_key], true end
|
|
local func = load_chunk(template.parse(view, plain))
|
|
if caching then cache[cache_key] = func end
|
|
return func, false
|
|
end
|
|
|
|
function template.compile_file(view, cache_key)
|
|
return template.compile(view, cache_key, false)
|
|
end
|
|
|
|
function template.compile_string(view, cache_key)
|
|
return template.compile(view, cache_key, true)
|
|
end
|
|
|
|
function template.parse(view, plain)
|
|
assert(view, "view was not provided for template.parse(view, plain)")
|
|
if plain ~= true then
|
|
view = template.load(view, plain)
|
|
if byte(view, 1, 1) == ESC then return view end
|
|
end
|
|
local j = 2
|
|
local c = {[[
|
|
context=... or {}
|
|
local ___,blocks,layout={},blocks or {}
|
|
local function include(v, c) return template.process(v, c or context) end
|
|
local function echo(...) for i=1,select("#", ...) do ___[#___+1] = tostring(select(i, ...)) end end
|
|
]] }
|
|
local i, s = 1, find(view, "{", 1, true)
|
|
while s do
|
|
local t, p = byte(view, s + 1, s + 1), s + 2
|
|
if t == LCUB then
|
|
local e = find(view, "}}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
c[j] = "___[#___+1]=template.escape("
|
|
c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "{")
|
|
c[j+2] = ")\n"
|
|
j=j+3
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == AST then
|
|
local e = find(view, "*}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
c[j] = "___[#___+1]=template.output("
|
|
c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "*")
|
|
c[j+2] = ")\n"
|
|
j=j+3
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == PERCNT then
|
|
local e = find(view, "%}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if z then
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
i = s
|
|
else
|
|
local n = e + 2
|
|
if byte(view, n, n) == LF then
|
|
n = n + 1
|
|
end
|
|
local r = rpos(view, s - 1)
|
|
if i <= r then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, r))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = visit(visitors, trim(sub(view, p, e - 1)), "%")
|
|
c[j+1] = "\n"
|
|
j=j+2
|
|
s, i = n - 1, n
|
|
end
|
|
end
|
|
elseif t == LPAR then
|
|
local e = find(view, ")}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
local f = visit(visitors, sub(view, p, e - 1), "(")
|
|
local x = find(f, ",", 2, true)
|
|
if x then
|
|
c[j] = "___[#___+1]=include([=["
|
|
c[j+1] = trim(sub(f, 1, x - 1))
|
|
c[j+2] = "]=],"
|
|
c[j+3] = trim(sub(f, x + 1))
|
|
c[j+4] = ")\n"
|
|
j=j+5
|
|
else
|
|
c[j] = "___[#___+1]=include([=["
|
|
c[j+1] = trim(f)
|
|
c[j+2] = "]=])\n"
|
|
j=j+3
|
|
end
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == LSQB then
|
|
local e = find(view, "]}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
c[j] = "___[#___+1]=include("
|
|
c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "[")
|
|
c[j+2] = ")\n"
|
|
j=j+3
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == MINUS then
|
|
local e = find(view, "-}", p, true)
|
|
if e then
|
|
local x, y = find(view, sub(view, s, e + 1), e + 2, true)
|
|
if x then
|
|
local z, w = escaped(view, s)
|
|
if z then
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
i = s
|
|
else
|
|
y = y + 1
|
|
x = x - 1
|
|
if byte(view, y, y) == LF then
|
|
y = y + 1
|
|
end
|
|
local b = trim(sub(view, p, e - 1))
|
|
if b == "verbatim" or b == "raw" then
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = "___[#___+1]=[=["
|
|
c[j+1] = visit(visitors, sub(view, e + 2, x))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
else
|
|
if byte(view, x, x) == LF then
|
|
x = x - 1
|
|
end
|
|
local r = rpos(view, s - 1)
|
|
if i <= r then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, r))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = 'blocks["'
|
|
c[j+1] = b
|
|
c[j+2] = '"]=include[=['
|
|
c[j+3] = visit(visitors, sub(view, e + 2, x), "-", b)
|
|
c[j+4] = "]=]\n"
|
|
j=j+5
|
|
end
|
|
s, i = y - 1, y
|
|
end
|
|
end
|
|
end
|
|
elseif t == NUM then
|
|
local e = find(view, "#}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
e = e + 2
|
|
if byte(view, e, e) == LF then
|
|
e = e + 1
|
|
end
|
|
s, i = e - 1, e
|
|
end
|
|
end
|
|
end
|
|
s = find(view, "{", s + 1, true)
|
|
end
|
|
s = sub(view, i)
|
|
if s and s ~= EMPTY then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, s)
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" -- luacheck: ignore
|
|
return concat(c)
|
|
end
|
|
|
|
function template.parse_file(view)
|
|
return template.parse(view, false)
|
|
end
|
|
|
|
function template.parse_string(view)
|
|
return template.parse(view, true)
|
|
end
|
|
|
|
function template.process(view, context, cache_key, plain)
|
|
assert(view, "view was not provided for template.process(view, context, cache_key, plain)")
|
|
return template.compile(view, cache_key, plain)(context)
|
|
end
|
|
|
|
function template.process_file(view, context, cache_key)
|
|
assert(view, "view was not provided for template.process_file(view, context, cache_key)")
|
|
return template.compile(view, cache_key, false)(context)
|
|
end
|
|
|
|
function template.process_string(view, context, cache_key)
|
|
assert(view, "view was not provided for template.process_string(view, context, cache_key)")
|
|
return template.compile(view, cache_key, true)(context)
|
|
end
|
|
|
|
function template.render(view, context, cache_key, plain)
|
|
assert(view, "view was not provided for template.render(view, context, cache_key, plain)")
|
|
template.print(template.process(view, context, cache_key, plain))
|
|
end
|
|
|
|
function template.render_file(view, context, cache_key)
|
|
assert(view, "view was not provided for template.render_file(view, context, cache_key)")
|
|
template.render(view, context, cache_key, false)
|
|
end
|
|
|
|
function template.render_string(view, context, cache_key)
|
|
assert(view, "view was not provided for template.render_string(view, context, cache_key)")
|
|
template.render(view, context, cache_key, true)
|
|
end
|
|
|
|
if safe then
|
|
return setmetatable({}, {
|
|
__index = function(_, k)
|
|
if type(template[k]) == "function" then
|
|
return function(...)
|
|
local ok, a, b = pcall(template[k], ...)
|
|
if not ok then
|
|
return nil, a
|
|
end
|
|
return a, b
|
|
end
|
|
end
|
|
return template[k]
|
|
end,
|
|
__new_index = function(_, k, v)
|
|
template[k] = v
|
|
end,
|
|
})
|
|
end
|
|
|
|
return template
|
|
end
|
|
|
|
return new()
|