-- acf model for /etc/network/interfaces -- Copyright(c) 2007 N. Angelacos - Licensed under terms of GPL2 module (..., package.seeall) -- iface is a local (private) table with private methods for managing -- the interfaces file. All low-level stuff is done here. It exposes -- the iface.tags, file(raw), and array (parsed), as well as a number -- of functions for managing the interface file. These are in a local -- table because nobody outside the model should know anything about -- the interfaces file (not even where it is - it could be in an LDAP -- directory for all we know) The public module functions are defined -- further below local iface = { tags = { "name", "comment", "method", "address", "netmask", "gateway", "hostname", "provider", "pre-up", "up", "down", "post-down" }, methods = { "dhcp", "static", "ppp", "loopback", "manual" }, -- lowlevel functions will insert file and array here } -- Lowlevel functions - they do things directly to iface. iface.read_file = function () local filename = "/etc/network/interfaces" iface.file = cfe { name=filename, filename=filename } local file = io.open(filename) local rc = false if file then iface.file.value = file:read("*a") -- make sure it has a terminating \n iface.file.value = string.gsub (iface.file.value, "([^\n])$", "%1\n") file:close() rc = true end return rc end iface.write_file = function () local rc = false local file = io.open ( iface.file.name, "w") if file then file:write ( iface.file.value ) file:close() rc = true end return rc end function iface.iface_type ( ) local f = {} for k,v in pairs(iface.tags) do f[v] = cfe { name = v } end for k,v in pairs ({"comment", "pre-up", "up", "down", "post-down"}) do f[v].type ="longtext" end f.method.type = "select" f.method.option = iface.methods return (f) end -- return true or false + the index of iface.array matching "name" function iface.index (name) if name== nil or #name == 0 then return true, 0 end if iface.array == nil then iface.unpack_interfaces () end for k,v in ipairs(iface.array) do if name == v.name.value then return true, k end end return false, 0 end function iface.append ( self, value, prefix ) self = self or "" -- if we already have some values, then append a newline if #self > 0 then self = self .. "\n" end -- strip the prefix local str = string.gsub(value, "^" .. ( prefix or "" ), "") -- and append return self .. str end function iface.expand ( self, prefix ) if #self == 0 then return "" end -- force the string to end in a single linefeed self = string.gsub (self, "\r", "") self = string.gsub (self, "[\n]*$", "\n") local str = "" for line in string.gmatch ( self, ".-\n") do if #line > 0 then str = str .. prefix .. " " .. line end end return str end function iface.unpack_interfaces () if iface.array == nil then iface.read_file() iface.array = {} end -- call it array so we don't have to call it iface.array everywhere local array = iface.array local count = 0 array[count] = iface.iface_type() for line in string.gmatch ( iface.file.value, ".-\n") do -- strip leading spaces, tabs line = string.gsub (line, "^[%s]*", "") line = string.gsub (line, "\n*$", "") -- it can be #, auto, iface, or a parameter if string.match(line, "^#") then array[count].comment.value = iface.append(array[count].comment.value, line , "#%s*" ) elseif string.match(line, "^auto") then -- do nothing elseif string.match(line, "^iface") then count = count + 1 array[count] = iface.iface_type() -- iface [inet | ipx] -- we assume inet array[count].name.value, array[count].method.value = string.match(line, "%w+%s+(%w+)%s+%w+%s+(%w+)") else -- it must be some kind of parameter local param, val = string.match(line, "(%S+)%s*(.*)$") if (param) then array[count][param].value = iface.append (array[count][param].value, val) end end end -- now move the comments to go with the interface for n = count,1,-1 do array[n].comment.value = array[n-1].comment.value end return array end function iface.pack_interfaces() local str = "" for n = 1,#iface.array,1 do local me = iface.array[n] for k,v in pairs (iface.tags) do if v == "comment" then str = str .. "\n" .. iface.expand ( me[v].value, "#") elseif v == "method" then str = str .. string.format ("\nauto %s\niface %s inet %s\n", me.name.value, me.name.value, me.method.value) elseif v == "name" then -- nothing else str = str .. iface.expand( me[v].value, "\t" .. v) end end end return str end function iface.commit() iface.file.value = iface.pack_interfaces() return iface.write_file() end function iface.add_after ( name, def ) -- if the new if.name is already in the table, then fail local rc, idx = iface.index(def.name.value) if idx > 0 then def.name.errtxt = "This interface is already defined" return false, def end -- if the name to insert after doesn't exist, just fail rc, idx = iface.index(name) if rc == false then return false, def end rc, def = iface.validate (def) if rc == false then return rc, def end table.insert( iface.array, idx+1, def ) return iface.commit() , def end function iface.read ( name ) -- if the name is blank, then return an empty def local rc, idx = iface.index(name or "") if name == nil or #name == 0 or rc == false then return rc, iface.iface_type() end return iface.commit(), iface.array[idx] end function iface.update ( def) -- if the def by that name doesn't exist, fail local rc, idx = iface.index(def.name.value or "" ) if idx == 0 then def.name.errtxt = "This is an invalid interface name" return false, def end rc, def = iface.validate ( def ) if rc == false then return rc, def end iface.array[idx] = def return iface.commit(), def end function iface.delete (name ) local rc, idx = iface.index(name or "" ) if idx == 0 then rc = false else table.remove (iface.array, idx ) end return iface.commit() end iface.validate = function ( def ) if #def.name.value == 0 then iface.name.errtxt = "The interface must have a name" end def.method.errtxt = "Method specified is invalid" for k,v in pairs (iface.methods) do if def.method.value == v then def.method.errtxt = nil end end -- More validation tests go here --- -- local rc = true for k,v in pairs(def) do if def[k].errtxt then result = false end end return result, def end ------------------------------------------------------------------------------- -- Public Methods ------------------------------------------------------------------------------- get_all_interfaces = iface.unpack_interfaces get_iface_by_name = iface.read create_iface_by_name = iface.add_after update_iface_by_name = function (def, name ) -- make sure name we think we are updating is name we are updating def.name.value = name return iface.update (def) end delete_iface_by_name = iface.delete