[Zope-Checkins] SVN: Zope/branches/2.12/src/ - rework Windows service stuff to make zopeservice.py in the instance home un-necessary

Chris Withers chris at simplistix.co.uk
Mon Oct 5 07:34:19 EDT 2009


Log message for revision 104793:
  - rework Windows service stuff to make zopeservice.py in the instance home un-necessary
    (this means that buildout instances work on Windows too :-) )
  - remove a few stray comments and old unneeded code from nt_svcutils/service.py
  

Changed:
  U   Zope/branches/2.12/src/Zope2/Startup/zopectl.py
  D   Zope/branches/2.12/src/Zope2/utilities/skel/bin/zopeservice.py.in
  U   Zope/branches/2.12/src/nt_svcutils/service.py

-=-
Modified: Zope/branches/2.12/src/Zope2/Startup/zopectl.py
===================================================================
--- Zope/branches/2.12/src/Zope2/Startup/zopectl.py	2009-10-05 11:06:04 UTC (rev 104792)
+++ Zope/branches/2.12/src/Zope2/Startup/zopectl.py	2009-10-05 11:34:19 UTC (rev 104793)
@@ -51,7 +51,42 @@
 WIN = False
 if sys.platform[:3].lower() == "win":
     WIN = True
+    import win32serviceutil
+    from nt_svcutils import service
+    
+    def do_windows(command):
+        def inner(self,arg):
 
+            INSTANCE_HOME = self.options.directory
+            name = 'Zope'+str(hash(INSTANCE_HOME.lower()))
+            display_name = 'Zope instance at '+INSTANCE_HOME
+
+            # This class exists only so we can take advantage of
+            # win32serviceutil.HandleCommandLine, it is never
+            # instantiated.
+            class InstanceService(service.Service):
+                _svc_name_ = name
+                _svc_display_name_ = display_name
+                _svc_description_ = "A Zope application instance running as a service"
+
+            # getopt sucks :-(
+            argv = [sys.argv[0]]
+            argv.extend(arg.split())
+            argv.append(command)
+
+            # we need to supply this manually as HandleCommandLine guesses wrong
+            serviceClassName = os.path.splitext(service.__file__)[0]+'.Service'
+            
+            err = win32serviceutil.HandleCommandLine(
+                InstanceService,
+                serviceClassName,
+                argv=argv,
+                )
+
+            return err,InstanceService
+            
+        return inner
+
 def string_list(arg):
     return arg.split()
 
@@ -132,11 +167,6 @@
         self.python = os.environ.get('PYTHON', config.python) or sys.executable
         self.zdrun = os.path.join(os.path.dirname(zdaemon.__file__),
                                   "zdrun.py")
-        if WIN:
-            # Add the path to the zopeservice.py script, which is needed for
-            # some of the Windows specific commands
-            servicescript = os.path.join(self.directory, 'bin', 'zopeservice.py')
-            self.servicescript = '"%s" %s' % (self.python, servicescript)
 
         self.exitcodes = [0, 2]
         if self.logfile is None and config.eventlog is not None:
@@ -171,6 +201,8 @@
             args = [opt, svalue]
         return args
 
+    ## START OF WINDOWS ONLY STUFF
+    
     if WIN:
         def get_status(self):
             # get_status from zdaemon relies on *nix specific socket handling.
@@ -182,46 +214,40 @@
             self.zd_status = None
             return
 
-        def do_stop(self, arg):
-            # Stop the Windows service
-            program = "%s stop" % self.options.servicescript
-            print program
-            os.system(program)
+        do_stop = do_windows('stop')
+        do_restart = do_windows('restart')
 
-        def do_restart(self, arg):
-            # Restart the Windows service
-            program = "%s restart" % self.options.servicescript
-            print program
-            os.system(program)
-
         # Add extra commands to install and remove the Windows service
 
-        def do_install(self, arg):
-            program = "%s install" % self.options.servicescript
-            print program
-            os.system(program)
+        def do_install(self,arg):
+            err,InstanceClass = do_windows('install')(self,arg)
+            if not err:
+                # If we installed successfully, put info in registry for the
+                # real Service class to use:
+                command = '"%s" -C "%s"' % (
+                    # This gives us the instance script for buildout instances
+                    # and the install script for classic instances.
+                    os.path.join(os.path.split(sys.argv[0])[0],'runzope'),
+                    self.options.configfile
+                    )
+                InstanceClass.setReg('command',command)
 
         def help_install(self):
             print "install -- Installs Zope as a Windows service."
 
