module(..., package.seeall) -- Load libraries require("modelfunctions") require("format") require("fs") require("validator") -- Set variables local configfile = "/etc/chrony/chrony.conf" local processname = "chronyd" local packagename = "chrony" local keyfile = "/etc/chrony/chrony.keys" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " -- ################################################################################ -- LOCAL FUNCTIONS function validate_config(config) local success = true for i,val in ipairs(config.value.server.value) do if string.find(val, "[^%w%s.-]") then config.value.server.errtxt = "Invalid entry on line "..i success = false break end end for i,val in ipairs(config.value.allow.value) do if string.find(val, "[^%w%s.-/]") then config.value.allow.errtxt = "Invalid entry on line "..i success = false break end end if not validator.is_valid_filename(config.value.driftfile.value) then config.value.driftfile.errtxt = "Invalid file name" success = false end if not validator.is_valid_filename(config.value.keyfile.value) then config.value.keyfile.errtxt = "Invalid file name" success = false end if config.value.commandkey.value ~= "" and not validator.is_integer(config.value.commandkey.value) then config.value.commandkey.errtxt = "Must be an integer" success = false end return success, config end local function get_keyfilestatus(filedetails) filedetails.value.filecontent.descr = "List of password numbers and passwords (ie. '10 cronpass')" -- check to see if the file is being used filedetails.value.status = cfe({ value="Key file in use", label="Key file status" }) local config = get_config() if config.value.keyfile.value ~= keyfile then filedetails.value.status.value = "" filedetails.value.status.errtxt = "Key file is not in use" end return filedetails end -- ################################################################################ -- PUBLIC FUNCTIONS function startstop_service(action) local result = modelfunctions.startstop_service(processname, action) table.insert(result.value.actions.value, "online") table.insert(result.value.actions.value, "offline") if action then local lower = action:lower() if lower == "online" or lower == "offline" then result.value.result.value = "" result.value.result.errtxt = nil -- try to find the password local config = get_config() if config.value.keyfile.value == "" then result.value.result.errtxt = "No key file defined" elseif config.value.commandkey.value == "" then result.value.result.errtxt = "No command key defined" else local content = fs.read_file(config.value.keyfile.value) or "" local password = string.match("\n"..content, "\n"..config.value.commandkey.value.."%s+(%S+)") if not password then result.value.result.errtxt = "Could not find password in key file" else local cmd = path.."chronyc < 0 then local cmd = path.."chronyc sources" local f = io.popen(cmd) details.sources.value = f:read("*a") or "" f:close() cmd = path.."chronyc sourcestats" f = io.popen(cmd) details.sourcestats.value = f:read("*a") or "" f:close() cmd = path.."chronyc tracking" f = io.popen(cmd) details.tracking.value = f:read("*a") or "" f:close() end return cfe({ type="group", value=details, label="Chrony Status Details" }) end function get_config() local output = {} output.server = cfe({ type="list", value={}, label="Servers", descr="List of NTP servers by name or IP (ie. 0.pool.ntp.org). If infrequent Internet connection, follow name/IP with 'offline'." }) output.allow = cfe({ type="list", value={}, label="Allow", descr="List of allowed clients by name/subnet/IP or 'all'."}) output.driftfile = cfe({ label="Drift File", descr="Name of drift file (ie. /var/log/chrony/chrony.drift)" }) output.keyfile = cfe({ label="Key File", descr="Name of key file (ie. /etc/chrony/chrony.keys)" }) output.commandkey = cfe({ label="Command Key", descr="Number of key in Key File for commands." }) local config = format.parse_linesandwords(fs.read_file(configfile) or "", "[!;#%%]") if config then for i,entry in ipairs(config) do if output[entry[1]] then if type(output[entry[1]].value) == "table" then table.insert(output[entry[1]].value, table.concat(entry, " ", 2)) else output[entry[1]].value = table.concat(entry, " ", 2) end end end end return cfe({ type="group", value=output, label="Chrony Config" }) end function update_config(config) local success, config = validate_config(config) if success then for name,val in pairs(config.value) do if type(val.value) == "table" then if #val.value > 0 then val.line = name.." "..table.concat(val.value, "\n"..name.." ") end else if val.value ~= "" then val.line = name .. " " .. val.value end end end local lines = fs.read_file_as_array(configfile) or {} local conf = format.parse_linesandwords(lines, "[!;#%%]") for i,entry in ipairs(conf) do if config.value[entry[1]] then if config.value[entry[1]].line then lines[entry.linenum] = config.value[entry[1]].line else lines[entry.linenum] = nil end config.value[entry[1]].line = nil end end -- remove the holes in the lines array (sparse array due to removing entries) local newlines = {} for i=1,table.maxn(lines) do table.insert(newlines, lines[i]) end -- add in missing entries to end for name,val in pairs(config.value) do if val.line then newlines[#newlines+1] = val.line val.line = nil end end fs.write_file(configfile, table.concat(newlines, "\n")) else config.errtxt = "Failed to save config" end return config end function get_keyfiledetails() return get_keyfilestatus(modelfunctions.getfiledetails(keyfile)) end function update_keyfiledetails(filedetails) return get_keyfilestatus(modelfunctions.setfiledetails(filedetails, {keyfile})) end function enable_keyfile() local result = cfe({ value="Enabled key file", label="Enable Key file result" }) local config = get_config() config.value.keyfile.value = keyfile config = update_config(config) if config.errtxt then result.value = "" result.errtxt = {config.errtxt} for name,val in pairs(config.value) do if val.errtxt then table.insert(result.errtxt, name.." - "..val.errtxt) end end result.errtxt = table.concat(result.errtxt, "\n") end return result end function get_filedetails() -- FIXME validate return modelfunctions.getfiledetails(configfile) end function update_filedetails(filedetails) -- FIXME validate return modelfunctions.setfiledetails(filedetails, {configfile}) end