Hi. I have just recently been given zope.org SVN write access and made my first bigger change - making zopectl work on Windows. Please review and test my changes. The change I made is based on code that I had in the plone.recipe.zope2instance buildout recipe and has seen quite some use by various developers, so should be reasonable stable. Hanno Hanno Schlichting wrote:
Log message for revision 75066: Support for using zopectl on Windows has been added. All commands are supported and there are two Windows specific ones: install and remove, which install or remove the Windows service. The start, stop and restart commands handle the Windows service.
Changed: U Zope/trunk/doc/CHANGES.txt U Zope/trunk/lib/python/Zope2/Startup/zopectl.py A Zope/trunk/skel/bin/zopectl.bat.in
-=- Modified: Zope/trunk/doc/CHANGES.txt =================================================================== --- Zope/trunk/doc/CHANGES.txt 2007-05-03 18:27:55 UTC (rev 75065) +++ Zope/trunk/doc/CHANGES.txt 2007-05-03 18:35:20 UTC (rev 75066) @@ -51,6 +51,12 @@
Features added
+ - Support for using zopectl on Windows has been added. All commands are + supported and there are two Windows specific ones: install and remove, + which install or remove the Windows service. The start, stop and + restart commands handle the Windows service. In order to use them, you + need to call 'bin\zopectl install' once. + - ZCatalog result objects (catalog brains) now have an interface, ZCatalog.interfaces.ICatalogBrain.
Modified: Zope/trunk/lib/python/Zope2/Startup/zopectl.py =================================================================== --- Zope/trunk/lib/python/Zope2/Startup/zopectl.py 2007-05-03 18:27:55 UTC (rev 75065) +++ Zope/trunk/lib/python/Zope2/Startup/zopectl.py 2007-05-03 18:35:20 UTC (rev 75066) @@ -48,6 +48,9 @@ from ZConfig.components.logger.handlers import FileHandlerFactory from ZConfig.datatypes import existing_dirpath
+WIN = False +if sys.platform[:3].lower() == "win": + WIN = True
def string_list(arg): return arg.split() @@ -127,6 +130,12 @@ self.python = 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: for handler in config.eventlog.handler_factories: @@ -158,11 +167,59 @@ args = [opt, svalue] return args
+ if WIN: + def get_status(self): + # get_status from zdaemon relies on *nix specific socket handling. + # We just don't support getting the status and sending actions to + # the control server on Windows. This could be extended to ask for + # the status of the Windows service though + self.zd_up = 0 + self.zd_pid = 0 + 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) + + 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 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) + + def help_remove(self): + print "remove -- Removes the Zope Windows service." + def do_start(self, arg): # signal to Zope that it is being managed - #(to indicate it's web-restartable) + # (to indicate it's web-restartable) os.putenv('ZMANAGED', '1') - ZDCmd.do_start(self, arg) + 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) + else: + ZDCmd.do_start(self, arg)
def get_startup_cmd(self, python, more): cmdline = ( '%s -c "from Zope2 import configure;' @@ -179,12 +236,17 @@ os.system(cmdline)
def do_foreground(self, arg): - self.options.program[1:1] = ["-X", "debug-mode=on"] - try: + if WIN: + # Adding arguments to the program is not supported on Windows + # and the runzope script doesn't put you in debug-mode either ZDCmd.do_foreground(self, arg) - finally: - self.options.program.remove("-X") - self.options.program.remove("debug-mode=on") + else: + self.options.program[1:1] = ["-X", "debug-mode=on"] + try: + ZDCmd.do_foreground(self, arg) + finally: + self.options.program.remove("-X") + self.options.program.remove("debug-mode=on")
def help_debug(self): print "debug -- run the Zope debugger to inspect your database" @@ -262,19 +324,23 @@ args.insert(0, self.options.python)
print 'Running tests via: %s' % ' '.join(args) - pid = os.fork() - if pid == 0: # child - os.execv(self.options.python, args) - - # Parent process running (execv replaces process in child - while True: - try: - os.waitpid(pid, 0) - except (OSError, KeyboardInterrupt): - continue - else: - break + if WIN: + # Windows process handling is quite different + os.system(' '.join(args)) + else: + pid = os.fork() + if pid == 0: # child + os.execv(self.options.python, args)
+ # Parent process running (execv replaces process in child + while True: + try: + os.waitpid(pid, 0) + except (OSError, KeyboardInterrupt): + continue + else: + break + def help_test(self): print "test [args]+ -- run unit / functional tests." print " See $ZOPE_HOME/bin/test.py --help for syntax." @@ -317,7 +383,8 @@ # If it is not reset, 'os.wait[pid]' can non-deterministically fail. # Thus, use a way such that "SIGCHLD" is definitely reset in children. #signal.signal(signal.SIGCHLD, signal.SIG_IGN) - if os.uname()[0] != 'Darwin': + if not WIN and os.uname()[0] != 'Darwin': + # On Windows the os.uname method does not exist. # On Mac OS X, setting up a signal handler causes waitpid to # raise EINTR, which is not preventable via the Python signal # handler API and can't be dealt with properly as we can't pass
Copied: Zope/trunk/skel/bin/zopectl.bat.in (from rev 75039, Zope/trunk/skel/bin/runzope.bat.in) =================================================================== --- Zope/trunk/skel/bin/runzope.bat.in 2007-05-03 07:18:30 UTC (rev 75039) +++ Zope/trunk/skel/bin/zopectl.bat.in 2007-05-03 18:35:20 UTC (rev 75066) @@ -0,0 +1,8 @@ +@set ZOPE_HOME=<<ZOPE_HOME>> +@set INSTANCE_HOME=<<INSTANCE_HOME>> +@set PYTHON=%ZOPE_HOME%\bin\python.exe +@set SOFTWARE_HOME=%ZOPE_HOME%\lib\python +@set CONFIG_FILE=%INSTANCE_HOME%\etc\zope.conf +@set PYTHONPATH=%SOFTWARE_HOME% +@set ZDCTL=%SOFTWARE_HOME%\Zope2\Startup\zopectl.py +"%PYTHON%" "%ZDCTL%" -C "%CONFIG_FILE%" %1 %2 %3 %4 %5 %6 %7