##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
#
##############################################################################
"""
Zope signal handlers for clean shutdown, restart and log rotation.

$Id: Signals.py,v 1.1.60.2 2005/04/13 03:08:56 sidnei Exp $
"""
__version__='$Revision: 1.1.60.2 $'[11:-2]

# Interesting signals and associated actions...
#  SIGHUP  (1)  - restart
#  SIGINT  (2)  - shutdown clean
#  SIGUSR1 (10) - pack (reserved, not used)
#  SIGUSR2 (12) - log reopen
#  SIGTERM (15) - shutdown fast

import os
import zLOG

if os.name == 'nt':
    try:
        from WinSignalHandler import SignalHandler
    except ImportError:
        msg = ('Can not install signal handlers.  Please install '
               '(or upgrade) your pywin32 installation '
               '(https://sf.net/projects/pywin32)')
        zLOG.LOG('Z2', zLOG.PROBLEM, msg)
        SignalHandler = None
else:
    from SignalHandler import SignalHandler

import sys
import Lifetime

def shutdownFastHandler():
    """Shutdown cleanly on SIGTERM. This is registered first,
       so it should be called after all other handlers."""
    zLOG.LOG('Z2', zLOG.INFO , "Shutting down fast")
    Lifetime.shutdown(0,fast=1)


def shutdownHandler():
    """Shutdown cleanly on SIGINT. This is registered first,
       so it should be called after all other handlers."""
    zLOG.LOG('Z2', zLOG.INFO , "Shutting down")
    sys.exit(0)

def restartHandler():
    """Restart cleanly on SIGHUP. This is registered first, so it
       should be called after all other SIGHUP handlers."""
    zLOG.LOG('Z2', zLOG.INFO , "Restarting")
    Lifetime.shutdown(1)

def logfileReopenHandler():
    """Reopen log files on SIGUSR2. This is registered first, so it
       should be called after all other SIGUSR2 handlers."""
    from zLOG.EventLogger import event_logger
    from ZServer.AccessLogger import access_logger
    from ZServer.DebugLogger import debug_logger
    for logger in (event_logger, access_logger, debug_logger):
        logger.reopen()
    zLOG.LOG('Z2', zLOG.INFO, "Log files reopened successfully")

# On Windows, a 'reopen' is useless - the file can not be renamed
# while open, so we perform a trivial 'rotate'.
def logfileRotateHandler():
    """Rotate log files on SIGUSR2. Only called on Windows. This is
       registered first if we are on Windows, so it should be called
       after all other SIGUSR2 handlers."""
    zLOG.LOG('Z2', zLOG.DEBUG, "Log files rotation starting...")
    from zLOG.EventLogger import event_logger
    from ZServer.AccessLogger import access_logger
    from ZServer.DebugLogger import debug_logger
    for logger in (event_logger, access_logger, debug_logger):
        for handler in logger.logger.handlers:
            if hasattr(handler, 'rotate') and callable(handler.rotate):
                handler.rotate()
    zLOG.LOG('Z2', zLOG.INFO, "Log files rotation complete")

def packHandler():
    """ Packs the main database.  Not safe to call under a signal
    handler, because it blocks the main thread """
    zLOG.LOG('Z2', zLOG.INFO, 'Packing main ZODB database')
    import Globals
    try:
        db = Globals.opened[0]
        db.pack()
        zLOG.LOG('Z2', zLOG.INFO,
                'Database packing launched or completed successfully')
    except:
        zLOG.LOG('Z2', zLOG.INFO,
                 'Call to pack failed!', error=sys.exc_info())

def registerZopeSignals():
    from signal import SIGTERM, SIGINT
    try:
        from signal import SIGHUP, SIGUSR1, SIGUSR2
    except ImportError:
        # Windows doesn't have these (but also doesn't care what the exact
        # numbers are)
        SIGHUP = 1
        SIGUSR1 = 10
        SIGUSR2 = 12

    if not SignalHandler:
        return
    SignalHandler.registerHandler(SIGTERM, shutdownFastHandler)
    SignalHandler.registerHandler(SIGINT, shutdownHandler)
    if os.name != 'nt':
        SignalHandler.registerHandler(SIGUSR2, logfileReopenHandler)
        SignalHandler.registerHandler(SIGHUP, restartHandler)
    else:
        # See comment above - Windows 'rotates' rather than 'reopens'
        SignalHandler.registerHandler(SIGUSR2, logfileRotateHandler)
        # Restart not currently implemented on Windows (the service itself 
        # generally is restarted when necessary)
    # SIGUSR1 is nominally reserved for pack, but we dont have an
    # implementation that is stable yet because if the signal handler
    # fires it will be caught in the main thread and all network operations
    # will cease until it's finished.
    # (The above is *not* True for Windows - a different thread is used to
    # catch the signals.  This probably could be switched on for Windows
    # if anyone cares)
    #SignalHandler.registerHandler(SIGUSR1, packHandler)
