"""Launch.py
DESCRIPTION
Python launch script for the WebKit application server.
This launch script will run in its standard location in the Webware/WebKit
directory as well as in a WebKit work directory outside of the Webware tree.
USAGE
Launch.py [StartOptions] [AppServer [AppServerOptions]]
StartOptions:
  -d, --work-dir=...     Set the path to the app server working directory.
                         By default this is the directory containing Lauch.py.
  -w, --webware-dir=...  Set the path to the Webware root directory.
                         By default this is the parent directory.
  -l, --library=...      Other directories to be included in the search path.
                         You may specify this option multiple times.
  -p, --run-profile      Set this to get profiling going (see Profiler.py).
  -o, --log-file=...     Redirect standard output and error to this file.
  -i, --pid-file=...     Set the file path to hold the app server process id.
                         This option is fully supported under Unix only.
  -u, --user=...         The name or uid of the user to run the app server.
                         This option is supported under Unix only.
  -g, --group=...        The name or gid of the group to run the app server.
                         This option is supported under Unix only.
AppServer:
  The name of the application server module.
  By default, the ThreadedAppServer will be used.
AppServerOptions:
  Options that shall be passed to the application server.
  For instance, the ThreadedAppServer accepts: start, stop, daemon
  You can also change configuration settings here by passing
  arguments of the form ClassName.SettingName=value
Please note that the default values for the StartOptions and the AppServer
can be easily changed at the top of the Launch.py script.
"""
workDir = None
webwareDir = None
libraryDirs = []
runProfile = False
logFile = None
pidFile = None
user = None
group = None
appServer = 'ThreadedAppServer'
import os, sys
def usage():
    """Print the docstring and exit with error."""
    sys.stdout.write(__doc__)
    sys.exit(2)
def launchWebKit(appServer=appServer, workDir=None, args=None):
    """Import and launch the specified WebKit app server.
    appServer  -- the name of the WebKit app server module
    workDir -- the server-side work directory of the app server
    args -- other options that will be given to the app server
    """
    
    if args is None:
        args = []
    if workDir:
        args.append('workdir=' + workDir)
    
    if appServer[-3:] == '.py':
        appServer = appServer[:-3]
    
    try:
        appServerMain = __import__('WebKit.' + appServer, None, None, 'main').main
    except ImportError, e:
        print 'Error: Cannot import the AppServer module.'
        print 'Reason:', str(e)
        sys.exit(1)
    
    from WebKit import Profiler
    if Profiler.startTime is None:
        from time import time
        Profiler.startTime = time()
    
    return appServerMain(args) 
