inetdconf.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # -*- test-case-name: twisted.runner.test.test_inetdconf -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Parser for inetd.conf files
  6. """
  7. # Various exceptions
  8. class InvalidConfError(Exception):
  9. """
  10. Invalid configuration file
  11. """
  12. class InvalidInetdConfError(InvalidConfError):
  13. """
  14. Invalid inetd.conf file
  15. """
  16. class InvalidServicesConfError(InvalidConfError):
  17. """
  18. Invalid services file
  19. """
  20. class UnknownService(Exception):
  21. """
  22. Unknown service name
  23. """
  24. class SimpleConfFile:
  25. """
  26. Simple configuration file parser superclass.
  27. Filters out comments and empty lines (which includes lines that only
  28. contain comments).
  29. To use this class, override parseLine or parseFields.
  30. """
  31. commentChar = '#'
  32. defaultFilename = None
  33. def parseFile(self, file=None):
  34. """
  35. Parse a configuration file
  36. If file is None and self.defaultFilename is set, it will open
  37. defaultFilename and use it.
  38. """
  39. close = False
  40. if file is None and self.defaultFilename:
  41. file = open(self.defaultFilename,'r')
  42. close = True
  43. try:
  44. for line in file.readlines():
  45. # Strip out comments
  46. comment = line.find(self.commentChar)
  47. if comment != -1:
  48. line = line[:comment]
  49. # Strip whitespace
  50. line = line.strip()
  51. # Skip empty lines (and lines which only contain comments)
  52. if not line:
  53. continue
  54. self.parseLine(line)
  55. finally:
  56. if close:
  57. file.close()
  58. def parseLine(self, line):
  59. """
  60. Override this.
  61. By default, this will split the line on whitespace and call
  62. self.parseFields (catching any errors).
  63. """
  64. try:
  65. self.parseFields(*line.split())
  66. except ValueError:
  67. raise InvalidInetdConfError('Invalid line: ' + repr(line))
  68. def parseFields(self, *fields):
  69. """
  70. Override this.
  71. """
  72. class InetdService:
  73. """
  74. A simple description of an inetd service.
  75. """
  76. name = None
  77. port = None
  78. socketType = None
  79. protocol = None
  80. wait = None
  81. user = None
  82. group = None
  83. program = None
  84. programArgs = None
  85. def __init__(self, name, port, socketType, protocol, wait, user, group,
  86. program, programArgs):
  87. self.name = name
  88. self.port = port
  89. self.socketType = socketType
  90. self.protocol = protocol
  91. self.wait = wait
  92. self.user = user
  93. self.group = group
  94. self.program = program
  95. self.programArgs = programArgs
  96. class InetdConf(SimpleConfFile):
  97. """
  98. Configuration parser for a traditional UNIX inetd(8)
  99. """
  100. defaultFilename = '/etc/inetd.conf'
  101. def __init__(self, knownServices=None):
  102. self.services = []
  103. if knownServices is None:
  104. knownServices = ServicesConf()
  105. knownServices.parseFile()
  106. self.knownServices = knownServices
  107. def parseFields(self, serviceName, socketType, protocol, wait, user,
  108. program, *programArgs):
  109. """
  110. Parse an inetd.conf file.
  111. Implemented from the description in the Debian inetd.conf man page.
  112. """
  113. # Extract user (and optional group)
  114. user, group = (user.split('.') + [None])[:2]
  115. # Find the port for a service
  116. port = self.knownServices.services.get((serviceName, protocol), None)
  117. if not port and not protocol.startswith('rpc/'):
  118. # FIXME: Should this be discarded/ignored, rather than throwing
  119. # an exception?
  120. try:
  121. port = int(serviceName)
  122. serviceName = 'unknown'
  123. except:
  124. raise UnknownService("Unknown service: %s (%s)" % (
  125. serviceName, protocol))
  126. self.services.append(InetdService(serviceName, port, socketType,
  127. protocol, wait, user, group, program,
  128. programArgs))
  129. class ServicesConf(SimpleConfFile):
  130. """
  131. /etc/services parser
  132. @ivar services: dict mapping service names to (port, protocol) tuples.
  133. """
  134. defaultFilename = '/etc/services'
  135. def __init__(self):
  136. self.services = {}
  137. def parseFields(self, name, portAndProtocol, *aliases):
  138. try:
  139. port, protocol = portAndProtocol.split('/')
  140. port = int(port)
  141. except:
  142. raise InvalidServicesConfError(
  143. 'Invalid port/protocol: %s' % (repr(portAndProtocol),))
  144. self.services[(name, protocol)] = port
  145. for alias in aliases:
  146. self.services[(alias, protocol)] = port