module(..., package.seeall)
require("procps")
require("getopts")
require("fs")
require("format")
local configdir
local datafile
local configfiles = {}
local configitems = {}
local processname = "tinydns"
local configfile = "/etc/conf.d/" .. processname
local initdoptions = getopts.getoptsfromfile_onperline("/etc/init.d/" .. processname)
if (initdoptions) then
configdir = initdoptions.DATADIR
datafile = initdoptions.ROOT .. "/data" or "/var/cache/data"
else
configdir = "/etc/" .. processname
datafile = "/var/cache/data"
end
-- ################################################################################
-- LOCAL FUNCTIONS
local function get_version()
local cmd_output_result, cmd_output_error
local cmd = "/sbin/apk_version -vs " .. processname .." 2>/dev/null"
local f = io.popen( cmd )
local cmdresult = f:read("*l")
if (cmdresult) and (#cmdresult > 0) then
cmd_output_result = string.match(cmdresult,"^%S*") or "Unknown"
else
cmd_output_error = "Program not installed"
end
f:close()
return cmd_output_result,cmd_output_error
end
-- This function could be used to check that valid parameters are used in different places
local function check_signs(sign)
local output = {}
local output = {prefix={
['.']="Name server for your domain",
['&']="Name server",
['=']="Host",
['+']="Alias",
['@']="Mail exchanger",
['=']="Host",
['^']="PTR record",
['C']="Canonical Name",
['Z']="SOA record",
[':']="Generic record",
['%']="Client location",
}}
output = output[sign]
return output
end
-- Return a table with the config-content of a file
-- Commented/Blank lines are ignored
local function get_value_from_file(file)
local output = {}
local filecontent = fs.read_file_as_array(file)
for i=1,table.maxn(filecontent) do
local l = filecontent[i]
if not (string.find ( l, "^[;#].*" )) and not (string.find (l, "^%s*$")) then
table.insert(output, string.match(l,"(.-)%s*$"))
end
end
if (#output > 0) then
return true, output
else
return false, output
end
end
-- Function to recursively inserts all filenames in a dir into an array
local function recursedir(path, filearray)
local k,v
for k,v in pairs(posix.dir(path) or {}) do
-- Ignore files that begins with a '.'
if not string.match(v, "^%.") then
local f = path .. "/" .. v
-- If subfolder exists, list files in this subfolder
if (posix.stat(f).type == "directory") then
recursedir(f, filearray)
else
table.insert(filearray, f)
end
end
end
end
-- Functin to split items into a table
local function split_config_items(orgitem)
local delimiter = ":"
local output = {}
output = format.string_to_table(string.sub(orgitem,1,1) .. ":" .. string.sub(orgitem,2),delimiter)
output.type = check_signs("prefix")
output.type = output.type[string.sub(orgitem,1,1)] or "unknown"
-- for k,v in pairs(cnffile) do
-- local configcontent = get_value_from_file(v)
-- if (configcontent) then
-- table.insert(configfiles, v)
-- end
-- end
return output
end
-- Feed the configfiles table with list of all availage configfiles
local function searchforconfigfiles()
local cnffile = {}
recursedir(configdir, cnffile)
for k,v in pairs(cnffile) do
local configcontent = get_value_from_file(v)
if (configcontent) then
table.insert(configfiles, v)
end
end
-- Debug option (adds the sampleconfig content)
table.insert(configfiles, "/usr/share/acf/app/tinydns/sampleconfig.conf")
end
searchforconfigfiles()
local function recurseoutput(table,cnt)
if not (cnt) then cnt=0 end
cnt = cnt + 1
for k,v in pairs(table or {}) do
if (type(v) == "string") then
io.write("
"..
tostring(v) .. "
")
else
io.write(" "..
tostring(k) .. "
")
recurseoutput(v,cnt)
end
end
end
-- Create table with doman levels
local function recursedomains(t,array,maxn,currnum)
if not (currnum) then currnum = maxn + 1 end
currnum = currnum - 1
-- FIXME: The current level should hold information on previous level too!
if not (currnum == 0) then
if not (array[t[currnum]]) then
array[t[currnum]] = {}
end
recursedomains(t,array[t[currnum]],maxn,currnum)
end
-- FIXME: This is a /really uggly/ hack to return the current table
-- If it's fixed nicely... it would be wonderful!
if (array[t[maxn]]) and
(array[t[maxn]][t[maxn-1]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]][t[maxn-4]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]][t[maxn-4]][t[maxn-5]]) then
return array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]][t[maxn-4]][t[maxn-5]]
end
if (array[t[maxn]]) and
(array[t[maxn]][t[maxn-1]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]][t[maxn-4]]) then
return array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]][t[maxn-4]]
end
if (array[t[maxn]]) and
(array[t[maxn]][t[maxn-1]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]]) then
return array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]]
end
if (array[t[maxn]]) and
(array[t[maxn]][t[maxn-1]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]]) then
return array[t[maxn]][t[maxn-1]][t[maxn-2]][t[maxn-3]]
end
if (array[t[maxn]]) and
(array[t[maxn]][t[maxn-1]]) and
(array[t[maxn]][t[maxn-1]][t[maxn-2]]) then
return array[t[maxn]][t[maxn-1]][t[maxn-2]]
end
if (array[t[maxn]]) and (array[t[maxn]][t[maxn-1]]) then
return array[t[maxn]][t[maxn-1]]
end
if (array[t[maxn]]) then
return array[t[maxn]]
end
end
-- ################################################################################
-- PUBLIC FUNCTIONS
-- Present some general status
function getstatus()
local status = {}
local version,versionerrtxt = get_version()
local config = getconfig()
status.version = cfe({ name = "version",
label="Program version",
value=version,
errtxt=versionerrtxt,
})
status.status = cfe({ name="status",
label="Program status",
value=procps.pidof(processname),
})
status.locations = config.locations
status.domains = config.domains
return status
end
-- Return config-information
function getconfig()
local config = {}
local configobjects = {}
local locations = {}
local domains = {}
local debug
local version,versionerrtxt = get_version()
local listenaddr = getopts.getoptsfromfile_onperline(configfile,"IP") or {}
config.listen = cfe({
name = "listen",
label="IP address to listen on",
value=listenaddr.IP or "",
})
--Loop through all available configfiles
for k,v in pairs(configfiles) do
local filecontent, fileresult
fileresult, filecontent = get_value_from_file(v)
for kk,vv in pairs(filecontent) do
local domaindetails = {}
local filecontent_table = split_config_items(vv)
-- This is mostly for debugging
-- This table contains all available configs
table.insert(configobjects, cfe({
name=vv,
value=vv,
option=filecontent_table,
}))
-- Create a table with location items
-- Containing all objects that start with %
if (filecontent_table[1] == "%") then
if not (locations[filecontent_table[2]]) then
locations[filecontent_table[2]] = {}
end
table.insert(locations[filecontent_table[2]], filecontent_table[3])
end
-- Create domain information tables
local domain, level1, level2, level3, level4, level5, level6, levels
if (filecontent_table[1] == "&") then
local descr=check_signs("prefix")
domain = format.string_to_table(filecontent_table[2], "%.")
domaindetails = cfe ({
name=filecontent_table[2],
label=descr[filecontent_table[1]],
value=filecontent_table[2],
})
end
local value = filecontent_table[2]
local currenttable
if (type(domain) == "table") then
currenttable = recursedomains(domain, domains, table.maxn(domain))
end
-- FIXME: This is where we should put the information into the table!
if (domaindetails.value) then
table.insert (currenttable , domaindetails)
end
--[[
if (type(domain) == "table") then
levels = table.maxn(domain)
level1 = domain[levels]
if (level1) then
level2 = (domain[levels-1] or "unknown") .. "."
end
if (level2) and (domain[levels-2]) then
level3 = domain[levels-2] .. "." .. level2
end
if (level3) and (domain[levels-3]) then
level4 = domain[levels-3] .. "." .. level3
end
if (level4) and (domain[levels-4]) then
level5 = domain[levels-4] .. "." .. level4
end
if (level5) and (domain[levels-5]) then
level6 = domain[levels-5] .. "." .. level5
end
if (level6) and (domain[levels-6]) then
level7 = domain[(levels-6)] .. "." .. level6
end
end
if (level1 ) and (#level1 > 0) and not (domain[levels] == "arpa") then
if not (domains[level1]) then
domains[level1] = {}
end
if (level2) and (#level2 > 0) then
if not (domains[level1][level2]) then
domains[level1][level2] = {}
end
if (level3) and (#level3 > 0) then
if not (domains[level1][level2][level3]) then
domains[level1][level2][level3] = {}
end
if (level4) and (#level4 > 0) then
if not (domains[level1][level2][level3][level4]) then
domains[level1][level2][level3][level4] = {}
end
if (level5) and (#level5 > 0) then
if not (domains[level1][level2][level3][level4][level5]) then
domains[level1][level2][level3][level4][level5] = {}
end
if (level6) and (#level6 > 0) then
if not (domains[level1][level2][level3][level4][level5][level6]) then
domains[level1][level2][level3][level4][level5][level6] = {}
end
if (level7) and (#level7 > 0) then
if not (domains[level1][level2][level3][level4][level5][level6][level7]) then
domains[level1][level2][level3][level4][level5][level6][level7] = {}
domains[level1][level2][level3][level4][level5][level6][level7] = {}
end
else
table.insert(domains[level1][level2][level3][level4][level5][level6], filecontent_table[4])
end --level7
else
table.insert(domains[level1][level2][level3][level4][level5], filecontent_table[4])
end --level6
else
table.insert(domains[level1][level2][level3][level4], filecontent_table[4])
end --level5
else
table.insert(domains[level1][level2][level3], filecontent_table[4])
end --level4
else
table.insert(domains[level1][level2], filecontent_table[4])
end --level3
else
table.insert(domains[level1], filecontent_table[4])
end --level2
end --level1
--]]
end
end
config.locations = cfe({
name="locations",
label="Locations",
value=locations,
})
config.domains = cfe({
name="domains",
label="Domains",
value=domains,
})
-- config.domains = debug
---[[
config.configitems = cfe({
name = "configitems",
label="Config items",
value=configobjects,
})
--]]
return config
end
-- ################################################################################
-- DEBUG INFORMATION (Everything below will be deleted in the future)
function getdebug()
local debug = {}
--[[
local signs = get_available_signs("prefix") or {}
debug.debugprefixes = cfe({
name = "debugprefixes",
label="Available prefixes",
option=signs,
type="select",
size=table.maxn(signs)+1,
})
local signs = get_available_signs("suffix") or {}
debug.debugsuffixes = cfe({
name = "debugsuffixes",
label="Available suffixes",
option=signs,
type="select",
size=table.maxn(signs)+1,
})
--]]
debug.configdir = cfe({
name = "configdir",
label="configdir",
value=configdir,
})
debug.datafile = cfe({
name = "datafile",
label="datafile",
value=datafile,
})
for k,v in pairs(configfiles) do
local cnfcontent
fake, cnfcontent = get_value_from_file(v)
for kk,vv in pairs(cnfcontent) do
table.insert(configitems,vv)
end
end
---[[
debug.configitems = cfe({
name = "configitems",
label="configitems",
option=configitems,
type="select",
})
--]]
debug.configfiles = cfe({
name = "configfiles",
label="configfiles",
option=configfiles,
type="select",
})
return debug
end