#!/usr/bin/env python

# Twisted, the Framework of Your Internet
# Copyright (C) 2001 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 Preamble
# This makes sure that users don't have to set up their environment
# specially in order to run these programs from bin/.
import sys,os,string

if string.find(os.path.abspath(sys.argv[0]),'Twisted') != -1:
    sys.path.append(os.path.dirname(
        os.path.dirname(os.path.abspath(sys.argv[0]))))
### end of preamble

from twisted.python import usage, util, runtime

util.addPluginDir()

# System Imports
from cPickle import load
import traceback
import imp

class ServerOptions(usage.Options):
    synopsis = "Usage: twistd [options]"

    optFlags = [['nodaemon','n',  "don't daemonize"],
                ['profile','p',   "run profiler"],
                ['quiet','q',     "be a little more quiet"],
                ['no_save','o',   "do not save state on shutdown"]]

    optStrings = [['logfile','l', None,
                   "log to a specified file, - for stdout"],
                  ['file','f','twistd.tap',
                   "read the given .tap file"],
                  ['python','y', None,
                   "read an application from within a Python file"],
                  ['pidfile','','twistd.pid',
                   "use a pidfile"],
                  ['rundir','d','.',
                   'change to a supplied directory before running']]

    def opt_plugin(self, pkgname):
        """read config.tac from a plugin package, as with -y
        """
        try:
            fname = imp.find_module(pkgname)[1]
        except ImportError:
            print "Error: Package %s not found. Is it in your ~/TwistedPlugins directory?" % pkgname
            sys.exit()
        self.python = os.path.join(fname, 'config.tac')

    opt_g = opt_plugin

config = ServerOptions()
try:
    config.parseOptions()
except usage.error, ue:
    config.opt_help()
    print "%s: %s" % (sys.argv[0], ue)
    os._exit(1)

platformType = runtime.platform.getType()
if platformType != 'java':
    # java can't chdir
    os.chdir(config.rundir)
sys.path.append(config.rundir)

if platformType != 'posix':
    # only posix can fork
    config.nodaemon = 1

# Twisted Imports
from twisted import copyright
from twisted.python import usage, log, runtime
from twisted.internet import main
from twisted.persisted import styles
class EverythingEphemeral(styles.Ephemeral):
    def __getattr__(self, key):
        try:
            return getattr(mainMod, key)
        except AttributeError:
            if initRun:
                raise
            else:
                print "Warning!  Loading from __main__: %s" % key
                return styles.Ephemeral()


# Load the servers.
# This will fix up accidental function definitions in evaluation spaces
# and the like.
initRun = 0
mainMod = sys.modules['__main__']


if os.path.exists(config.pidfile):
    try:
        pid = int(open(config.pidfile).read())
    except ValueError:
        sys.exit('Pidfile %s contains non numeric value' % config.pidfile)

    try:
        os.kill(pid, 0)
    except OSError, why:
	import errno
	if why[0] == errno.ESRCH:
	    # The pid doesnt exists.
	    if not config.quiet:
		print 'Removing stale pidfile %s' % config.pidfile
		os.remove(config.pidfile)
	else:
	    sys.exit('Can\'t check status of PID %s from pidfile %s: %s' % (pid, config.pidfile, why[1]))
    else:
        sys.exit('A server is already running, PID %s' %  pid)

if config.logfile == '-':
    if not config.nodaemon:
        print 'daemons cannot log to stdout'
        os._exit(1)
    logFile = sys.stdout
elif config.nodaemon and not config.logfile:
    logFile = sys.stdout
else:
    logFile = open(config.logfile or 'twistd.log','ab+')

oldstdin = sys.stdin
oldstdout = sys.stdout
oldstderr = sys.stderr
log.startLogging(logFile)
sys.stdout.flush()
log.msg("twistd %s (%s %s) starting up" % (copyright.version,
                                           sys.executable,
                                           runtime.shortPythonVersion()))
if not config.python:
    print "Loading %s..." % config.file
    sys.modules['__main__'] = EverythingEphemeral()
    application = load(open(config.file,'rb'))
    sys.modules['__main__'] = mainMod
    main.theApplication = application
    styles.doUpgrade()
else:
    pyfile = os.path.abspath(config.python)
    print "Loading %s..." % pyfile
    dict = {'__file__': pyfile}
    execfile(pyfile, dict, dict)
    try:
        application = dict['application']
    except KeyError:
        log.msg("Error - python file %s must set a variable named 'application', an instance of twisted.internet.app.Application. No such variable was found!" % repr(pyfile))
        sys.exit()

print "Loaded."
initRun = 1

if not config.nodaemon:
    # Turn into a daemon.
    if os.fork():   # launch child and...
        os._exit(0) # kill off parent
    os.setsid()
    os.umask(077)
    oldstdin.close()
    oldstdout.close()
    oldstderr.close()

if os.name != 'java':
    # java doesn't have getpid
    open(config.pidfile,'wb').write(str(os.getpid()))


try:
    if config.profile:
        import profile
        profile.run("application.run(%d)" % (not config.no_save))
    else:
        application.run(not config.no_save)
except:
    if config.nodaemon:
        file = oldstdout
    else:
        file = open("TWISTD-CRASH.log",'a')
    traceback.print_exc(file=file)
    file.flush()
if os.name != 'java':
    # again, java with the not having getpid
    os.unlink(config.pidfile)
print "Server Shut Down."
