# 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
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# 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
#
"""Twisted inetd.
Stability: semi-stable
Maintainer: U{Andrew Bennetts<spiv@twistedmatrix.com>}
Future Plans: Bugfixes. Specifically for UDP and Sun-RPC, which don't work
correctly yet.
"""
import os, pwd, grp, traceback, socket, commands
from twisted.internet.app import Application
from twisted.internet import reactor
from twisted.internet.protocol import Protocol,ServerFactory
from twisted.python import log,usage
from twisted.protocols import wire
import inetdconf
try:
import portmap
rpcOk = 1
except ImportError:
rpcOk = 0
# A dict of known 'internal' services (i.e. those that don't involve spawning
# another process.
internalProtocols = {
'echo': wire.Echo,
'chargen': wire.Chargen,
'discard': wire.Discard,
'daytime': wire.Daytime,
'time': wire.Time,
}
# Protocol map
protocolDict = {'tcp': socket.IPPROTO_TCP, 'udp': socket.IPPROTO_UDP}
def forkPassingFD(exe, args, env, user, group, fdesc):
"""Run exe as a child process, passing fdesc as fd 0.
This will also make sure that fdesc is removed from the parent's reactor.
"""
# This is half-cannibalised from twisted.internet.process.Process
pid = os.fork()
if pid == 0: # Child
try:
# Close stdin/stdout (we keep stderr from the parent to report
# errors with)
for fd in range(2):
os.close(fd)
# Make the socket be fd 0
# (and fd 1, although I'm not sure if that matters)
os.dup(fdesc.fileno())
os.dup(fdesc.fileno())
# Close unused file descriptors
for fd in range(3, 256):
try: os.close(fd)
except: pass
# Set uid/gid
os.setgid(group)
os.setuid(user)
# Start the new process
os.execvpe(exe, args, env)
except:
# If anything goes wrong, just die.
stderr = os.fdopen(2, 'w')
stderr.write('Unable to spawn child:\n')
traceback.print_exc(file=stderr)
# Close the socket so the client doesn't think it's still
# connected to a server
try:
s = socket.fromfd(0, socket.AF_INET, socket.SOCK_STREAM)
s.shutdown(2)
except:
pass
os._exit(1)
else: # Parent
reactor.removeReader(fdesc)
reactor.removeWriter(fdesc)
class InetdProtocol(Protocol):
"""Forks a child process on connectionMade, passing the socket as fd 0."""
def connectionMade(self):
service = self.factory.service
forkPassingFD(service.program, service.programArgs, os.environ,
service.user, service.group, self.transport)
class InetdFactory(ServerFactory):
protocol = InetdProtocol
def __init__(self, service):
self.service = service
def main(options=None):
from twisted.runner import inetdtap
# Parse options, read various config files
if not options:
options = tap.Options()
options.parseOptions()
app = Application('tinet')
tap.updateApplications(app, options)
app.run(save=0)
if __name__ == '__main__':
main()
|