def main(args=None):
    """Evaluate the command line arguments and call launchWebKit."""
    global workDir, webwareDir, libraryDirs, runProfile, \
        logFile, pidFile, user, group, appServer
    if args is None:
        args = sys.argv[1:]
    
    if args and not args[0].startswith('-'):
        arg2 = args.pop(0)
    else:
        arg2 = None
    
    args1 = []
    args2 = []
    while args:
        arg = args.pop(0)
        if (arg.startswith('--')
                and 2 < arg.find('.', 2) < arg.find('=', 5)):
            args2.append(arg)
        else:
            args1.append(arg)
    
    from getopt import getopt, GetoptError
    try:
        opts, args1 = getopt(args1, 'd:w:l:po:i:u:g:', [
            'work-dir=', 'webware-dir=', 'library=', 'run-profile',
            'log-file=', 'pid-file=', 'user=', 'group='])
    except GetoptError, error:
        print str(error)
        print
        usage()
    for opt, arg in opts:
        if opt in ('-d', '--work-dir'):
            workDir = arg
        elif opt in ('-w', '--webware-dir'):
            webwareDir = arg
        elif opt in ('-l', '--library'):
            libraryDirs.append(arg)
        elif opt in ('-p', '--run-profile'):
            runProfile = True
        elif opt in ('-o', '--log-file'):
            logFile = arg
        elif opt in ('-i', '--pid-file'):
            pidFile = arg
        elif opt in ('-u', '--user'):
            user = arg
        elif opt in ('-g', '--group'):
            group = arg
    if arg2:
        appServer = arg2
    elif args1 and not args1[0].startswith('-') and '=' not in args1[0]:
        appServer = args1.pop(0)
    args = args2 + args1
    
    gid = group
    if gid is not None:
        try:
            gid = int(gid)
        except ValueError:
            try:
                import grp
                entry = grp.getgrnam(gid)
            except KeyError:
                print 'Error: Group %r does not exist.' % gid
                sys.exit(2)
            except ImportError:
                print 'Error: Group names are not supported.'
                sys.exit(2)
            gid = entry[2]
    
    uid = user
    if uid is not None:
        try:
            uid = int(uid)
        except ValueError:
            try:
                import pwd
                entry = pwd.getpwnam(uid)
            except KeyError:
                print 'Error: User %r does not exist.' % uid
                sys.exit(2)
            except ImportError:
                print 'Error: User names are not supported.'
                sys.exit(2)
            if not gid:
                gid = entry[3]
            uid = entry[2]
    
    if workDir:
        workDir = os.path.expanduser(workDir)
    else:
        scriptName = sys.argv and sys.argv[0]
        if not scriptName or scriptName == '-c':
            scriptName = 'Launch.py'
        workDir = os.path.dirname(os.path.abspath(scriptName))
    try:
        os.chdir(workDir)
    except OSError, error:
        print 'Error: Could not set working directory.'
        print 'The path %r cannot be used.' % workDir
        print error.strerror
        print 'Check the --work-dir option.'
        sys.exit(1)
    workDir = os.curdir
    
    if webwareDir:
        webwareDir = os.path.expanduser(webwareDir)
    else:
        webwareDir = os.pardir
    if libraryDirs:
        libraryDirs = map(os.path.expanduser, libraryDirs)
    
    
    global __name__, __package__
    name = __name__.rsplit('.', 1)[-1]
    if name != __name__:
        sys.modules[name] = sys.modules[__name__]
        del sys.modules[__name__]
        __name__ = name
        __package__ = None
    
    sysPath = sys.path 
    sys.path = [webwareDir] 
    try: 
        from Properties import name as webwareName
        from WebKit.Properties import name as webKitName
    except ImportError:
        webwareName = None
    if webwareName != 'Webware for Python' or webKitName != 'WebKit':
        print 'Error: Cannot find the Webware directory.'
        print 'The path %r seems to be wrong.' % webwareDir
        print 'Check the --webware-dir option.'
        sys.exit(1)
    if not os.path.exists(os.path.join(webwareDir, 'install.log')):
        print 'Error: Webware has not been installed.'
        print 'Please run install.py in the Webware directory:'
        print '> cd', os.path.abspath(webwareDir)
        print '> python install.py'
        sys.exit(1)
    
    path = [] 
    webKitDir = os.path.abspath(os.path.join(webwareDir, 'WebKit'))
    for p in [workDir, webwareDir] + libraryDirs + sysPath:
        if not p:
            continue 
        p = os.path.abspath(p)
        if p == webKitDir or p in path or not os.path.exists(p):
            continue 
        path.append(p)
    sys.path = path 
    
    args = (appServer, workDir, args)
    
    if 'stop' in args[2]:
        print 'Stopping WebKit.%s...' % appServer
        errorlevel = launchWebKit(*args)
        if not errorlevel:
            if pidFile and os.path.exists(pidFile):
                try:
                    os.remove(pidFile)
                except Exception:
                    print 'The pid file could not be removed.'
                    print
        sys.exit(errorlevel)
    
    if pidFile:
        pidFile = os.path.expanduser(pidFile)
        
        try:
            pid = int(open(pidFile).read())
        except Exception:
            pid = None
        if pid is not None:
            print 'According to the pid file, the server is still running.'
            
            killed = False
            try:
                from signal import SIGTERM, SIGKILL
                print 'Trying to terminate the server with pid %d...' % pid
                os.kill(pid, SIGTERM)
            except OSError, error:
                from errno import ESRCH
                if error.errno == ESRCH: 
                    print 'The pid file was stale, continuing with startup...'
                    killed = True
                else:
                    print 'Cannot terminate server with pid %d.' % pid
                    print error.strerror
                    sys.exit(1)
            except (ImportError, AttributeError):
                print 'Cannot check or terminate server with pid %d.' % pid
                sys.exit(1)
            if not killed:
                from time import sleep
                try:
                    for i in range(100):
                        sleep(0.1)
                        os.kill(pid, SIGTERM)
                except OSError, error:
                    from errno import ESRCH
                    if error.errno == ESRCH:
                        print 'Server with pid %d has been terminated.' % pid
                        killed = True
            if not killed:
                try:
                    for i in range(100):
                        sleep(0.1)
                        os.kill(pid, SIGKILL)
                except OSError, error:
                    from errno import ESRCH
                    if error.errno == ESRCH:
                        print 'Server with pid %d has been killed by force.' % pid
                        killed = True
            if not killed:
                print 'Server with pid %d cannot be terminated.' % pid
                sys.exit(1)
        
        try:
            open(pidFile, 'w').write(str(os.getpid()))
        except Exception:
            print 'The pid file %r could not be written.' % pidFile
            sys.exit(1)
    olduid = oldgid = stdout = stderr = log = None
    errorlevel = 1
    try:
        
        if gid is not None:
            try:
                oldgid = os.getgid()
                if gid != oldgid:
                    os.setgid(gid)
                    if group:
                        print 'Changed server process group to %r.' % group
                else:
                    oldgid = None
            except Exception:
                if group:
                    print 'Could not set server process group to %r.' % group
                    oldgid = None
                    sys.exit(1)
        
        if uid is not None:
            try:
                olduid = os.getuid()
                if uid != olduid:
                    os.setuid(uid)
                    print 'Changed server process user to %r.' % user
                else:
                    olduid = None
            except Exception:
                print 'Could not change server process user to %r.' % user
                olduid = None
                sys.exit(1)
        msg = 'WebKit.' + appServer
        if args[2]:
            msg = '%s %s' % (msg, ' '.join(args[2]))
        else:
            msg = 'Starting %s...' % msg
        print msg
        
        if logFile:
            logFile = os.path.expanduser(logFile)
            try:
                log = open(logFile, 'a', 1) 
                print 'Output has been redirected to %r...' % logFile
                stdout, stderr = sys.stdout, sys.stderr
                sys.stdout = sys.stderr = log
            except IOError, error:
                print 'Cannot redirect output to %r.' % logFile
                print error.strerror
                log = None
                sys.exit(1)
        else:
            print
        
        from WebKit import Profiler
        if Profiler.startTime is None:
            from time import time
            Profiler.startTime = time()
        
        if runProfile:
            print ('Profiling is on. '
                'See docstring in Profiler.py for more info.')
            print
            try:
                from cProfile import Profile
            except ImportError:
                from profile import Profile
            profiler = Profile()
            Profiler.profiler = profiler
            errorlevel = Profiler.runCall(launchWebKit, *args)
            print
            print 'Writing profile stats to %s...' % Profiler.statsFilename
            Profiler.dumpStats()
            print 'WARNING: Applications run much slower when profiled,'
            print 'so turn off profiling in Launch.py when you are finished.'
        else:
            errorlevel = launchWebKit(*args)
    finally:
        print
        
        if log:
            sys.stdout, sys.stderr = stdout, stderr
            log.close()
        
        
        
        
        if oldgid is not None:
            try:
                os.setgid(oldgid)
            except Exception:
                pass
            else:
                oldgid = None
        if olduid is not None:
            try:
                os.setuid(olduid)
            except Exception:
                pass
            else:
                olduid = None
        
        
        
        if pidFile and os.path.exists(pidFile):
            try:
                os.remove(pidFile)
            except Exception:
                if oldgid is None and olduid is None:
                    print 'The pid file could not be removed.'
                    print
    sys.exit(errorlevel)
if __name__ == '__main__':
    main()