[Zope-Checkins] CVS: Zope/ZServer/medusa - asyncore.py:1.18 http_server.py:1.32 logger.py:1.16

Chris McDonough chrism@zope.com
Tue, 11 Jun 2002 18:02:48 -0400


Update of /cvs-repository/Zope/ZServer/medusa
In directory cvs.zope.org:/tmp/cvs-serv24893/medusa

Modified Files:
	asyncore.py http_server.py logger.py 
Log Message:
These patches provide clean signal handling and logfile rotation to Zope.

All Zope process will respond to signals in the specified manner:

  SIGHUP -  close open database connections and sockets, then restart the
            process

  SIGTERM - close open database connections and sockets, then shut down.

  SIGINT  - same as SIGTERM

  SIGUSR2 - rotate all Zope log files (z2.log, event log, detailed log)

The common idiom for doing automated logfile rotation will become:

kill -USR2 `cat /path/to/var/z2.pid`

The common idiom for doing "prophylactic" restarts will become:

kill -HUP `cat /path/to/var/z2.pid`

When a process is interrupted via ctrl-C or via a TERM signal (INT, TERM),
all open database connections and sockets will be closed before
the process dies.  This will speed up restart time for sites that
use a FileStorage as its index will be written to the filesystem before
shutdown. 

Unspecified signals kill the process without doing cleanup.




=== Zope/ZServer/medusa/asyncore.py 1.17 => 1.18 ===
                 self.handle_close()
                 return ''
+            if why[0] == EAGAIN:
+                # Happens as a result of a nonfatal signal when select is
+                # interrupted
+                return ''
             else:
                 raise socket.error, why
 
@@ -524,12 +528,15 @@
             # NOTE: this is a difference from the Python 2.2 library
             # version of asyncore.py. This prevents a hanging condition
             # on Linux 2.2 based systems.
-            while 1:
+            i = 0
+            while i < 5: # this is a guess
                 try:
                     return apply (os.read, (self.fd,)+args)
                 except exceptions.OSError, why:
                     if why[0] != EAGAIN:
                         raise
+                    else:
+                        i = i + 1
 
         def send (self, *args):
             return apply (os.write, (self.fd,)+args)


=== Zope/ZServer/medusa/http_server.py 1.31 => 1.32 ===
         self.total_clients.increment()
         try:
-            conn, addr = self.accept()
+            tup = self.accept()
         except socket.error:
                 # linux: on rare occasions we get a bogus socket back from
                 # accept.  socketmodule.c:makesockaddr complains that the
                 # address family is unknown.  We don't want the whole server
                 # to shut down because of this.
-            self.log_info ('warning: server accept() threw an exception', 'warning')
+            self.log_info ('warning: server accept() threw an exception',
+                           'warning')
+            self.total_clients.decrement()
             return
+        try:
+            conn, addr = tup
         except TypeError:
-                # unpack non-sequence.  this can happen when a read event
-                # fires on a listening socket, but when we call accept()
-                # we get EWOULDBLOCK, so dispatcher.accept() returns None.
-                # Seen on FreeBSD3.
-            self.log_info ('warning: server accept() threw EWOULDBLOCK', 'warning')
+            # unpack non-sequence.  this can happen when a read event
+            # fires on a listening socket, but when we call accept()
+            # we get EWOULDBLOCK, so dispatcher.accept() returns None.
+            # Seen on FreeBSD3 and Linux.
+            #self.log_info ('warning: server accept() returned %s '
+            #               '(EWOULDBLOCK?)' % tup, 'warning')
+            self.total_clients.decrement()
             return
-            
+
         self.channel_class (self, conn, addr)
         
     def install_handler (self, handler, back=0):


=== Zope/ZServer/medusa/logger.py 1.15 => 1.16 ===
         # pass this either a path or a file object.
     def __init__ (self, file, flush=1, mode='a'):
+
+        self.filename = None
+
         if type(file) == type(''):
             if (file == '-'):
                 import sys
                 self.file = sys.stdout
             else:
+                self.filename = file
                 self.file = open (file, mode)
         else:
             self.file = file
         self.do_flush = flush
         
+    def reopen(self):
+        if self.filename:
+            self.file.close()
+            self.file = open(self.filename,'a')            
+
     def __repr__ (self):
         return '<file logger: %s>' % self.file