[plain text]

# Twisted, the Framework of Your Internet
# Copyright (C) 2001-2002 Matthew W. Lefkowitz
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""Parser for inetd.conf files

Stability: stable

Maintainer: U{Andrew Bennetts<>}

Future Plans: xinetd configuration file support?

# Various exceptions
class InvalidConfError(Exception):
    """Invalid configuration file"""

class InvalidInetdConfError(InvalidConfError):
    """Invalid inetd.conf file"""

class InvalidServicesConfError(InvalidConfError):
    """Invalid services file"""

class InvalidRPCServicesConfError(InvalidConfError):
    """Invalid rpc services file"""

class UnknownService(Exception):
    """Unknown service name"""

class SimpleConfFile:
    """Simple configuration file parser superclass.

    Filters out comments and empty lines (which includes lines that only 
    contain comments).

    To use this class, override parseLine or parseFields.
    commentChar = '#'
    defaultFilename = None
    def parseFile(self, file=None):
        """Parse a configuration file
        If file is None and self.defaultFilename is set, it will open
        defaultFilename and use it.
        if file is None and self.defaultFilename:
            file = open(self.defaultFilename,'r')
        for line in file.readlines():
            # Strip out comments
            comment = line.find(self.commentChar)
            if comment != -1:
                line = line[:comment]

            # Strip whitespace
            line = line.strip()

            # Skip empty lines (and lines which only contain comments)
            if not line:


    def parseLine(self, line):
        """Override this.
        By default, this will split the line on whitespace and call
        self.parseFields (catching any errors).
        except ValueError:
            raise InvalidInetdConfError, 'Invalid line: ' + repr(line)
    def parseFields(self, *fields):
        """Override this."""

class InetdService:
    """A simple description of an inetd service."""
    name = None
    port = None
    socketType = None
    protocol = None
    wait = None
    user = None
    group = None
    program = None
    programArgs = None
    def __init__(self, name, port, socketType, protocol, wait, user, group,
                 program, programArgs): = name
        self.port = port
        self.socketType = socketType
        self.protocol = protocol
        self.wait = wait
        self.user = user = group
        self.program = program
        self.programArgs = programArgs

class InetdConf(SimpleConfFile):
    """Configuration parser for a traditional UNIX inetd(8)"""

    defaultFilename = '/etc/inetd.conf'
    def __init__(self, knownServices=None): = []
        if knownServices is None:
            knownServices = ServicesConf()
        self.knownServices = knownServices

    def parseFields(self, serviceName, socketType, protocol, wait, user,
                    program, *programArgs):
        """Parse an inetd.conf file.

        Implemented from the description in the Debian inetd.conf man page.
        # Extract user (and optional group)
        user, group = (user.split('.') + [None])[:2]

        # Find the port for a service
        port =, protocol), None)
        if not port and not protocol.startswith('rpc/'):
            # FIXME: Should this be discarded/ignored, rather than throwing
            #        an exception?
                port = int(serviceName)
                serviceName = 'unknown'
                raise UnknownService, "Unknown service: %s (%s)" \
                      % (serviceName, protocol), port, socketType,
                                          protocol, wait, user, group, program,
class ServicesConf(SimpleConfFile):
    """/etc/services parser
    @ivar services: dict mapping service names to (port, protocol) tuples.
    defaultFilename = '/etc/services'

    def __init__(self): = {}

    def parseFields(self, name, portAndProtocol, *aliases):
            port, protocol = portAndProtocol.split('/')
            port = long(port)
            raise InvalidServicesConfError, 'Invalid port/protocol:' + \
                                            repr(portAndProtocol)[(name, protocol)] = port
        for alias in aliases:
  [(alias, protocol)] = port

class RPCServicesConf(SimpleConfFile):
    """/etc/rpc parser

    @ivar dict mapping rpc service names to rpc ports.

    defaultFilename = '/etc/rpc'

    def __init__(self): = {}
    def parseFields(self, name, port, *aliases):
            port = long(port)
            raise InvalidRPCServicesConfError, 'Invalid port:' + repr(port)
                       [name] = port
        for alias in aliases:
  [alias] = port