Index: twisted/scripts/_twistw.py =================================================================== --- twisted/scripts/_twistw.py (revision 27398) +++ twisted/scripts/_twistw.py (working copy) @@ -2,10 +2,22 @@ # Copyright (c) 2001-2008 Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.python import log +import os +import sys +import msvcrt + +try: + import win32api + import win32con + import win32process + import win32security + import win32service +except ImportError: + win32process = None + +from twisted.python import win32 from twisted.application import app, service, internet from twisted import copyright -import sys, os @@ -24,6 +36,87 @@ +def getPythonArgs(): + """ + Return the list of command line args that were used to start + the current Python interpreter and were not stored in C{sys.argv}. + + These are the options that control the Python interpreter itself, + like the Python executable, optimization level, warning filters, + division behaviour and literal string handling. + """ + args = [sys.executable] + for warnoption in sys.warnoptions: + args.append("-W") + args.append(warnoption) + if type(1 / 2) is not int: + args.append("-Qnew") + if type("") is not str: + args.append("-U") + if not __debug__: + if getPythonArgs.__doc__ is None: + args.append("-OO") + else: + args.append("-O") + return args + + + +def daemonize(): + args = [os.path.abspath(__file__)] + sys.argv + executable = sys.executable + cmdline = win32.quoteArguments(getPythonArgs() + args) + inherit = False + priority = win32process.GetPriorityClass(win32process.GetCurrentProcess()) + flags = (win32process.CREATE_BREAKAWAY_FROM_JOB | # session leader + win32process.CREATE_NEW_PROCESS_GROUP | # group leader + win32process.DETACHED_PROCESS | # no controlling terminal + priority) + info = win32process.STARTUPINFO() + win32process.CreateProcess(executable, cmdline, None, None, + inherit, flags, None, None, info) + # Do what exec* functions do, let the OS do the cleanup. + os._exit(0) + + + +def daemonize2(): + args = [sys.argv[1], "--nodaemon"] + sys.argv[2:] + executable = sys.executable + cmdline = win32.quoteArguments(getPythonArgs() + args) + inherit = True + priority = win32process.GetPriorityClass(win32process.GetCurrentProcess()) + flags = (win32process.CREATE_NO_WINDOW | # create an invisible console + win32process.CREATE_NEW_PROCESS_GROUP | # group leader + priority) + attributes = win32security.SECURITY_ATTRIBUTES() + attributes.bInheritHandle = True + station = win32service.CreateWindowStation(None, 0, + win32con.GENERIC_READ | + win32con.GENERIC_WRITE, + attributes) + station.SetProcessWindowStation() + sname = win32service.GetUserObjectInformation(station, + win32service.UOI_NAME) + dname = str(os.getpid()) + desktop = win32service.CreateDesktop(dname, 0, + win32con.GENERIC_READ | + win32con.GENERIC_WRITE, + attributes) + desktop.SetThreadDesktop() + null = os.open("NUL", os.O_RDWR) + handle = msvcrt.get_osfhandle(null) + info = win32process.STARTUPINFO() + info.lpDesktop = "%s\\%s" % (sname, dname) + info.dwFlags = win32process.STARTF_USESTDHANDLES + info.hStdInput = info.hStdOutput = info.hStdError = handle + win32process.CreateProcess(executable, cmdline, None, None, + inherit, flags, None, None, info) + # Same as above, exit as fast as posible. + os._exit(0) + + + class WindowsApplicationRunner(app.ApplicationRunner): """ An ApplicationRunner which avoids unix-specific things. No @@ -34,17 +127,55 @@ """ Do pre-application-creation setup. """ + self.config['nodaemon'] = (self.config['nodaemon'] + or self.config['debug']) self.oldstdout = sys.stdout self.oldstderr = sys.stderr - os.chdir(self.config['rundir']) def postApplication(self): """ Start the application and run the reactor. """ - service.IService(self.application).privilegedStartService() - app.startApplication(self.application, not self.config['no_save']) + self.startApplication(self.application) + self.startReactor(None, self.oldstdout, self.oldstderr) + + + def startApplication(self, application): + """ + Configure global process state based on the given application and run + the application. + + @param application: An object which can be adapted to + L{service.IService}. + """ + self.setupEnvironment(self.config['rundir'], self.config['nodaemon']) + service.IService(application).privilegedStartService() + app.startApplication(application, not self.config['no_save']) app.startApplication(internet.TimerService(0.1, lambda:None), 0) - self.startReactor(None, self.oldstdout, self.oldstderr) - log.msg("Server Shut Down.") + + + def setupEnvironment(self, rundir, nodaemon): + """ + Set the working directory, and daemonize. + + @type rundir: C{str} + @param rundir: The path to set as the working directory. + + @type nodaemon: C{bool} + @param nodaemon: A flag which, if set, indicates that daemonization + should not be done. + """ + daemon = not nodaemon and win32process is not None + if daemon: + daemonize() + elif win32process is not None: + # Restore standard SIGINT handling lost during daemonization + win32api.SetConsoleCtrlHandler(None, 0) + # Note: This needs to come after daemonization, + # otherwise we would change working dir twice. + os.chdir(rundir) + + +if __name__ == "__main__": + daemonize2()