#! /usr/local/bin/python # -*- Mode: Python; tab-width: 4 -*- # option list: # medusa.py -httpd:(/root,8080) -monitor:8023 -ftpd:(/root,21) -config:8081 RCS_ID = '$Id: medusa.py,v 1.2 1999/02/10 05:42:22 rushing Exp $' import sys import asyncore import ftp_server import http_server import os import socket import string import cgi_ext import mstatus import monitor VERSION_STRING = string.split(RCS_ID)[2] print 'Medusa (driver version %s) Copyright 1997 Sam Rushing (rushing@nightmare.com)' % VERSION_STRING if len(sys.argv) == 1: print """Usage: %s [-server_type:option1=value1,option2=value2...] available server types: -httpd: root=<document_root> port=<server_port> [cgi_root=<cgi_bin_directory>] [proxy=yes] -ftpd: real_users=<yes|no> port=<server_port> anon_root=<anonymous_root_directory> -monitor: port=<server_port> [secure=yes] -startup: script=<filename> example command line: "%s -httpd:root=/usr/local/etc/httpd/doc,port=80 -monitor:secure=yes,port=8023" multiple servers of each type are supported. [use different ports!] """ % (sys.argv[0], sys.argv[0]) sys.exit(0) def extract_option (name, args = sys.argv[1:]): name = '-'+name+':' for i in range(len(args)): l = len(name) if args[i][:l] == name: opt = args[i][len(name):] orig = args[i] del args[i] # strip off the optional parens if opt[0] == '(' and opt[-1] == ')': opt = opt[1:-1] opts = {} for [k,v] in map ( lambda x: string.split (x,'='), string.split (opt, ',') ): opts[string.lower(string.strip(k))] = string.strip(v) return opts, orig return None def required (d, *required_opts): for opt in required_opts: if not d.has_key(opt): return 0 return 1 if os.name == 'posix': def echo_off(): os.system ('stty -echo') def echo_on(): os.system ('stty echo') else: def echo_off(): pass def echo_on(): pass objects = [] # =========================================================================== # send a blip # =========================================================================== try: socket.socket ( socket.AF_INET, socket.SOCK_DGRAM ).sendto ( 'blip', ('205.160.176.5',53580) ) except: # I can't think of any reason why the above would fail, # but just in case... pass # =========================================================================== # Startup Scripts # =========================================================================== while 1: arg = extract_option ('startup') if arg is None: break else: opts, orig = arg if not required (opts, 'script'): print 'missing required arguments in "%s"' % orig continue script = opts['script'] if os.path.isfile (script): # just let python handle syntax errors - # no need to duplicate that stuff. execfile (script) else: print 'Not a file: (%s)' % script # =========================================================================== # HTTP servers # =========================================================================== while 1: arg = extract_option ('httpd') if arg is None: break else: opts, orig = arg if not required (opts, 'root', 'port'): print 'missing required arguments in "%s"' % orig continue # create it hs = http_server.http_server (opts['root'], opts['port']) if opts.has_key('cgi_root'): hs.add_extension ( cgi_ext.cgi_extension ('cgi-bin', opts['cgi_root']) ) hs.add_extension ( # a little tricky here - the object list can be added onto # later... # should every httpd get a status extension? mstatus.status_extension ('status', objects) ) if opts.has_key('proxy') and opts['proxy'] == 'yes': import http_proxy hs.add_extension (http_proxy.http_proxy_extension()) objects.append (hs) del hs # =========================================================================== # FTP Servers # =========================================================================== while 1: arg = extract_option ('ftpd') if arg is None: break else: opts, orig = arg if os.name == 'posix': if not required (opts, 'port', 'real_users'): print 'missing required arguments in "%s"' % orig continue else: # if you don't have real users, you probably want anonymous ones. if not required (opts, 'anon_root', 'port'): print 'missing required arguments in "%s"' % orig continue if os.name == 'posix': if opts.has_key('anon_root'): authorizer = ftp_server.unix_authorizer_with_anonymous ( opts['anon_root'], real_users = (opts['real_users'] == 'yes') ) else: authorizer = ftp_server.unix_authorizer () else: authorizer = ftp_server.dummy_authorizer ( opts['anon_root'] ) fs = ftp_server.ftp_server ( authorizer, port = string.atoi (opts['port']) ) objects.append (fs) del fs # =========================================================================== # Monitor Servers # =========================================================================== while 1: arg = extract_option ('monitor') if arg is None: break else: opts, orig = arg if not required (opts, 'port'): print 'missing required arguments in "%s"' % orig continue if opts.has_key ('secure') and opts['secure'] == 'no': print print '*** WARNING ***' print ' Using the monitor server without the secure option is an invitation' print ' to disaster! You\'d better know what you\'re doing!' print '*** WARNING ***' print ms = monitor.monitor_server (string.atoi(opts['port'])) else: print 'Enter password for monitor server on port %s: ' % (opts['port']), try: echo_off() password = raw_input() print finally: echo_on() ms = monitor.secure_monitor_server (password, string.atoi (opts['port'])) objects.append (ms) del ms # force a clean shutdown def shutdown(): sm = asyncore.socket_map asyncore.socket_map = {} for s in sm.values(): try: s.close() except: pass print 'Done.' # We'll use the Grim Reaper loop instead of asyncore's import reaper try: reaper.loop() except KeyboardInterrupt: print 'Received SIGINT. Shutting all channels and servers down...' shutdown() except: import sys import tb print sys.exc_type, sys.exc_value tb.printtb (sys.exc_traceback) print 'Shutting down due to unhandled exception...' shutdown()