-        def do_remove(self, arg):
-            program = "%s remove" % self.options.servicescript
-            print program
-            os.system(program)
+        do_remove = do_windows('remove')
 
         def help_remove(self):
             print "remove -- Removes the Zope Windows service."
 
+    ## END OF WINDOWS ONLY STUFF
+            
     def do_start(self, arg):
         # signal to Zope that it is being managed
         # (to indicate it's web-restartable)
         os.putenv('ZMANAGED', '1')
         if WIN:
-            # On Windows start the service, this fails with a reasonable
-            # error message as long as the service is not installed
-            program = "%s start" % self.options.servicescript
-            print program
-            os.system(program)
+            do_windows('start')(self,arg)
         else:
             ZDCmd.do_start(self, arg)
 

Deleted: Zope/branches/2.12/src/Zope2/utilities/skel/bin/zopeservice.py.in
===================================================================
--- Zope/branches/2.12/src/Zope2/utilities/skel/bin/zopeservice.py.in	2009-10-05 11:06:04 UTC (rev 104792)
+++ Zope/branches/2.12/src/Zope2/utilities/skel/bin/zopeservice.py.in	2009-10-05 11:34:19 UTC (rev 104793)
@@ -1,117 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE
-#
-##############################################################################
-"""
-A Zope Windows NT service frontend.
-
-Usage:
-
-  Installation
-
-    The Zope service should be installed by the Zope Windows
-    installer. You can manually install, uninstall the service from
-    the commandline.
-
-      ntservice.py [options] install|update|remove|start [...]
-           |stop|restart [...]|debug [...]
-
-    Options for 'install' and 'update' commands only:
-
-     --username domain\username : The Username the service is to run
-                                  under
-
-     --password password : The password for the username
-
-     --startup [manual|auto|disabled] : How the service starts,
-                                        default = manual
-
-    Commands
-
-      install : Installs the service
-
-      update : Updates the service.  Use this if you change any
-               configuration settings and need the service to be
-               re-registered.
-
-      remove : Removes the service
-
-      start : Starts the service, this can also be done from the
-              services control panel
-
-      stop : Stops the service, this can also be done from the
-             services control panel
-
-      restart : Restarts the service
-
-      debug : Runs the service in debug mode
-
-    You can view the usage options by running this module without any
-    arguments.
-
-  Starting Zope
-
-    Start Zope by clicking the 'start' button in the services control
-    panel. You can set Zope to automatically start at boot time by
-    choosing 'Auto' startup by clicking the 'statup' button.
-
-  Stopping Zope
-
-    Stop Zope by clicking the 'stop' button in the services control
-    panel. You can also stop Zope through the web by going to the
-    Zope control panel and by clicking 'Shutdown'.
-
-  Event logging
-
-    Service related events (such as startup, shutdown, or errors executing
-    the Zope process) are logged to the NT application event log. Use the
-    event viewer to see these events.
-
-    Zope Events are still written to the Zope event logs.
-
-"""
-import sys, os
-
-# these are replacements from mkzopeinstance
-INSTANCE_HOME = r'<<INSTANCE_HOME>>'
-ZOPE_SCRIPTS = r'<<ZOPE_SCRIPTS>>'
-ZOPE2PATH = r'<<ZOPE2PATH>>'
-
-ZOPE_RUN = os.path.join(ZOPE_SCRIPTS, 'runzope')
-CONFIG_FILE = os.path.join(INSTANCE_HOME, 'etc', 'zope.conf')
-PYTHONSERVICE_EXE = os.path.join(ZOPE_SCRIPTS, 'PythonService.exe')
-
-os.environ["INSTANCE_HOME"] = INSTANCE_HOME
-
-# XXX: we need to find nt_svcutils.service
-sys.path[0:0] = [ZOPE2PATH]
-
-from nt_svcutils.service import Service
-
-servicename = 'Zope_%s' % str(hash(INSTANCE_HOME.lower()))
-
-class InstanceService(Service):
-    _svc_name_ = servicename
-    _svc_display_name_ = 'Zope instance at %s' % INSTANCE_HOME
-    # _svc_description_ can also be set (but what to say isn't clear!)
-    # If the exe we expect is not there, let the service framework search
-    # for it.  This will be true for people running from source builds and
-    # relying on pre-installed pythonservice.exe.
-    # Note this is only used at install time, not runtime.
-    if os.path.isfile(PYTHONSERVICE_EXE):
-        _exe_name_ = PYTHONSERVICE_EXE
-
-    process_runner = ZOPE_RUN
-    process_args = '-C "%s"' % CONFIG_FILE
-
-if __name__ == '__main__':
-    import win32serviceutil
-    win32serviceutil.HandleCommandLine(InstanceService)

