##############################################################################
#
# 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
#
##############################################################################
"""
Zope signal handlers for clean shutdown, restart and log rotation.

$Id: Signals.py 40218 2005-11-18 14:39:19Z andreasjung $
"""
__version__='$Revision: 1.3 $'[11:-2]

import logging
import sys, os

import Lifetime

logger = logging.getLogger("Z2")

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)')
        logger.warning(msg)
        SignalHandler = None
else:
    from SignalHandler import SignalHandler

def shutdownFastHandler():
    """Shutdown cleanly on SIGTERM. This is registered first,
       so it should be called after all other handlers."""
    logger.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."""
    logger.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."""
    logger.info("Restarting")
    Lifetime.shutdown(1)

class LogfileReopenHandler:
    """Reopen log files on SIGUSR2.

    This is registered first, so it should be called after all other
    SIGUSR2 handlers.
    """
    def __init__(self, loggers):
        self.loggers = [log for log in loggers if log is not None]

    def __call__(self):
        for log in self.loggers:
            log.reopen()
        logger.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'.
class LogfileRotateHandler:
    """Rotate log files on SIGUSR2. Only called on Windows. This is 
       registered first, so it should be called after all other SIGUSR2 
       handlers."""
    def __init__(self, loggers):
        self.loggers = [log for log in loggers if log is not None]

    def __call__(self):
        logger.debug("Log files rotation starting...")
        for log in self.loggers:
            for f in log.handler_factories:
                handler = f()
                if hasattr(handler, 'rotate') and callable(handler.rotate):
                    handler.rotate()
        logger.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 """
    logger.info('Packing main ZODB database')
    import Globals
    try:
        db = Globals.opened[0]
        db.pack()
        logger.info('Database packing launched or completed successfully')
    except:
        logger.exception('Call to pack failed!')
        

def registerZopeSignals(loggers):
    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(SIGHUP, restartHandler)
        SignalHandler.registerHandler(SIGUSR2, LogfileReopenHandler(loggers))
    else:
        # no restart handler on windows.
        # Log files get 'rotated', not 'reopened'
        SignalHandler.registerHandler(SIGUSR2, LogfileRotateHandler(loggers))
    # 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)
