[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