# Run the sb_server as a WinNT service. Should work on Windows 2000 # and Windows XP. # # * Install as a service using "pop3proxy_service.py install" # * Start the service (Use Control Panel etc, or # "pop3proxy_service.py start". Check the event # log should anything go wrong. # * To debug the service: "pop3proxy_service.py debug" # Service then runs in the command prompt, showing all # print statements. # * To remove the service: "pop3proxy_service.py remove" # This module is part of the spambayes project, which is Copyright 2002 # The Python Software Foundation and is covered by the Python Software # Foundation license. # Originally written by Mark Hammond. import sys, os # Messages from pop3proxy will go nowhere when executed as a service # Try and detect that print will go nowhere and redirect. # redirect output somewhere useful when running as a service. import win32api try: win32api.GetConsoleTitle() except win32api.error: # No console - if we are running from Python sources, # redirect to win32traceutil, but if running from a binary # install, redirect to a log file. # Want to move to logging module later, so for now, we # hack together a simple logging strategy. if hasattr(sys, "frozen"): temp_dir = win32api.GetTempPath() for i in range(3,0,-1): try: os.unlink(os.path.join(temp_dir, "SpamBayesService%d.log" % (i+1))) except os.error: pass try: os.rename( os.path.join(temp_dir, "SpamBayesService%d.log" % i), os.path.join(temp_dir, "SpamBayesService%d.log" % (i+1)) ) except os.error: pass # Open this log, as unbuffered so crashes still get written. sys.stdout = open(os.path.join(temp_dir,"SpamBayesService1.log"), "wt", 0) sys.stderr = sys.stdout else: import win32traceutil # If running from sources, patch up sys.path if not hasattr(sys, "frozen"): # We are in the 'spambayes\win32' directory. We # need the parent on sys.path, so 'spambayes.spambayes' is a package, # and 'pop3proxy' is a module try: # module imported by service manager, or 2.3 (in which __main__ # exists, *and* sys.argv[0] is always already absolute) this_filename=__file__ except NameError: this_filename = sys.argv[0] if not os.path.isabs(sys.argv[0]): # Python 2.3 __main__ # patch up sys.argv, as our cwd will confuse service registration code sys.argv[0] = os.path.abspath(sys.argv[0]) this_filename = sys.argv[0] sb_dir = os.path.dirname(os.path.dirname(this_filename)) sb_scripts_dir = os.path.join(sb_dir,"scripts") sys.path.insert(0, sb_dir) sys.path.insert(-1, sb_scripts_dir) # and change directory here, so pop3proxy uses the default # config file etc os.chdir(sb_dir) # Rest of the standard Python modules we use. import traceback import threading import cStringIO # The spambayes imports we need. import sb_server # The win32 specific modules. import win32serviceutil, win32service import pywintypes, win32con, winerror from ntsecuritycon import * class Service(win32serviceutil.ServiceFramework): # The script name was changed to "sb_server" but I'll leave this as pop3proxy # overwise people might accidently run two proxies. _svc_name_ = "pop3proxy" _svc_display_name_ = "SpamBayes Service" _svc_deps_ = ['tcpip'] # We depend on the tcpip service. def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.event_stopped = threading.Event() self.event_stopping = threading.Event() self.thread = None def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stopping.set() sb_server.stop() def SvcDoRun(self): import servicemanager # Setup our state etc try: sb_server.prepare() except sb_server.AlreadyRunningException: msg = "The SpamBayes proxy service could not be started, as "\ "another SpamBayes server is already running on this machine" servicemanager.LogErrorMsg(msg) errCode = winerror.ERROR_SERVICE_SPECIFIC_ERROR self.ReportServiceStatus(win32service.SERVICE_STOPPED, win32ExitCode=errCode, svcExitCode = 1) return assert not sb_server.state.launchUI, "Service can't launch a UI" # Start the thread running the server. thread = threading.Thread(target=self.ServerThread) thread.start() # Write an event log record - in debug mode we will also # see this message printed. from spambayes.Options import optionsPathname extra = " as user '%s', using config file '%s'" \ % (win32api.GetUserName(), optionsPathname) servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, extra) ) try: # Thread running - wait for the stopping event. self.event_stopping.wait() # Either user requested stop, or thread done - wait for it # to actually stop, but reporting we are still alive. # Wait up to 60 seconds for shutdown before giving up and # exiting uncleanly - we wait for current proxy connections # to close, but you have to draw the line somewhere. for i in range(60): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stopped.wait(1) if self.event_stopped.isSet(): break print "The service is still shutting down..." else: # eeek - we timed out - give up in disgust. print "The worker failed to stop - aborting it anyway" except KeyboardInterrupt: pass # Write another event log record. s = sb_server.state status = " after %d sessions (%d ham, %d spam, %d unsure)" % \ (s.totalSessions, s.numHams, s.numSpams, s.numUnsure) servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, status) ) def ServerThread(self): try: try: sb_server.start() except SystemExit: # user requested shutdown print "pop3proxy service shutting down due to user request" except: # Otherwise an error we should log. ob = cStringIO.StringIO() traceback.print_exc(file=ob) message = "The pop3proxy service failed with an " \ "unexpected error\r\n\r\n" + ob.getvalue() # print it too, so any other log we have gets it. print message # Log an error event to the event log. import servicemanager servicemanager.LogErrorMsg(message) finally: self.event_stopping.set() self.event_stopped.set() if __name__=='__main__': win32serviceutil.HandleCommandLine(Service)