module (..., package.seeall) -- Load libraries require("format") require("fs") require("procps") require("getopts") require("daemoncontrol") require("processinfo") -- Set variables local configfile = "/etc/ntpd.conf" local confdfile = "/etc/conf.d/ntpd" local packagename = "openntpd" local processname = "ntpd" -- ################################################################################ -- LOCAL FUNCTIONS -- This function is used to get config_content. local function config_content( f ) local config = {} config.name = f local conf_file = fs.read_file_as_array ( config.name ) for i=1,table.maxn(conf_file) do local l = conf_file[i] -- Filter out commented lines if not string.find ( l, "^[;#].*" ) then local a,b,c = string.match ( l, "^%s*(%S+)%s*(%S*)%s*(%S*).*$" ) if (a) then if not (config[string.lower(a)]) then config[string.lower(a)] = {} end if (string.lower(a) == "listen") then table.insert (config[string.lower(a)], {value=c} ) else table.insert (config[string.lower(a)], {value=b} ) end end end end return config end local function last_time_change() local cmdoutput = {} local cmd, error = io.popen("cat /var/log/messages | grep ntpd | grep adjusting | tail -1" ,r) local cmdoutput1,cmdoutput2 = string.match(cmd:read("*a"), "^%s*(%S+%s+%S+%s+%S+%s+).*: (.*)$") cmd:close() cmdoutput1 = cmdoutput1 or "(Have no data on updates)" cmdoutput2 = cmdoutput2 or "" return cmdoutput1 .. cmdoutput2 end -- This function needs: -- addremove = [add|remove] -- file = Path to the file -- variable = e.g. "NTPD_OPTS" -- option = What value to look for to add or remove from the variable. local function addremove_opts( addremove, file, variable, option ) if (string.lower(addremove) == "remove" ) then cmdtxt = "/bin/sed -i 's/\\(" .. variable .. ".*\\)" .. option .. "/\\1/' " .. file local cmd, error = io.popen ( cmdtxt ) local cmdoutput = cmd:read("*a") cmd:close() -- Cleanup the variable by removing unneccesary blanks cmdtxt = "/bin/sed -i 's/\\\"\\ /\\\"/g' " .. file cmdtxt = cmdtxt .. ";/bin/sed -i 's/\\ \\\"/\\\"/g' " .. file local cmd, error = io.popen ( cmdtxt ) cmd:close() elseif (string.lower(addremove) == "add" ) then cmdtxt = "/bin/sed -i 's/\\(" .. variable .. ".*\\)\\\"/\\1" .. option .. " \\\"/' " .. file local cmd, error = io.popen ( cmdtxt ) local cmdoutput = cmd:read("*a") cmd:close() end return cmdtxt end local function addremove_config( addremove, variable, value ) local file = configfile local cmdoutput local filecontentarray = fs.read_file_as_array(file) if (addremove == "delete" ) then if not (value) then return false, cfe({ name="openntpd.model.addremove_config(delete)", errtxt="You need to choose a 'Timeserver host' to delete!", }) end local modifyresult for k,v in pairs(filecontentarray) do if (string.find(v,"^%s*" .. variable .. "%s*" .. value .. "%s*$" )) then modifyresult = k break end end if (modifyresult) then table.remove(filecontentarray,modifyresult) fs.write_file(file, table.concat(filecontentarray, "\n")) return true, cfe({ name="openntpd.model.addremove_config(delete)", descr="* Record was successfully deleted!", }) end -- If nothing happened... then report error! return false, cfe({ name="openntpd.model.addremove_config(delete)", errtxt="Couldn't find the record to delete!", }) elseif (addremove == "add" ) then if not (value) then return false, cfe({ name="openntpd.model.addremove_config(add)", errtxt="You need to enter a value to add!", }) end if not (variable) then return false, cfe({ name="openntpd.model.addremove_config(add)", errtxt="You need to enter a variable for the value to add!", }) end local alreadyexists for k,v in pairs(filecontentarray) do -- if (string.find(v,"^.-%s+" .. value .. "%s*$" )) then if (string.find(v,"^%s*" .. variable .. "%s*" .. value .. "%s*$" )) then alreadyexists = k break end end if (alreadyexists) then return false, cfe({ name="openntpd.model.addremove_config(add)", errtxt="The entered record already exists in the config!", }) end table.insert(filecontentarray, variable .. " " .. value ) fs.write_file(file, table.concat(filecontentarray, "\n")) return true, cfe({ name="openntpd.model.addremove_config(add)", descr="* Record was successfully added!", }) else return false, cfe({ name="openntpd.model.addremove_config()", errtxt="Wrong usage of this function!", }) end return false, cfe({ name="openntpd.model.addremove_config()", errtxt="Something went wrong! You entered '" .. tostring(addremove) .. "' as function.", }) end local function process_status_text(procname) local t = procps.pidof(procname) if (t) and (#t > 0) then return "Enabled" else return "Disabled" end end -- ################################################################################ -- PUBLIC FUNCTIONS -- action should be a CFE function startstop_service ( self, action ) local cmd = action.value local cmdresult,cmdmessage,cmderror,cmdaction = daemoncontrol.daemoncontrol("ntpd", cmd) action.descr=cmdmessage action.errtxt=cmderror -- Reporting back (true|false, the original acition) return cmdresult,action end function modify_config(self, addremove, variable, value) return addremove_config(addremove, variable, value) end function modify_opts (self, addremove, file, variable, opts) -- See to that only *my* configs are modyfied. if (file == configfile) or (file == confdfile) then -- See to that the variable/option only exists once if (addremove == "add") then local remove = addremove_opts( "remove", file, variable, opts ) end local remove = addremove_opts( addremove, file, variable, opts ) end end function getstatus () local path = configfile local status = {} local config = getconfig() if not (fs.is_file(path)) then local file_result,err = fs.write_file(path, "") end local value, errtxt = processinfo.package_version(packagename) status.version = cfe({ name = "version", label="Program version", value=value, errtxt=errtxt, }) status.status = cfe({ name="status", label="Program status", value=process_status_text(processname), }) local autostart_sequense, autostart_errtxt = processinfo.process_botsequence(processname) status.autostart = cfe({ name="autostart", label="Autostart sequence", value=autostart_sequense, errtxt=autostart_errtxt, }) status.date = config.date status.setstimeonstartup = config.setstimeonstartup status.timechanged = config.timechanged return status end function getconfig () local path = configfile local config = {} local configopts = getopts.getoptsfromfile(confdfile) configopts["variables"] = config_content(path) config.setstimeonstartup = cfe({ name = "setstimeonstartup", label="Sets time directly at startup", value = "No", type="checkbox", }) if (getopts.getoptsfromfile(confdfile,"NTPD_OPTS", "-s")) then config.setstimeonstartup.value = "Yes" config.setstimeonstartup.checked = "Yes" end config.cmdsavesetstimeonstartup = cfe({ name = "cmdsavesetstimeonstartup", label="Save the above settings", value = "Save", type="submit", descr="See above checkbox", }) config.timechanged = cfe({ name = "timechanged", label="Previous time adjustment", value = last_time_change(), }) config.date = cfe({ name = "date", label="Current time", value=os.date(), }) local options = {} for k,v in pairs(configopts.variables) do if (type(v) == "table") and ( (string.lower(k) == "servers") or (string.lower(k) == "server")) then local srvtype = "" if (string.lower(k) == "servers") then srvtype = " (pool)" end for k1, v1 in pairs(v) do if (v1.value) then table.insert(options,v1.value..srvtype) end end end end config.hosts_list = cfe({ name = "hosts_list", label="Timeserver hosts", type="select", option=options, size=#options + 1, descr="In most cases you could use pool.ntp.org or [countryname].pool.ntp.org (if listed in http://www.pool.ntp.org/)." }) if (#options < 2) then config.hosts_list.size = 2 end config.hosts_add = cfe({ name = "hosts_add", label="Host to add", }) local options = { cfe({value="1", label="Val1",}), cfe({value="2", label="Val1",}), } config.hosts_type = cfe({ name="hosts_type", label="Type of server", value="2", type="radio", option={ cfe({value="servers", label="Server pool",}), cfe({value="server", label="Single server",}), }, }) local options = {} for k,v in pairs(configopts.variables) do if (type(v) == "table") and (string.lower(k) == "listen") then for k1, v1 in pairs(v) do if (v1.value) then table.insert(options,v1.value) end end end end config.listen_list = cfe({ name = "listen_list", label="Listen on address", type="select", option=options, size=#options + 1, descr="Empty list = Listening (acting as server) is disabled; '*' = Listen on all local addresses.", }) if (#options < 2) then config.listen_list.size = 2 end config.listen_add = cfe({ name = "listen_add", label="New listen address", }) -- DEBUG INFORMATION config.debug = cfe({ name = "debug", label="Debug information", value=configopts, }) return config end function get_logfile () local file = {} local cmdtxt = "cat /var/log/messages | grep ntpd" local cmd, error = io.popen(cmdtxt ,r) local cmdoutput = cmd:read("*a") cmd:close() file["filename"] = cfe({ name="filename", label="File name", value=cmdtxt, }) file["filecontent"] = cfe({ type="longtext", name="filecontent", label="File content", value=cmdoutput, }) return file end function get_filedetails(self,num) local path if (num == "2") then path = configfile2 else path = configfile end local file = {} local filedetails = {} local config = {} local filenameerrtxt if (path) and (fs.is_file(path)) then filedetails = fs.stat(path) config = getconfig(path) else config = {} config.filename = {} config["filename"]["errtxt"]="Config file '".. path .. "' is missing!" end file["filename" .. (num or "")] = cfe({ name="filename" .. (num or ""), label="File name", value=path, errtxt=filenameerrtxt }) file["filesize" .. (num or "")] = cfe({ name="filesize" .. (num or ""), label="File size", value=filedetails.size or 0, }) file["mtime" .. (num or "")] = cfe({ name="mtime" .. (num or ""), label="File date", value=filedetails.mtime or "---", }) file["filecontent" .. (num or "")] = cfe({ type="longtext", name="filecontent" .. (num or ""), label="File content", value=fs.read_file(path), }) -- Sum all errors into one cfe local sumerrors = "" for k,v in pairs(config) do if (config[k]) and (config[k]["errtxt"]) and (config[k]["errtxt"] ~= "") then sumerrors = sumerrors .. config[k]["errtxt"] .. "\n" end end if (sumerrors ~= "") then file["sumerrors" .. (num or "")] = cfe ({ name="sumerrors" .. (num or ""), label = "Configuration errors", errtxt = string.match(sumerrors, "(.-)\n$"), }) end return file end function update_filecontent (self, modifications) local path = configfile local file_result,err = fs.write_file(path, format.dostounix(modifications)) return file_result end