-- acf model for /etc/dhcp/* -- Copyright(c) 2007 A. Brodmann - Licensed under terms of GPL2 module (..., package.seeall) --- get additional libraries require("modelfunctions") validator = require("acf.validator") local subnet = {} local configfile = "/etc/dhcp/dhcpd.conf" local confdfile = "/etc/conf.d/dhcpd" local filelist = {configfile, confdfile} local processname = "dhcpd" local packagename = "dhcp" local leasefile = "/var/lib/dhcp/dhcpd.leases" local config -- ################################################################################ -- LOCAL FUNCTIONS local replacemagiccharacters = function(str) return string.gsub(str, "[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1") end local replaceentry = function(file, configentry, newstring) if newstring then return string.gsub(file, string.gsub(replacemagiccharacters(table.concat(configentry, "\n")), "\n", "%%s+"), replacemagiccharacters(newstring), 1) else return string.gsub(file, "[^\n%S]*"..string.gsub(replacemagiccharacters(table.concat(configentry, "\n")), "\n", "%%s+").."%s*;%s*\n?", "", 1) end end local parseconfigfile = function(file) -- first, remove all comments file = file or "" lines = {} for line in string.gmatch(file, "([^\n]*)\n?") do lines[#lines+1] = string.gsub(line, "#.*$", "") end file = table.concat(lines, " ") -- each line either ends with ';' or with '{'...'}' -- build an array with one entry per statement, each entry having an array of elements, and possibly a subarray local config = {} local stack = {config} local entry for word in string.gmatch(file, "(%S+)") do if word == "{" then if not entry then entry = {} table.insert(stack[#stack], entry) end entry.sub = {} stack[#stack+1] = entry.sub entry = nil elseif word == "}" then stack[#stack] = nil else if not entry then entry = {} table.insert(stack[#stack], entry) entry[1] = word else entry[#entry+1] = word end if string.find(word, ";$") then entry[#entry] = string.sub(word, 1, -2) entry = nil end end end return config end local validate_host = function( host ) local success = true if host.value.host.value == "" or string.find(host.value.host.value, "[^%w.-]") then host.value.host.errtxt = "Invalid host name" success = false end if not validator.is_mac(host.value.mac.value) then host.value.mac.errtxt = "Invalid mac address" success = false end if host.value.addresses.value ~= "" then for address in string.gmatch(host.value.addresses.value, "([^,%s]+),?%s*") do if string.find(address, "[^%w.-]") then host.value.addresses.errtxt = "Invalid domain name / IPv4 address" success = false end end end return success, host end local validate_subnet = function( net ) local success = true if net.value.subnet.value == "" or string.find(net.value.subnet.value, "[^%w.-]") then net.value.subnet.errtxt = "Invalid domain name / IPv4 address" success = false end if not validator.is_ipv4(net.value.netmask.value) then net.value.netmask.errtxt = "Invalid IPv4 address" success = false end if net.value.defleasetime.value ~= "" and not validator.is_integer_in_range(net.value.defleasetime.value, 1800, 86400) then net.value.defleasetime.errtxt = "Lease time must be: 1800 < x < 86400" success = false end if net.value.maxleasetime.value ~= "" and not validator.is_integer_in_range(net.value.maxleasetime.value, 1800, 86400) then net.value.maxleasetime.errtxt = "Lease time must be: 1800 < x < 86400" success = false end if net.value.routers.value ~= "" then for router in string.gmatch(net.value.routers.value, "([^,%s]+),?%s*") do if string.find(router, "[^%w.-]") then net.value.routers.errtxt = "Invalid domain name / IPv4 address" success = false break end end end if string.find(net.value.domainname.value, "[^%w.-]") then net.value.domainname.errtxt = "Invalid domain name" success = false end if net.value.domainnameservers.value ~= "" then for server in string.gmatch(net.value.domainnameservers.value, "([^,%s]+),?%s*") do if string.find(server, "[^%w.-]") then net.value.domainnameservers.errtxt = "Invalid domain name / IPv4 address" success = false end end end if net.value.leaserangestart.value ~= "" and not validator.is_ipv4(net.value.leaserangestart.value) then net.value.leaserangestart.errtxt = "Invalid IPv4 address" success = false end if net.value.leaserangeend.value ~= "" then if not validator.is_ipv4(net.value.leaserangeend.value) then net.value.leaserangeend.errtxt = "Invalid IPv4 address" success = false elseif net.value.leaserangestart.value == "" then net.value.leaserangeend.errtxt = "Cannot define range end without range start" success = false end end success = modelfunctions.validateselect(net.value.unknownclients) and success return success, net end local validate_settings = function ( settings ) local success = modelfunctions.validateselect(settings.value.dnsupdatestyle) if settings.value.defleasetime.value ~= "" and not validator.is_integer_in_range(settings.value.defleasetime.value, 1800, 86400) then settings.value.defleasetime.errtxt = "Out of range 1800 < x < 86400 or not integer" success = false end if settings.value.maxleasetime.value ~= "" and not validator.is_integer_in_range(settings.value.maxleasetime.value, 1800, 86400) then settings.value.maxleasetime.errtxt = "Out of range 1800 < x < 86400 or not integer" success = false end if string.find(settings.value.domainname.value, "[^%w.-]") then settings.value.domainname.errtxt = "Invalid domain name" success = false end if settings.value.domainnameservers.value ~= "" then for server in string.gmatch(settings.value.domainnameservers.value, "([^,%s]+),?%s*") do if string.find(server, "[^%w.-]") then settings.value.domainnameservers.errtxt = "Invalid domain name / IPv4 address" success = false end end end return success, settings end -- Give it the string and the position of the { and it will find the corresponding } local find_section_end = function(file, section_start) local i = section_start+1 local indent = 1 while indent > 0 and i <= #file do local char = string.sub(file, i, i) if char == "}" then indent = indent-1 elseif char == "{" then indent = indent+1 elseif char == "#" then i = string.find(file, "\n", i) elseif char == "'" then i = string.find(file, "'", i) elseif char == '"' then i = string.find(file, '"', i) end i=i+1; end return i-1 end local host_write = function(host) local file = fs.read_file(configfile) or "" config = config or parseconfigfile(file) -- First, add the host line if necessary local hostline = "host "..host.value.host.value local found = false for i,value in ipairs(config or {}) do if value[1] == "host" and value[2] == host.value.host.value then found = true end end if not found then file = file.."\n"..hostline.." {\n}" end -- Now, find the host section local host_start = select(2, string.find(file, replacemagiccharacters(hostline).."%s*{")) local host_end = find_section_end(file, host_start) - 1 host_start = string.find(file, "\n", host_start) + 1 local hostcontent = string.sub(file, host_start, host_end) -- Update the host data host.value.mac.replace = "hardware ethernet "..host.value.mac.value if host.value.addresses.value ~= "" then host.value.addresses.replace = "fixed-address "..host.value.addresses.value end local hostconfig = parseconfigfile(hostcontent) for i,value in ipairs(hostconfig or {}) do if value[1] == "hardware" and value[2] == "ethernet" then hostcontent = replaceentry(hostcontent, value, host.value.mac.replace) host.value.mac.replace = nil elseif value[1] == "fixed-address" then hostcontent = replaceentry(hostcontent, value, host.value.addresses.replace) host.value.addresses.replace = nil end end -- add in new lines at the top if they didn't exist local newlines = {} newlines[#newlines+1] = host.value.mac.replace host.value.mac.replace = nil newlines[#newlines+1] = host.value.addresses.replace host.value.addresses.replace = nil if #newlines > 0 then for i,line in ipairs(newlines) do newlines[i] = " "..line end newlines[#newlines+1] = hostcontent hostcontent = table.concat(newlines, ";\n") end -- The host is updated, put it into the file file = string.sub(file, 1, host_start-1) .. hostcontent .. string.sub(file, host_end+1, -1) -- Finally, write out the new file fs.write_file(configfile, string.gsub(file, "\n*$", "")) config = nil end local subnet_write = function(net) local file = fs.read_file(configfile) or "" config = config or parseconfigfile(file) -- First, add or update the submet line local subnetline = "subnet "..net.value.subnet.value.." netmask "..net.value.netmask.value local found = false for i,value in ipairs(config or {}) do if value[1] == "subnet" and value[2] == net.value.subnet.value then file = replaceentry(file, value, subnetline) found = true end end if not found then file = file.."\n"..subnetline.." {\n}" end -- Now, find the subnet section local subnet_start = select(2, string.find(file, replacemagiccharacters(subnetline).."%s*{")) local subnet_end = find_section_end(file, subnet_start) - 1 subnet_start = string.find(file, "\n", subnet_start) + 1 local subnetcontent = string.sub(file, subnet_start, subnet_end) -- Update the subnet data if net.value.defleasetime.value ~= "" then net.value.defleasetime.replace = "default-lease-time "..net.value.defleasetime.value end if net.value.maxleasetime.value ~= "" then net.value.maxleasetime.replace = "max-lease-time "..net.value.maxleasetime.value end if net.value.routers.value ~= "" then net.value.routers.replace = "option routers "..net.value.routers.value end if net.value.domainname.value ~= "" then net.value.domainname.replace = 'option domain-name "'..net.value.domainname.value..'"' end if net.value.domainnameservers.value ~= "" then net.value.domainnameservers.replace = "option domain-name-servers "..net.value.domainnameservers.value end if net.value.leaserangestart.value ~= "" then net.value.leaserangestart.replace = "range "..net.value.leaserangestart.value end if net.value.leaserangeend.value ~= "" then net.value.leaserangestart.replace = net.value.leaserangestart.replace.." "..net.value.leaserangeend.value end if net.value.unknownclients.value ~= "" then net.value.unknownclients.replace = net.value.unknownclients.value.." unknown-clients" end local subnetconfig = parseconfigfile(subnetcontent) for i,value in ipairs(subnetconfig or {}) do if value[1] == "default-lease-time" then subnetcontent = replaceentry(subnetcontent, value, net.value.defleasetime.replace) net.value.defleasetime.replace = nil elseif value[1] == "max-lease-time" then subnetcontent = replaceentry(subnetcontent, value, net.value.maxleasetime.replace) net.value.maxleasetime.replace = nil elseif value[1] == "option" then if value[2] == "routers" then subnetcontent = replaceentry(subnetcontent, value, net.value.routers.replace) net.value.routers.replace = nil elseif value[2] == "domain-name" then subnetcontent = replaceentry(subnetcontent, value, net.value.domainname.replace) net.value.domainname.replace = nil elseif value[2] == "domain-name-servers" then subnetcontent = replaceentry(subnetcontent, value, net.value.domainnameservers.replace) net.value.domainnameservers.replace = nil end elseif value[1] == "range" then -- We need to steal the dynamic-bootp status if value[2] == "dynamic-bootp" and net.value.leaserangestart.replace then net.value.leaserangestart.replace = string.gsub(net.value.leaserangestart.replace, "range", "%1 "..value[2]) end -- Need to use a pool if unknownclients defined if net.value.unknownclients.replace then subnetcontent = replaceentry(subnetcontent, value) else subnetcontent = replaceentry(subnetcontent, value, net.value.leaserangestart.replace) net.value.leaserangestart.replace = nil end -- We only support one pool per subnet elseif value[1] == "pool" then for x,y in ipairs(value.sub or {}) do if y[2] == "unknown-clients" then subnetcontent = replaceentry(subnetcontent, y, net.value.unknownclients.replace) net.value.unknownclients.replace = nil elseif y[1] == "range" then -- We need to steal the dynamic-bootp status if y[2] == "dynamic-bootp" and net.value.leaserangestart.replace then net.value.leaserangestart.replace = string.gsub(net.value.leaserangestart.replace, "range", "%1 "..y[2]) end subnetcontent = replaceentry(subnetcontent, y, net.value.leaserangestart.replace) net.value.leaserangestart.replace = nil end end if net.value.leaserangestart.replace then subnetcontent = string.gsub(subnetcontent, "(pool%s*{%s*\n)", "%1"..replacemagiccharacters(net.value.leaserangestart.replace)..";\n") net.value.leaserangestart.replace = nil end if net.value.unknownclients.replace then subnetcontent = string.gsub(subnetcontent, "(pool%s*{%s*\n)", "%1"..replacemagiccharacters(net.value.unknownclients.replace)..";\n") net.value.unknownclients.replace = nil end end end -- add in new lines at the top if they didn't exist local newlines = {} newlines[#newlines+1] = net.value.defleasetime.replace net.value.defleasetime.replace = nil newlines[#newlines+1] = net.value.maxleasetime.replace net.value.maxleasetime.replace = nil newlines[#newlines+1] = net.value.routers.replace net.value.routers.replace = nil newlines[#newlines+1] = net.value.domainname.replace net.value.domainname.replace = nil newlines[#newlines+1] = net.value.domainnameservers.replace net.value.domainnameservers.replace = nil if net.value.leaserangestart.replace and not net.value.unknownclients.replace then newlines[#newlines+1] = net.value.leaserangestart.replace net.value.leaserangestart.replace = nil end if #newlines > 0 then for i,line in ipairs(newlines) do newlines[i] = " "..line end newlines[#newlines+1] = subnetcontent subnetcontent = table.concat(newlines, ";\n") end if net.value.unknownclients.replace then local temp = " pool {\n "..net.value.unknownclients.replace..";\n" net.value.unknownclients.replace = nil if net.value.leaserangestart.replace then temp = temp .. " " .. net.value.leaserangestart.replace .. ";\n" net.value.leaserangestart.replace = nil end subnetcontent = subnetcontent .. temp .. " }\n" end -- The subnet is updated, put it into the file file = string.sub(file, 1, subnet_start-1) .. subnetcontent .. string.sub(file, subnet_end+1, -1) -- Finally, write out the new file fs.write_file(configfile, string.gsub(file, "\n*$", "")) config = nil end -- ################################################################################ -- PUBLIC FUNCTIONS function get_startstop(self, clientdata) return modelfunctions.get_startstop(processname) end function startstop_service(self, startstop, action) return modelfunctions.startstop_service(startstop, action) end function getstatus () return modelfunctions.getstatus(processname, packagename, "DHCP Status") end create_new_host = function() host = { host = cfe({ label="Host Name" }), mac = cfe({ label="MAC Address" }), addresses = cfe({ label="Fixed Addresses", descr="Comma-separated addresses" }), } return cfe({ type="group", value=host, label="Host" }) end host_read = function( name ) config = config or parseconfigfile(fs.read_file(configfile) or "") local host = create_new_host() host.value.host.value = name for j,k in ipairs(config) do if k[1] == "host" and k[2] == name then for i,value in ipairs(k.sub or {}) do if value[1] == "hardware" and value[2] == "ethernet" then host.value.mac.value = value[3] or "" elseif value[1] == "fixed-address" then host.value.addresses.value = table.concat(value, " ", 2) end end break end end return host end host_update = function(self, host) local success, host = validate_host( host ) if not host.value.host.errtxt then local previous_success = success success = false host.value.host.errtxt = "This host does not exist" local hosts = get_hosts() for i,ht in ipairs(hosts.value) do if ht == host.value.host.value then success = previous_success host.value.host.errtxt = nil break end end end if success then host_write(host) else host.errtxt = "Failed to update host" end return host end host_create = function(self, host) local success, host = validate_host(host) if not host.value.host.errtxt then local hosts = get_hosts() for i,ht in ipairs(hosts.value) do if ht == host.value.host.value then success = false host.value.host.errtxt = "This host already exists" break end end end if success then host_write(host) else host.errtxt = "Failed to create host" end return host end host_delete = function(name) local file = fs.read_file(configfile) or "" config = config or parseconfigfile(file) local cmdresult = cfe({ value="Failed to delete host - not found", label="Delete host result" }) local hosts = get_hosts() for i,host in ipairs(hosts.value) do if host == name then local start, endd = string.find(file, "host%s*"..replacemagiccharacters(name).."[^{]*{") endd = find_section_end(file, endd) endd = string.find(file, "\n", endd) file = string.sub(file, 1, start-1) .. string.sub(file, endd+1, -1) fs.write_file(configfile, string.gsub(file, "\n*$", "")) config = nil cmdresult.value = "Host Deleted" end end return cmdresult end get_hosts = function () config = config or parseconfigfile(fs.read_file(configfile) or "") local retval = {} for i,entry in ipairs(config) do if string.lower(entry[1] or "") == "host" then table.insert(retval, entry[2]) end end return cfe({ type="list", value=retval, label="Host list" }) end create_new_subnet = function() net = { subnet = cfe({ label="Subnet" }), netmask = cfe({ label="Netmask" }), defleasetime = cfe({ label="Default Lease Time" }), maxleasetime = cfe({ label="Maximum Lease Time" }), routers = cfe({ label="Routers", descr="Comma-separated addresses" }), domainname = cfe({ label="Domainname" }), domainnameservers = cfe({ label="Domain Name Servers", descr="Comma-separated addresses" }), --wpad = cfe({ label="Web Proxy Auto Discovery" }), leaserangestart = cfe({ label="Lease Range Start" }), leaserangeend = cfe({ label="Lease Range End" }), unknownclients = cfe({ type="select", label="Unknown Clients", option={"", "allow", "deny"} }), } return cfe({ type="group", value=net, label="Subnet" }) end subnet_read = function( name ) config = config or parseconfigfile(fs.read_file(configfile) or "") local net = create_new_subnet() net.value.subnet.value = name local pools = 0 local ranges = 0 for j,k in ipairs(config) do if k[1] == "subnet" and k[2] == name then net.value.netmask.value = k[4] or "" for i,value in ipairs(k.sub or {}) do if value[1] == "default-lease-time" then net.value.defleasetime.value = value[2] or "" elseif value[1] == "max-lease-time" then net.value.maxleasetime.value = value[2] or "" elseif value[1] == "option" then if value[2] == "routers" then net.value.routers.value = table.concat(value, " ", 3) elseif value[2] == "domain-name" then net.value.domainname.value = string.sub(value[3] or "", 2, -2) elseif value[2] == "domain-name-servers" then net.value.domainnameservers.value = table.concat(value, " ", 3) --elseif value[2] == "local-wpad-server" then -- net.value.wpad.value = string.sub(value[3] or "", 2, -2) end elseif value[1] == "range" then ranges = ranges + 1 if value[2] == "dynamic-bootp" then net.value.leaserangestart.value = value[3] or "" net.value.leaserangeend.value = value[4] or "" else net.value.leaserangestart.value = value[2] or "" net.value.leaserangeend.value = value[3] or "" end -- We only support one pool per subnet elseif value[1] == "pool" then pools = pools + 1 for x,y in ipairs(value.sub or {}) do if y[2] == "unknown-clients" then net.value.unknownclients.value = y[1] elseif y[1] == "range" then ranges = ranges + 1 if y[2] == "dynamic-bootp" then net.value.leaserangestart.value = y[3] or "" net.value.leaserangeend.value = y[4] or "" else net.value.leaserangestart.value = y[2] or "" net.value.leaserangeend.value = y[3] or "" end end end end end break end end if pools > 1 or ranges > 1 then net.value.subnet.errtxt = "Warning! This subnet contains multiple pool/range definitions. This is not supported by ACF. Saving may break functionality!" end return net end subnet_update = function(self, net) local success, net = validate_subnet( net ) if not net.value.subnet.errtxt then local previous_success = success success = false net.value.subnet.errtxt = "This subnet does not exist" local subnets = get_subnets() for i,subnet in ipairs(subnets.value) do if subnet == net.value.subnet.value then success = previous_success net.value.subnet.errtxt = nil break end end end if success then subnet_write(net) else net.errtxt = "Failed to update subnet" end return net end subnet_create = function(self, net) local success, net = validate_subnet(net) if not net.value.subnet.errtxt then local subnets = get_subnets() for i,subnet in ipairs(subnets.value) do if subnet == net.value.subnet.value then success = false net.value.subnet.errtxt = "This subnet already exists" break end end end if success then subnet_write(net) else net.errtxt = "Failed to create subnet" end return net end subnet_delete = function(name) local file = fs.read_file(configfile) or "" config = config or parseconfigfile(file) local cmdresult = cfe({ value="Failed to delete subnet - not found", label="Delete subnet result" }) local subnets = get_subnets() for i,subnet in ipairs(subnets.value) do if subnet == name then local start, endd = string.find(file, "subnet%s*"..replacemagiccharacters(name).."%s*netmask[^{]*{") endd = find_section_end(file, endd) endd = string.find(file, "\n", endd) file = string.sub(file, 1, start-1) .. string.sub(file, endd+1, -1) fs.write_file(configfile, string.gsub(file, "\n*$", "")) config = nil cmdresult.value = "Subnet Deleted" end end return cmdresult end get_subnets = function () config = config or parseconfigfile(fs.read_file(configfile) or "") local retval = {} for i,entry in ipairs(config) do if string.lower(entry[1] or "") == "subnet" then table.insert(retval, entry[2]) end end return cfe({ type="list", value=retval, label="Subnet list" }) end read_settings = function() config = config or parseconfigfile(fs.read_file(configfile) or "") local settings = {} settings.domainname = cfe({ label="Domain Name" }) settings.domainnameservers = cfe({ label="Domain Name Servers", descr="Comma-separated addresses" }) settings.dnsupdatestyle = cfe({ type="select", label="DNS Update Style", option={"none", "ad-hoc", "interim"} }) settings.defleasetime = cfe({ label="Default Lease Time" }) settings.maxleasetime = cfe({ label="Maximum Lease Time" }) for i,value in ipairs(config) do if value[1] == "option" then if value[2] == "domain-name" then settings.domainname.value = string.sub(value[3] or "", 2, -2) elseif value[2] == "domain-name-servers" then settings.domainnameservers.value = table.concat(value, " ", 3) end elseif value[1] == "default-lease-time" then settings.defleasetime.value = value[2] or "" elseif value[1] == "max-lease-time" then settings.maxleasetime.value = value[2] or "" elseif value[1] == "ddns-update-style" then settings.dnsupdatestyle.value = value[2] or "" end end return cfe({ type="group", value=settings, label = "Global settings" }) end update_settings = function (self, settings) success, settings = validate_settings(settings) if success then local file = fs.read_file(configfile) or "" config = config or parseconfigfile(file) -- set up the lines we want to enter if settings.value.domainname.value ~= "" then settings.value.domainname.replace = 'option domain-name "'..settings.value.domainname.value..'"' end if settings.value.domainnameservers.value ~= "" then settings.value.domainnameservers.replace = "option domain-name-servers "..settings.value.domainnameservers.value end settings.value.dnsupdatestyle.replace = "ddns-update-style "..settings.value.dnsupdatestyle.value if settings.value.defleasetime.value ~= "" then settings.value.defleasetime.replace = "default-lease-time "..settings.value.defleasetime.value end if settings.value.maxleasetime.value ~= "" then settings.value.maxleasetime.replace = "max-lease-time "..settings.value.maxleasetime.value end -- replace existing lines for i,value in ipairs(config) do if value[1] == "option" then if value[2] == "domain-name" then file = replaceentry(file, value, settings.value.domainname.replace) settings.value.domainname.replace = nil elseif value[2] == "domain-name-servers" then file = replaceentry(file, value, settings.value.domainnameservers.replace) settings.value.domainnameservers.replace = nil end elseif value[1] == "ddns-update-style" then file = replaceentry(file, value, settings.value.dnsupdatestyle.replace) settings.value.dnsupdatestyle.replace = nil elseif value[1] == "default-lease-time" then file = replaceentry(file, value, settings.value.defleasetime.replace) settings.value.defleasetime.replace = nil elseif value[1] == "max-lease-time" then file = replaceentry(file, value, settings.value.maxleasetime.replace) settings.value.maxleasetime.replace = nil end end -- add in new lines at the top if they didn't exist local newlines = {} newlines[#newlines+1] = settings.value.domainname.replace settings.value.domainname.replace = nil newlines[#newlines+1] = settings.value.domainnameservers.replace settings.value.domainnameservers.replace = nil newlines[#newlines+1] = settings.value.dnsupdatestyle.replace settings.value.dnsupdatestyle.replace = nil newlines[#newlines+1] = settings.value.defleasetime.replace settings.value.defleasetime.replace = nil newlines[#newlines+1] = settings.value.maxleasetime.replace settings.value.maxleasetime.replace = nil if #newlines > 0 then newlines[#newlines+1] = file file = table.concat(newlines, ";\n") end fs.write_file(configfile, string.gsub(file, "\n*$", "")) config = nil else settings.errtxt = "Failed to update global settings" end return settings end listconfigfiles = function() local listed_files = {} for i,name in ipairs(filelist) do local filedetails = fs.stat(name) or {} table.insert ( listed_files , {filename=name, mtime=filedetails.mtime or "---", filesize=filedetails.size or "0"} ) end table.sort(listed_files, function (a,b) return (a.filename < b.filename) end ) return cfe({ type="list", value=listed_files, label="DHCP File List" }) end getconfigfile = function(filename) return modelfunctions.getfiledetails(filename, filelist) end setconfigfile = function(self, filedetails) return modelfunctions.setfiledetails(self, filedetails, filelist) end getleases = function() return modelfunctions.getfiledetails(leasefile) end