module (..., package.seeall) -- Load libraries require("modelfunctions") require("fs") require("format") -- Set variables local configfile = "/etc/ntpd.conf" local confdfile = "/etc/conf.d/ntpd" local packagename = "openntpd" local processname = "ntpd" -- ################################################################################ -- LOCAL FUNCTIONS 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 local function validate_config(config) local success = true -- Three of the fields are just lists of IP addresses / hostnames local tags = {"server", "servers", "listen"} for i,tag in ipairs(tags) do local field = config.value[tag] for j,entry in ipairs(field.value) do if string.find(entry, "[^%w.-]") then if not (tag=="listen" and entry=="*") then field.errtxt = "Invalid IP address/hostname" success = false break end end end end return success, config end -- ################################################################################ -- PUBLIC FUNCTIONS function startstop_service(action) return modelfunctions.startstop_service(processname, action) end function getstatus() return modelfunctions.getstatus(processname, packagename, "OpenNTPD Status") end function getstatusdetails() local status = {} status.timechanged = cfe({ value=last_time_change(), label="Previous time adjustment" }) status.date = cfe({ value=os.date(), label="Current time" }) return cfe({ type="group", value=status, label="OpenNTPD Detailed Status" }) end function read_config () local config = {} config.server = cfe({ type="list", value={}, label="Single servers", descr="List of server IP addresses/hostnames. OpenNTPD will attempt to synchronize to one resolved address for each hostname entry." }) config.servers = cfe({ type="list", value={}, label="Multiple servers", descr="List of server IP addresses/hostnames. OpenNTPD will attempt to synchronize to all resolved addresses for each hostname entry." }) config.listen = cfe({ type="list", value={}, label="Addresses to listen on", descr="List of IP addresses/hostnames to listen on. '*' means listen on all local addresses." }) config.setstimeonstartup = cfe({ type="boolean", value=false, label="Set time on startup" }) local conf = format.parse_linesandwords(fs.read_file(configfile) or "") for i,line in ipairs(conf) do if line[1] == "server" then table.insert(config.server.value, line[2]) elseif line[1] == "servers" then table.insert(config.servers.value, line[2]) elseif line[1] == "listen" and line[2] == "on" then table.insert(config.listen.value, line[3]) end end local opts = string.sub(format.parse_ini_file(fs.read_file(confdfile) or "", "", "NTPD_OPTS") or "", 2, -2) if format.opts_to_table(opts, "-s") then config.setstimeonstartup.value = true end return cfe({ type="group", value=config, label="OpenNTPD Config" }) end function update_config(config) local success, config = validate_config(config) if success then local reverseserver = {} for i,val in ipairs(config.value.server.value) do reverseserver[val] = i end local reverseservers = {} for i,val in ipairs(config.value.servers.value) do reverseservers[val] = i end local reverselisten = {} for i,val in ipairs(config.value.listen.value) do reverselisten[val] = i end local configcontent = string.gsub(fs.read_file(configfile) or "", "\n+$", "") local configlines = format.parse_linesandwords(configcontent) for i=#configlines,1,-1 do if configlines[i][1] == "server" then if reverseserver[configlines[i][2]] then reverseserver[configlines[i][2]] = nil else configcontent = format.replace_line(configcontent, configlines[i].linenum) end elseif configlines[i][1] == "servers" then if reverseservers[configlines[i][2]] then reverseservers[configlines[i][2]] = nil else configcontent = format.replace_line(configcontent, configlines[i].linenum) end elseif configlines[i][1] == "listen" and configlines[i][2] == "on" then if reverselisten[configlines[i][3]] then reverselisten[configlines[i][3]] = nil else configcontent = format.replace_line(configcontent, configlines[i].linenum) end end end -- Then add the missing ones lines = {configcontent} for entry in pairs(reverselisten) do lines[#lines+1] = "listen on "..entry end for entry in pairs(reverseserver) do lines[#lines+1] = "server "..entry end for entry in pairs(reverseservers) do lines[#lines+1] = "servers "..entry end fs.write_file(configfile, table.concat(lines, "\n")) -- Finally, handle setstimeonstartup local opts = {} if config.value.setstimeonstartup.value then opts["-s"] = "" end fs.write_file(confdfile, format.update_ini_file(fs.read_file(confdfile) or "", "", "NTPD_OPTS", '"'..format.table_to_opts(opts)..'"')) else config.errtxt = "Failed to save config" end return config end function get_filedetails() return modelfunctions.getfiledetails(configfile) end function update_filedetails(filedetails) return modelfunctions.setfiledetails(filedetails, {configfile}) end