Ticket #4073: windows-daemon.diff

File windows-daemon.diff, 6.2 KB (added by zseil, 11 years ago)

incomplete patch

  • twisted/scripts/_twistw.py

     
    22# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    33# See LICENSE for details.
    44
    5 from twisted.python import log
     5import os
     6import sys
     7import msvcrt
     8
     9try:
     10    import win32api
     11    import win32con
     12    import win32process
     13    import win32security
     14    import win32service
     15except ImportError:
     16    win32process = None
     17
     18from twisted.python import win32
    619from twisted.application import app, service, internet
    720from twisted import copyright
    8 import sys, os
    921
    1022
    1123
     
    2436
    2537
    2638
     39def getPythonArgs():
     40    """
     41    Return the list of command line args that were used to start
     42    the current Python interpreter and were not stored in C{sys.argv}.
     43
     44    These are the options that control the Python interpreter itself,
     45    like the Python executable, optimization level, warning filters,
     46    division behaviour and literal string handling.
     47    """
     48    args = [sys.executable]
     49    for warnoption in sys.warnoptions:
     50        args.append("-W")
     51        args.append(warnoption)
     52    if type(1 / 2) is not int:
     53        args.append("-Qnew")
     54    if type("") is not str:
     55        args.append("-U")
     56    if not __debug__:
     57        if getPythonArgs.__doc__ is None:
     58            args.append("-OO")
     59        else:
     60            args.append("-O")
     61    return args
     62
     63
     64
     65def daemonize():
     66    args = [os.path.abspath(__file__)] + sys.argv
     67    executable = sys.executable
     68    cmdline = win32.quoteArguments(getPythonArgs() + args)
     69    inherit = False
     70    priority = win32process.GetPriorityClass(win32process.GetCurrentProcess())
     71    flags = (win32process.CREATE_BREAKAWAY_FROM_JOB | # session leader
     72             win32process.CREATE_NEW_PROCESS_GROUP |  # group leader
     73             win32process.DETACHED_PROCESS |          # no controlling terminal
     74             priority)
     75    info = win32process.STARTUPINFO()
     76    win32process.CreateProcess(executable, cmdline, None, None,
     77                               inherit, flags, None, None, info)
     78    # Do what exec* functions do, let the OS do the cleanup.
     79    os._exit(0)
     80
     81
     82
     83def daemonize2():
     84    args = [sys.argv[1], "--nodaemon"] + sys.argv[2:]
     85    executable = sys.executable
     86    cmdline = win32.quoteArguments(getPythonArgs() + args)
     87    inherit = True
     88    priority = win32process.GetPriorityClass(win32process.GetCurrentProcess())
     89    flags = (win32process.CREATE_NO_WINDOW | # create an invisible console
     90             win32process.CREATE_NEW_PROCESS_GROUP | # group leader
     91             priority)
     92    attributes = win32security.SECURITY_ATTRIBUTES()
     93    attributes.bInheritHandle = True
     94    station = win32service.CreateWindowStation(None, 0,
     95                                               win32con.GENERIC_READ |
     96                                               win32con.GENERIC_WRITE,
     97                                               attributes)
     98    station.SetProcessWindowStation()
     99    sname = win32service.GetUserObjectInformation(station,
     100                                                  win32service.UOI_NAME)
     101    dname = str(os.getpid())
     102    desktop = win32service.CreateDesktop(dname, 0,
     103                                         win32con.GENERIC_READ |
     104                                         win32con.GENERIC_WRITE,
     105                                         attributes)
     106    desktop.SetThreadDesktop()
     107    null = os.open("NUL", os.O_RDWR)
     108    handle = msvcrt.get_osfhandle(null)
     109    info = win32process.STARTUPINFO()
     110    info.lpDesktop = "%s\\%s" % (sname, dname)
     111    info.dwFlags = win32process.STARTF_USESTDHANDLES
     112    info.hStdInput = info.hStdOutput = info.hStdError = handle
     113    win32process.CreateProcess(executable, cmdline, None, None,
     114                               inherit, flags, None, None, info)
     115    # Same as above, exit as fast as posible.
     116    os._exit(0)
     117
     118
     119
    27120class WindowsApplicationRunner(app.ApplicationRunner):
    28121    """
    29122    An ApplicationRunner which avoids unix-specific things. No
     
    34127        """
    35128        Do pre-application-creation setup.
    36129        """
     130        self.config['nodaemon'] = (self.config['nodaemon']
     131                                   or self.config['debug'])
    37132        self.oldstdout = sys.stdout
    38133        self.oldstderr = sys.stderr
    39         os.chdir(self.config['rundir'])
    40134
    41135
    42136    def postApplication(self):
    43137        """
    44138        Start the application and run the reactor.
    45139        """
    46         service.IService(self.application).privilegedStartService()
    47         app.startApplication(self.application, not self.config['no_save'])
     140        self.startApplication(self.application)
     141        self.startReactor(None, self.oldstdout, self.oldstderr)
     142
     143
     144    def startApplication(self, application):
     145        """
     146        Configure global process state based on the given application and run
     147        the application.
     148
     149        @param application: An object which can be adapted to
     150            L{service.IService}.
     151        """
     152        self.setupEnvironment(self.config['rundir'], self.config['nodaemon'])
     153        service.IService(application).privilegedStartService()
     154        app.startApplication(application, not self.config['no_save'])
    48155        app.startApplication(internet.TimerService(0.1, lambda:None), 0)
    49         self.startReactor(None, self.oldstdout, self.oldstderr)
    50         log.msg("Server Shut Down.")
     156
     157
     158    def setupEnvironment(self, rundir, nodaemon):
     159        """
     160        Set the working directory, and daemonize.
     161
     162        @type rundir: C{str}
     163        @param rundir: The path to set as the working directory.
     164
     165        @type nodaemon: C{bool}
     166        @param nodaemon: A flag which, if set, indicates that daemonization
     167            should not be done.
     168        """
     169        daemon = not nodaemon and win32process is not None
     170        if daemon:
     171            daemonize()
     172        elif win32process is not None:
     173            # Restore standard SIGINT handling lost during daemonization
     174            win32api.SetConsoleCtrlHandler(None, 0)
     175        # Note: This needs to come after daemonization,
     176        # otherwise we would change working dir twice.
     177        os.chdir(rundir)
     178
     179
     180if __name__ == "__main__":
     181    daemonize2()