#!/usr/bin/env python # Date: Sat Jul 14 03:58:09 CDT 2007 import os import sys import getopt import socket import atexit import logging import traceback import ConfigParser from paste import gzipper from flup.server.fcgi import WSGIServer from flup.middleware.session import MemorySessionStore, SessionMiddleware #------------------------------------------------------------------------------ def main( args ): # system defaults debug = False service = False config = 'etc/toil.conf' # interpret command line options #TODO: replace with newer optparse? try: opts, args = getopt.getopt( args, "hdsc:", ["help", "debug", "service", "config="] ) except getopt.GetoptError: usage() sys.exit(1) for o, a in opts: if o in ("-h", "--help"): usage() sys.exit(1) if o in ("-c", "--config"): config = a if o in ("-d", "--debug"): debug = True if o in ("-s", "--service"): service = True # initialize the configuration conf = ConfigParser.ConfigParser() conf.read( config ) try: conf.set( 'TOIL', 'debug', str(debug) ) except ConfigParser.NoSectionError: print "[ERROR] The configuration file couldn't be found!" print " Solution: use --config to specify the file path" return port = conf.getint( 'TOIL', 'server.port' ) interface = conf.get( 'TOIL', 'server.interface' ) # augmenting the module path for project-specific imports try: sys.path.append( conf.get('TOIL', 'path.lib') ) except ConfigParser.NoOptionError: pass if service: daemonize() # initialize the logging subsystem formatDebug = logging.Formatter( '[%(levelname)s] <%(name)s> %(asctime)s: %(message)s (%(filename)s:%(lineno)d)' ) formatInfo = logging.Formatter( '[%(levelname)s] <%(name)s> %(asctime)s: %(message)s' ) rootlogger = logging.getLogger( '' ) rootlogger.setLevel( logging.DEBUG ) # default to logging to file fh = logging.FileHandler( conf.get('TOIL', 'path.log'), 'a' ) if debug: fh.setLevel( logging.DEBUG ) fh.setFormatter( formatDebug ) else: fh.setLevel( logging.INFO ) fh.setFormatter( formatInfo ) rootlogger.addHandler( fh ) # if debug is enabled, send all info to the console as well if debug: ch = logging.StreamHandler() ch.setFormatter( formatDebug ) rootlogger.addHandler( ch ) logger = logging.getLogger( 'toil' ) logger.debug( 'initialized application' ) if debug: logger.info( 'debugging is turned on' ) # Parse the package, module, and class to get the WSGI application module = conf.get( 'TOIL', 'server.application' ).split('.') clazz = module[-1] module = __import__( '.'.join( module[:-1] ), globals(), locals(), [module[-1]], -1 ) app = getattr( module, clazz ) # initialize the application try: app = app(conf) app.initialize() except: logger.error( 'encountered fatal during initilialization: %s' % traceback.format_exc() ) # TODO: multiple subroutine exit points return logger.debug('discovered and initialized application: %s' % conf.get('TOIL', 'server.application')) # wrap the application with middleware handler = gzipper.middleware(app.handler) handler = SessionMiddleware(MemorySessionStore(), handler) atexit.register( shutdown ) gotSIGHUP = 0 try: logger.info( 'starting application server on %s:%d' % (interface, port) ) gotSIGHUP = WSGIServer( handler, multiplexed=True, multiprocess=True, bindAddress=(interface,port) ).run() except socket.error, e: logger.error( 'encountered server runtime problem: %s' % e[1] ) try: app.shutdown() except: logger.error( 'encountered fatal during shutdown: %s' % traceback.format_exc() ) sys.exit( 1 ) return gotSIGHUP #------------------------------------------------------------------------------ def daemonize(): # Schroeder: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 UMASK = 0 WORKDIR = "/" MAXFD = 1024 if ( hasattr(os, "devnull") ): REDIRECT_TO = os.devnull else: REDIRECT_TO = "/dev/null" try: pid = os.fork() except OSError, e: raise Exception, "%s [%d]" % (e.strerror, e.errno) if (pid == 0): os.setsid() try: pid = os.fork() except OSError, e: raise Exception, "%s [%d]" % (e.strerror, e.errno) if (pid == 0): os.chdir(WORKDIR) os.umask(UMASK) else: os._exit( 0 ) else: os._exit( 0 ) import resource maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if (maxfd == resource.RLIM_INFINITY): maxfd = MAXFD for fd in range(0, maxfd): try: os.close(fd) except OSError: pass os.open(REDIRECT_TO, os.O_RDWR) os.dup2(0, 1) os.dup2(0, 2) #------------------------------------------------------------------------------ def usage(): print 'Usage: main.py [OPTION]' print '' print 'The available options are as follows:' print ' -d, --debug Turn system debugging on' print ' -h, --help Display this help and exit' print ' -s, --service Daemonize system at startup' print ' -c, --config=FILE Specify configuration file' print '' print 'Report bugs to jquigley@jquigley.com' #------------------------------------------------------------------------------ def shutdown(): logger = logging.getLogger( 'toil' ) logger.info( 'exiting application server' ) sys.exit( 0 ) #------------------------------------------------------------------------------ if __name__ == '__main__': main( sys.argv[1:] ) #------------------------------------------------------------------------------ # EOF