[Twisted-Python] Daemon processes on windows

Žiga Seilnacht ziga.seilnacht at gmail.com
Sat Nov 7 21:21:57 EST 2009


Brian Granger wrote:
> Hi,
> 
> I have a server-like process that uses twisted.  I need it to daemonize
> itself and on linux/os x I am simply
> using the daemonize function from twistd.  This works fine.  What about
> Windows though....I saw that the
> win32 version of twistd doesn't have (unless I am missing it) the ability to
> daemonize a process.
> 
> Is is simply impossible to daemonize a process on windows?  If so, is there
> any way to have a child
> process on windows ignore SIGINT send to the parent?
> 
> Cheers and thanks,
> 
> Brian
> 

It is possible to daemonize a process on Windows. I experimented with 
adding that support to the twistd script, but got swamped with other 
work and couldn't finish it. Below is the code that I have so far. You 
can save it in a module and call the daemonize() function from your script.

The process of daemonization is similar to the one on UNIX -- you have 
to spawn a child process twice, the first child is responsible for 
breaking away from any job objects (somewhat similar to becoming a 
session leader on UNIX), becoming a new process group leader and closing 
all handles (file descriptors) that might have been inherited.

The second child has to open dummy std* files and a new (hidden) 
console, otherwise the signals stop working. There is a slight 
complication with window stations and desktops. Each console creates at 
least one window and some other user objects, so we have to create
a separate desktop, or other processes would be able to manipulate them 
  and send us arbitrary (Windows) messages.

Regards,
Ziga



import os
import sys
import msvcrt

import win32con
import win32process
import win32security
import win32service

from twisted.python import win32



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

     flags = (win32process.CREATE_BREAKAWAY_FROM_JOB | # session leader
              win32process.CREATE_NEW_PROCESS_GROUP |  # group leader
              win32process.DETACHED_PROCESS) # no controlling terminal

     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
     # create an invisible console
     flags = (win32process.CREATE_NO_WINDOW
     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 possible.
     os._exit(0)



if __name__ == "__main__":
     daemonize2()



More information about the Twisted-Python mailing list