module(..., package.seeall)
require("fs")
require("procps")
require("getopts")
require("format")
require("daemoncontrol")
require("validator")
local configfile = "/etc/shorewall/shorewall.conf"
local processname = "shorewall"
local baseurl = "/etc/shorewall/"
local config = {}
local function getloglevels()
local loglevels = {}
for i=1,8 do
table.insert(loglevels,i)
end
return loglevels
end
local function getdetails()
local f,error = io.popen("/sbin/shorewall status")
local fake = f:read("*l")
local fake = f:read("*l")
local programstatus = f:read("*l") or ""
local programstate = f:read("*l") or ""
f:close()
local f,error = io.popen("/sbin/shorewall version")
local programversion = "shorewall-" .. f:read("*l")
f:close()
return programversion,programstatus,programstate
end
local function read_config(file)
local path = baseurl .. file
if not (fs.is_file(path)) then
return {}
end
local filecontent = fs.read_file_as_array(path)
local output = {}
for k,v in pairs(filecontent) do
if not string.find ( v, "^[;#].*" ) then
table.insert(output, v)
end
end
return output
end
---[[
local function addremove_config( addremove, file, value, orgvalue )
filepath = baseurl .. file
local cmdoutput
-- Check if we are about to change a valid filename
local isvalidfile
for k,v in pairs(getfilelist()) do
isvalidfile = true
if (v.value == filepath) then
break
end
isvalidfile = false
end
if not (fs.is_file(filepath)) or not (isvalidfile) then
return false, cfe({
name="model:addremove_config()",
errtxt="'" .. filepath .. "' is not a valid file!",
})
end
if not (type(value) == "table") then
return false, cfe({
name="model:addremove_config()",
errtxt="Value should come as an array!",
})
end
local filecontentarray = fs.read_file_as_array(filepath)
if (addremove == "delete" ) then
local modifyrow
local orgrecordtable = {}
for word in string.gmatch(orgvalue, "%S+") do
table.insert(orgrecordtable, word)
end
for i=1, #filecontentarray do
local recordtable = {}
for word in string.gmatch(filecontentarray[i], "%S+") do
table.insert(recordtable, word)
end
if (table.concat(recordtable) == table.concat(orgrecordtable)) then
modifyrow = i
end
end
if (tonumber(modifyrow)) then
table.remove(filecontentarray, modifyrow)
fs.write_file(filepath, table.concat(filecontentarray, "\n"))
return true, cfe({
name="model:addremove_config()",
descr="* Record was successfully deleted!",
})
else
return false, cfe({
name="model:addremove_config()",
errtxt="Record was not deleted!" ..
"
orgvalue:" .. tostring(orgvalue) ..
"
modifyrow:" .. tostring(modifyrow) ..
"
orgrecordtable:" .. table.concat(orgrecordtable, ";") ..
"
file:" .. tostring(file) ..
"
filecontentarray:" .. table.concat(filecontentarray, ";") ..
""
,
})
end
elseif (addremove == "add" ) then
--Check if such record already exists
for k,v in pairs(filecontentarray) do
if not string.find ( v, "^[;#].*" ) then
local recordtable = {}
for word in string.gmatch(v, "%S+") do
table.insert(recordtable, word)
end
if (table.concat(recordtable) == table.concat(value)) then
return false, cfe({
name="model:addremove_config()",
errtxt="The config already holds this kind of config!",
})
end
end
end
table.insert(filecontentarray, (#filecontentarray), table.concat(value, "\t"))
fs.write_file(filepath, table.concat(filecontentarray, "\n"))
return true, cfe({
name="model:addremove_config()",
descr="* Record was successfully added!",
})
elseif (addremove == "modify" ) then
local modifyrow
local orgrecordtable = {}
for word in string.gmatch(orgvalue, "%S+") do
table.insert(orgrecordtable, word)
end
for i=1, #filecontentarray do
local recordtable = {}
for word in string.gmatch(filecontentarray[i], "%S+") do
table.insert(recordtable, word)
end
if (table.concat(recordtable) == table.concat(orgrecordtable)) then
modifyrow = i
end
end
if (tonumber(modifyrow)) then
table.remove(filecontentarray, modifyrow)
table.insert(filecontentarray, modifyrow, table.concat(value, "\t"))
fs.write_file(filepath, table.concat(filecontentarray, "\n"))
return true, cfe({
name="model:addremove_config()",
descr="* Record was successfully modified!",
})
else
return false, cfe({
name="model:addremove_config()",
errtxt="Record was not modified!"..
"
orgvalue:" .. tostring(orgvalue) ..
"
modifyrow:" .. tostring(modifyrow) ..
"
orgrecordtable:" .. table.concat(orgrecordtable, ";") ..
"
file:" .. tostring(file) ..
"
filecontentarray:" .. table.concat(filecontentarray, ";") ..
""
,
})
end
else
return false, cfe({
name="model:addremove_config()",
errtxt="Wrong usage of this function! Available options are [add|delete|modify]. You chose '" .. addremove .. "'",
})
end
return false, cfe({
name="model:addremove_config()",
errtxt="Something went wrong!",
debug=value,
})
end
--]]
local function autostarts()
local cmd_output_result, cmd_output_error
local cmd = "/sbin/rc_status | egrep '^S' | egrep '" .. processname .."' 2>/dev/null"
local f = io.popen( cmd )
local cmdresult = f:read("*a")
if (cmdresult) and (#cmdresult > 0) then
cmd_output_result = "Process will autostart at next boot (at sequence '" .. string.match(cmdresult,"^%a+(%d%d)") .. "')"
else
cmd_output_error = "Not programmed to autostart"
end
f:close()
return cmd_output_result,cmd_output_error
end
-- ################################################################################
-- PUBLIC FUNCTIONS
function modify_config(self, addremove, file, value, orgvalue )
return addremove_config(addremove, file, value, orgvalue )
end
-- action should be a CFE
function startstop_service ( self, action )
local cmd = action.value
local cmdresult,cmdmessage,cmderror,cmdaction = daemoncontrol.daemoncontrol(processname, cmd)
action.descr=cmdmessage
action.errtxt=cmderror
-- Reporting back (true|false, the original acition)
return cmdresult,action
end
function getconfig()
local config = {}
config.params = cfe({
name = "params",
label="List of parameters",
type="select",
option=read_config("params"),
})
config.params.size=#config.params.option + 1
config.interfaces = cfe({
name = "interfaces",
label="List of interfaces",
type="select",
option=read_config("interfaces"),
})
config.interfaces.size=#config.interfaces.option + 1
config.zones = cfe({
name = "zones",
label="List of zones",
type="select",
option=read_config("zones"),
})
config.zones.size=#config.zones.option + 1
config.policy = cfe({
name = "policy",
label="List of policy",
type="select",
option=read_config("policy"),
})
config.policy.size=#config.policy.option + 1
config.rules = cfe({
name = "rules",
label="List of rules",
type="select",
option=read_config("rules"),
})
config.rules.size=#config.rules.option + 1
return config
end
function getstatus()
local status = {}
local programversion,programstatus,programstate = getdetails()
status.version = cfe({ name = "version",
label="Program version",
value=programversion,
})
status.status = cfe({ name="status",
label="Program status",
value=programstatus,
})
status.state = cfe({ name="state",
label="Program reports",
value=programstate,
})
local autostart_sequense, autostart_errtxt = autostarts()
status.autostart = cfe({ name="autostart",
label="Autostart sequence",
value=autostart_sequense,
errtxt=autostart_errtxt,
})
return status
end
function configcheck ()
local check = {}
local f,err = io.popen("/bin/echo -n '>> Check starts at: ';/bin/date; /bin/echo; /etc/init.d/shorewall check; /bin/echo; /bin/echo -n '>> Check stops at: '; /bin/date;")
local checkresult = f:read("*a")
f:close()
check.checkresult = cfe({ name = "checkresult",
type="longtext",
label="Result of checking config",
value=checkresult,
})
return check
end
function get_defined_zones ()
local output = {}
for k,v in pairs(read_config("zones")) do
table.insert(output, string.match(v, "^%s*(%S*)"))
end
return output
end
function getlogfile ()
local logfile = {}
local cmdaction = "grep Shorewall /var/log/messages"
local f, error = io.popen(cmdaction ,r)
local checkresult = f:read("*a")
f:close()
logfile.checkresult = cfe({ name = "checkresult",
type="longtext",
label="Result of logfiles",
value=checkresult,
})
logfile.filename = cfe({
name="filename",
label="File name",
value=cmdaction,
})
return logfile
end
function getfilelist ()
local filepath = baseurl
local listed_files = {}
local k,v
for name in posix.files(filepath) do
if not string.match(name, "^%.") and not string.match(name, "^Makefile") then
local filedetails = fs.stat(filepath .. name)
table.insert ( listed_files , cfe({name=name, value=filepath .. name, mtime=filedetails.mtime, size=filedetails.size,}) )
end
end
table.sort(listed_files, function (a,b) return (a.name < b.name) end )
return listed_files
end
function getfiledetails(self,search)
local file = {}
local path = nil
--Validate filename
local available_files = getfilelist()
for k,v in pairs(available_files) do
if ( tostring(available_files[k]["value"]) == tostring(search.value) ) then
path = tostring(search.value)
end
end
if not (path) or (path == "") then
file["filename"] = search
file["filename"]["label"] = "File name"
file["filename"]["errtxt"] = "Invalid path!"
return file
end
local filedetails = fs.stat(path)
file["filename"] = cfe({
name="filename",
label="File name",
value=path,
})
file["filesize"] = cfe({
name="filesize",
label="File size",
value=filedetails.size,
})
file["mtime"] = cfe({
name="mtime",
label="File name",
value=filedetails.mtime,
})
file["filecontent"] = cfe({
type="longtext",
name="filecontent",
label="File content",
value=fs.read_file(path),
})
return file
end
-- modifications should be a CFE
function updatefilecontent (self, filetochange)
local path = nil
--Validate filename
local available_files = getfilelist()
for k,v in pairs(available_files) do
if ( tostring(available_files[k]["value"]) == tostring(filetochange.name) ) then
path = tostring(filetochange.name)
end
end
if not (path) then
filetochange.errtxt = "Invalid path!"
return filetochange
end
local file_result,err = fs.write_file(path, format.dostounix(filetochange.value))
return file_result, err
end