[Zope-Checkins] CVS: Zope/lib/python/nt_svcutils -
service.py:1.4.2.4
Jeremy Hylton
jeremy at zope.com
Mon Jan 26 22:02:51 EST 2004
Update of /cvs-repository/Zope/lib/python/nt_svcutils
In directory cvs.zope.org:/tmp/cvs-serv20625/Zope/lib/python/nt_svcutils
Modified Files:
Tag: jeremy-windows-service-branch
service.py
Log Message:
Finish off first cut at redirecting I/O.
Add redirect() method that reads using ReadFile() and writes to the log file. redirect() also catches I/O errors and logs ones we don't understand.
Replace CloseHandle() calls with Close() calls. Seems more Pythonic.
Add a bunch of comments and revise doc string.
=== Zope/lib/python/nt_svcutils/service.py 1.4.2.3 => 1.4.2.4 ===
--- Zope/lib/python/nt_svcutils/service.py:1.4.2.3 Mon Jan 26 15:57:18 2004
+++ Zope/lib/python/nt_svcutils/service.py Mon Jan 26 22:02:49 2004
@@ -38,8 +38,13 @@
BACKOFF_INITIAL_INTERVAL = 5
class Service(win32serviceutil.ServiceFramework):
- """ A class representing a Windows NT service that can manage an
- instance-home-based Zope/ZEO/ZRS processes """
+ """Base class for a Windows Server to manage an external process.
+
+ Subclasses can be used to managed an instance home-based Zope or
+ ZEO process. The win32 Python service module registers a specific
+ file and class for a service. To manage an instance, a subclass
+ should be created in the instance home.
+ """
# The PythonService model requires that an actual on-disk class declaration
# represent a single service. Thus, the below definition of start_cmd,
@@ -55,7 +60,14 @@
r'"C:\Program Files\Zope-2.7.0-a1\lib\python\Zope\Startup\run.py" '
r'-C "C:\Zope-Instance\etc\zope.conf"'
)
-
+
+ # If capture_io is True, then log_file must be the path of a file
+ # that the controlled process's stdout and stderr will be written to.
+ # The I/O capture is immature. It does not handle buffering in the
+ # controlled process or sensible interleaving of output between
+ # stdout and stderr. It is intended primarily as a stopgap when
+ # the controlled process produces critical output that can't be
+ # written to a log file using mechanism inside that process.
capture_io = False
log_file = None
@@ -80,6 +92,7 @@
def createProcess(self, cmd):
self.start_time = time.time()
if self.capture_io:
+ self.log = open(self.log_file, "ab")
return self.createProcessCaptureIO(cmd)
else:
return win32process.CreateProcess(
@@ -137,8 +150,6 @@
while 1:
info, handles = self.createProcess(self.start_cmd)
- # XXX integrate handles into the wait and make a loop
- # that reads data and writes it into a logfile
self.hZope = info[0] # process handle
# XXX why the test before the log message?
if self.backoff_interval > BACKOFF_INITIAL_INTERVAL:
@@ -157,12 +168,14 @@
"""
keep_running = True
- # ignore stdin
+ # Assume that the controlled program isn't expecting anything
+ # on stdin.
handles[0].Close()
+ waitfor = [self.hWaitStop, self.hZope, handles[1], handles[2]]
while 1:
- rc = win32event.WaitForMultipleObjects(
- (self.hWaitStop, self.hZope) + handles[1:], 0, win32event.INFINITE)
+ rc = win32event.WaitForMultipleObjects(waitfor, 0,
+ win32event.INFINITE)
if rc == win32event.WAIT_OBJECT_0:
# user sent a stop service request
self.SvcStop()
@@ -174,31 +187,37 @@
status = win32process.GetExitCodeProcess(self.hZope)
# exit status 0 means the user caused a clean shutdown,
# presumably via the web interface
- print "exit status", status
keep_running = status != 0
break
else:
- i = rc - (win32event.WAIT_OBJECT_0 + 2)
- if i == 0:
- try:
- ec, data = win32file.ReadFile(handles[1], 8192)
- except pywintypes.error, err:
- print err
- continue
- if data:
- self.info("stdout: %s" % data)
- elif i == 1:
- try:
- ec, data = win32file.ReadFile(handles[2], 8192)
- except pywintypes.error, err:
- print err
- continue
- if data:
- self.info("stderr: %s" % data)
+ i = rc - win32event.WAIT_OBJECT_0
+ if not self.redirect(waitfor[i]):
+ del waitfor[i]
handles[1].Close()
handles[2].Close()
return keep_running
+ def redirect(self, handle):
+ # This call will block until 80 bytes of output are ready.
+ # If the controlled program is buffering its I/O, it's
+ # possible for this to take a long time. Don't know if
+ # there is a better solution.
+ try:
+ ec, data = win32file.ReadFile(handle, 80)
+ except pywintypes.error, err:
+ # 109 means that the pipe was closed by the controlled
+ # process. Other errors might have similarly inocuous
+ # explanations, but we haven't run into them yet.
+ if err[0] != 109:
+ self.warning("Error reading output from process: %s" % err)
+ return False
+ # In the absence of overlapped I/O, the Python win32api
+ # turns all error codes into exceptions.
+ assert ec == 0
+ self.log.write(data)
+ self.log.flush()
+ return True
+
def checkRestart(self):
# this was an abormal shutdown.
if self.backoff_cumulative > BACKOFF_MAX:
@@ -241,10 +260,9 @@
# circumstances of a service process.
info = win32process.CreateProcess(None, cmd, None, None, True, 0,
None, None, si)
-
- win32file.CloseHandle(stdin[0])
- win32file.CloseHandle(stdout[1])
- win32file.CloseHandle(stderr[1])
+ stdin[0].Close()
+ stdout[1].Close()
+ stderr[1].Close()
return info, (c_stdin, c_stdout, c_stderr)
@@ -260,7 +278,7 @@
pid = win32api.GetCurrentProcess()
dup = win32api.DuplicateHandle(pid, pipe, pid, 0, 0,
win32con.DUPLICATE_SAME_ACCESS)
- win32file.CloseHandle(pipe)
+ pipe.Close()
return dup
if __name__ == '__main__':
More information about the Zope-Checkins
mailing list