David,<br><br><div class="gmail_quote"><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">While this process is certainly doable, I'll also point out that on<br>
Windows, the more "natural" approach to this is to implement the<br>
process as a service. That also buys you some regular Windows<br>
approaches to management (net stop/start from command line, services<br>
UI from the control panel) and status inquiry (sc query from command<br>
line). So while it may be a little platform-specific code you're<br>
final result will integrate more naturally into that environment for<br>
any system administrators.<br>
<br></blockquote><div><br>It is very possible I might have to go this route. We have some odd requrements,<br>such as needing to run the server using the Windows Server 2008 HPC job scheduler.<br>the job scheduler does some funky things and I am finding that some of the "usual"<br>
assumptions you make on Windows don't hold. So at this point, I need all the tricks<br>in my bad that I can get. Thanks for the example!<br> </div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
While I haven't done this with a twistd based Twisted service, I've<br>
done it with a lot of Twisted code in general. It's easiest if you have<br>
pywin32 installed and can use it's utility service wrappers, for which<br>
the pywin32 package has examples.<br></blockquote><div><br>OK, nice to know about these examples.<br> <br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I've tended to offload the service definition to its own module, and<br>
have it just import and execute the main code (with the twisted<br>
reactor.run call) after the service gets started - that way you can<br>
also manually run the twisted code without involving any of the<br>
service support, or to support execution on non-Windows platforms.<br>
<br>
Here's one example of a service object that invoices Twisted-based<br>
code. When stopping, you use callFromThread because the thread that<br>
handles the request from the Windows service manager is separate from<br>
the thread executing the main code (which is a thread created during<br>
the startup process).<br>
<br>
That's also why you start the reactor without signal initialization,<br>
since it'll be in a secondary thread. In SvcDoRun is where you could<br>
import platform-generic modules to set up your twisted code, and then<br>
this code starts the reactor.<br></blockquote><div><br>Bummer, then I can't use this approach. My "server" uses reactor.spawnProcess<br>which needs the signal handlers to be installed (SIGCHLD specifically) to work<br>
properly... do you know if it can be done without the dual thread trick.<br><br>Thanks,<br><br>Brian<br></div><div><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Oh, and I left some code in that shows retrieving service-specific<br>
parameters from the registry as is typical for Windows services<br>
(stored in HKLM/System/CurrentControlSet/Services/<svc_name>/Parameters<br>
if I recall corrrectly):<br>
<br>
- - - - -<br>
<br>
class DataImportService(win32serviceutil.ServiceFramework):<br>
<br>
__version__ = '1.0.0'<br>
<br>
_svc_name_ = 'fdi'<br>
_svc_display_name_ = 'SomeCompany Data Integration'<br>
<br>
stopping = False<br>
debug = False<br>
<br>
def __init__(self, *args, **kwargs):<br>
win32serviceutil.ServiceFramework.__init__(self, *args, **kwargs)<br>
self.initialize()<br>
self._stopped = threading.Event()<br>
self.log = None<br>
<br>
def initialize(self):<br>
# This is separate from __init__ so during debugging the bootstrap<br>
# code can override __init__ but still finish initialization.<br>
<br>
def _getOption(self, option, default):<br>
return win32serviceutil.GetServiceCustomOption(self._svc_name_,<br>
option,<br>
default)<br>
<br>
def SvcStop(self):<br>
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)<br>
print '%s Service stopping' % self._svc_display_name_<br>
reactor.callFromThread(reactor.stop)<br>
self._stopped.wait(5)<br>
print '%s Service stopped' % self._svc_display_name_<br>
if self.log:<br>
self.log.close()<br>
<br>
def SvcDoRun(self):<br>
"""Main entry point for service execution"""<br>
<br>
if hasattr(sys, 'frozen'):<br>
home_dir = os.path.dirname(sys.executable)<br>
else:<br>
home_dir = os.path.dirname(__file__)<br>
<br>
# First, let's set up some logging<br>
if self.debug:<br>
dupstdout = True<br>
logprefix = None<br>
else:<br>
dupstdout = False<br>
logprefix = os.path.join(home_dir, 'logs', 'dataimport.log')<br>
<br>
self.log = LogClass(logprefix, dupstdout)<br>
<br>
# And then reroute stdout/stderr to that file object<br>
sys.stdout = self.log<br>
sys.stderr = self.log<br>
<br>
print '%s Service %s starting' % (self._svc_display_name_,<br>
self.__version__)<br>
<br>
try:<br>
<br>
# Process config file<br>
config_file = self._getOption('config',<br>
os.path.join(home_dir, 'data',<br>
'dataimport.ini'))<br>
<br>
# ... other service-related initialization ...<br>
<br>
# ... do any Twisted related initialization ...<br>
<br>
# Start everything up<br>
reactor.callLater(0, self.log.write,<br>
'%s Service operational\n' %<br>
self._svc_display_name_)<br>
reactor.run(installSignalHandlers=False)<br>
<br>
# We're shutting down.<br>
<br>
# ... do any shutdown processing ...<br>
<br>
# Flag that we're exiting so service thread can be more<br>
# accurate in terms of declaring shutdown.<br>
self._stopped.set()<br>
<br>
except:<br>
<br>
# For right now just log a traceback and abort<br>
log.err()<br>
# But try to gracefully close down log to let final traceback<br>
# information make it out to the log file.<br>
if self.log:<br>
self.log.close()<br>
<br>
- - - - -<br>
<br>
Your main startup code in the service wrapper module can use helper<br>
functions from win32serviceutil for startup (see the pywin32 examples)<br>
which provides some automatic support for installing/uninstalling the<br>
service, or you can implement your own startup if you want to support<br>
different options and what not. This all works under py2exe as well<br>
if you want to package things up and have a nice self-contained exe as<br>
a Windows service.<br>
<font color="#888888"><br>
-- David<br>
</font><div><div class="h5"><br>
<br>
_______________________________________________<br>
Twisted-Python mailing list<br>
<a href="mailto:Twisted-Python@twistedmatrix.com">Twisted-Python@twistedmatrix.com</a><br>
<a href="http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python" target="_blank">http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python</a><br>
</div></div></blockquote><br>
</div><br>