Modified: Zope/branches/2.12/src/nt_svcutils/service.py
===================================================================
--- Zope/branches/2.12/src/nt_svcutils/service.py	2009-10-05 11:06:04 UTC (rev 104792)
+++ Zope/branches/2.12/src/nt_svcutils/service.py	2009-10-05 11:34:19 UTC (rev 104793)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -50,26 +50,21 @@
     should be created in the instance home.
     """
 
-    # The PythonService model requires that an actual on-disk class declaration
-    # represent a single service.  Thus, the definitions below for the instance
-    # must be overridden in a subclass in a file within the instance home for
-    # each instance.
-    # The values below are just examples.
-    _svc_name_ = r'Zope-Instance'
-    _svc_display_name_ = r'Zope instance at C:\Zope-Instance'
-
-    process_runner = r'C:\Program Files\Zope-2.7.0-a1\bin\python.exe'
-    process_args = r'{path_to}\run.py -C {path_to}\zope.conf'
     evtlog_name = 'Zope'
 
     def __init__(self, args):
+
+        # We get passed in the service name
+        self._svc_name_ = args[0]
+
+        # ...and from that, we can look up the other needed bits
+        # from the registry:
+        self._svc_display_name_ = self.getReg('DisplayName')
+        self._svc_command_ = self.getReg('Command',keyname='PythonClass')
+        
         win32serviceutil.ServiceFramework.__init__(self, args)
-        # Just say "Zope", instead of "Zope_-xxxxx"
-        try:
-            servicemanager.SetEventSourceName(self.evtlog_name)
-        except AttributeError:
-            # old pywin32 - that's ok.
-            pass
+        
+        servicemanager.SetEventSourceName(self.evtlog_name)
         # Create an event which we will use to wait on.
         # The "service stop" request will set this event.
         # We create it inheritable so we can pass it to the child process, so
@@ -80,6 +75,27 @@
         self.hWaitStop = win32event.CreateEvent(sa, 0, 0, None)
         self.redirect_thread = None
 
+    @classmethod
+    def openKey(cls,serviceName,keyname=None):
+        keypath = "System\\CurrentControlSet\\Services\\"+serviceName
+        if keyname:
+            keypath += ('\\'+keyname)
+        return win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,keypath,0,win32con.KEY_ALL_ACCESS)
+    
+    @classmethod
+    def setReg(cls,name,value,serviceName=None,keyname='PythonClass'):
+        if not serviceName:
+            serviceName = cls._svc_name_
+        key = cls.openKey(serviceName,keyname)
+        try:
+            win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, value)
+        finally:
+            win32api.RegCloseKey(key)
+
+    def getReg(self,name,keyname=None):
+        key = self.openKey(self._svc_name_,keyname)
+        return win32api.RegQueryValueEx(key,name)[0]
+    
     def SvcStop(self):
         # Before we do anything, tell the SCM we are starting the stop process.
         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
@@ -168,15 +184,12 @@
         self.logmsg(servicemanager.PYS_SERVICE_STARTED)
 
         while 1:
-            # We pass *this* file and the handle as the first 2 params, then
-            # the 'normal' startup args.
-            # See the bottom of this script for how that is handled.
-            cmd = '"%s" %s' % (self.process_runner, self.process_args)
-            info = self.createProcess(cmd)
+            info = self.createProcess(self._svc_command_)
             # info is (hProcess, hThread, pid, tid)
             self.hZope = info[0] # process handle
-            # XXX why the test before the log message?
             if self.backoff_interval > BACKOFF_INITIAL_INTERVAL:
+                # make a note that we've created a process after backing
+                # off?
                 self.info("created process")
             if not (self.run() and self.checkRestart()):
                 break



More information about the Zope-Checkins mailing list