Blame SignalHandler.cc

Packit a4aae4
Packit a4aae4
// -*- mode: c++; c-basic-offset:4 -*-
Packit a4aae4
Packit a4aae4
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
Packit a4aae4
// Access Protocol.
Packit a4aae4
Packit a4aae4
// Copyright (c) 2002,2003 OPeNDAP, Inc.
Packit a4aae4
// Author: James Gallagher <jgallagher@opendap.org>
Packit a4aae4
//
Packit a4aae4
// This library is free software; you can redistribute it and/or
Packit a4aae4
// modify it under the terms of the GNU Lesser General Public
Packit a4aae4
// License as published by the Free Software Foundation; either
Packit a4aae4
// version 2.1 of the License, or (at your option) any later version.
Packit a4aae4
//
Packit a4aae4
// This library is distributed in the hope that it will be useful,
Packit a4aae4
// but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4aae4
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4aae4
// Lesser General Public License for more details.
Packit a4aae4
//
Packit a4aae4
// You should have received a copy of the GNU Lesser General Public
Packit a4aae4
// License along with this library; if not, write to the Free Software
Packit a4aae4
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit a4aae4
//
Packit a4aae4
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
Packit a4aae4
Packit a4aae4
// (c) COPYRIGHT URI/MIT 1994-2002
Packit a4aae4
// Please read the full copyright statement in the file COPYRIGHT_URI.
Packit a4aae4
//
Packit a4aae4
// Authors:
Packit a4aae4
//      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
Packit a4aae4
Packit a4aae4
#include "config.h"
Packit a4aae4
Packit a4aae4
#include <cstdlib>
Packit a4aae4
Packit a4aae4
#include <signal.h>
Packit a4aae4
#include <pthread.h>
Packit a4aae4
Packit a4aae4
#ifdef HAVE_UNISTD_H
Packit a4aae4
#include <unistd.h> //for _exit
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#include "SignalHandler.h"
Packit a4aae4
#include "util.h"
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
EventHandler *SignalHandler::d_signal_handlers[NSIG];
Packit a4aae4
Sigfunc *SignalHandler::d_old_handlers[NSIG];
Packit a4aae4
SignalHandler *SignalHandler::d_instance = 0;
Packit a4aae4
Packit a4aae4
// instance_control is used to ensure that in a MT environment d_instance is
Packit a4aae4
// correctly initialized.
Packit a4aae4
static pthread_once_t instance_control = PTHREAD_ONCE_INIT;
Packit a4aae4
Packit a4aae4
/// Private static void method.
Packit a4aae4
void
Packit a4aae4
SignalHandler::initialize_instance()
Packit a4aae4
{
Packit a4aae4
    // MT-Safe if called via pthread_once or similar
Packit a4aae4
    SignalHandler::d_instance = new SignalHandler;
Packit a4aae4
    atexit(SignalHandler::delete_instance);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/// Private static void method.
Packit a4aae4
void
Packit a4aae4
SignalHandler::delete_instance()
Packit a4aae4
{
Packit a4aae4
    if (SignalHandler::d_instance) {
Packit a4aae4
        for (int i = 0; i < NSIG; ++i) {
Packit a4aae4
        	// Fortify warns about a leak because the EventHandler objects
Packit a4aae4
        	// are not deleted, but that's OK - this is a singleton and
Packit a4aae4
        	// so the 'leak' is really just a constant amount of memory that
Packit a4aae4
        	// gets used.
Packit a4aae4
        	d_signal_handlers[i] = 0;
Packit a4aae4
            d_old_handlers[i] = 0;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        delete SignalHandler::d_instance;
Packit a4aae4
        SignalHandler::d_instance = 0;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This private method is the adapter between the C-style interface of the
Packit a4aae4
    signal subsystem and C++'s method interface. This uses the lookup table
Packit a4aae4
    to find an instance of EventHandler and calls that instance's
Packit a4aae4
    handle_signal method.
Packit a4aae4
Packit a4aae4
    @param signum The number of the signal. */
Packit a4aae4
void
Packit a4aae4
SignalHandler::dispatcher(int signum)
Packit a4aae4
{
Packit a4aae4
    // Perform a sanity check...
Packit a4aae4
    if (SignalHandler::d_signal_handlers[signum] != 0)
Packit a4aae4
        // Dispatch the handler's hook method.
Packit a4aae4
        SignalHandler::d_signal_handlers[signum]->handle_signal(signum);
Packit a4aae4
Packit a4aae4
    Sigfunc *old_handler = SignalHandler::d_old_handlers[signum];
Packit a4aae4
    if (old_handler == SIG_IGN || old_handler == SIG_ERR)
Packit a4aae4
        return;
Packit a4aae4
    else if (old_handler == SIG_DFL) {
Packit a4aae4
        switch (signum) {
Packit a4aae4
#if 0
Packit a4aae4
#ifndef WIN32
Packit a4aae4
        case SIGHUP:
Packit a4aae4
        case SIGKILL:
Packit a4aae4
        case SIGUSR1:
Packit a4aae4
        case SIGUSR2:
Packit a4aae4
        case SIGPIPE:
Packit a4aae4
        case SIGALRM:
Packit a4aae4
#endif
Packit a4aae4
        case SIGINT:
Packit a4aae4
        case SIGTERM: _exit(EXIT_FAILURE);
Packit a4aae4
Packit a4aae4
            // register_handler() should never allow any fiddling with
Packit a4aae4
            // signals other than those listed above.
Packit a4aae4
        default: abort();
Packit a4aae4
#endif
Packit a4aae4
        // Calling _exit() or abort() is not a good thing for a library to be
Packit a4aae4
        // doing. This results in a warning from rpmlint
Packit a4aae4
        default:
Packit a4aae4
            throw Error(internal_error, "Signal handler operation on an unsupported signal.");
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
    else
Packit a4aae4
        old_handler(signum);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get a pointer to the single instance of SignalHandler. */
Packit a4aae4
SignalHandler*
Packit a4aae4
SignalHandler::instance()
Packit a4aae4
{
Packit a4aae4
    pthread_once(&instance_control, initialize_instance);
Packit a4aae4
Packit a4aae4
    return d_instance;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Register an event handler. By default run any previously registered
Packit a4aae4
    action/handler such as those installed using \c sigaction(). For signals
Packit a4aae4
    such as SIGALRM (the alarm signal) this may not be what you want; see the
Packit a4aae4
    \e override parameter. See also the class description.
Packit a4aae4
Packit a4aae4
    @param signum Bind the event handler to this signal number. Limited to
Packit a4aae4
    those signals that, according to POSIX.1, cause process termination.
Packit a4aae4
    @param eh A pointer to the EventHandler for \c signum.
Packit a4aae4
    @param override If \c true, do not run the default handler/action.
Packit a4aae4
    Instead run \e eh and then treat the signal as if the original action was
Packit a4aae4
    SIG_IGN. Default is false.
Packit a4aae4
    @return A pointer to the old EventHandler or null. */
Packit a4aae4
EventHandler *
Packit a4aae4
SignalHandler::register_handler(int signum, EventHandler *eh, bool override)
Packit a4aae4
{
Packit a4aae4
    // Check first for improper use.
Packit a4aae4
    switch (signum) {
Packit a4aae4
#ifndef WIN32
Packit a4aae4
        case SIGHUP:
Packit a4aae4
        case SIGKILL:
Packit a4aae4
        case SIGUSR1:
Packit a4aae4
        case SIGUSR2:
Packit a4aae4
        case SIGPIPE:
Packit a4aae4
        case SIGALRM:
Packit a4aae4
#endif
Packit a4aae4
        case SIGINT:
Packit a4aae4
        case SIGTERM: break;
Packit a4aae4
Packit a4aae4
        default: throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                string("Call to register_handler with unsupported signal (")
Packit a4aae4
                + long_to_string(signum) + string(")."));
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // Save the old EventHandler
Packit a4aae4
    EventHandler *old_eh = SignalHandler::d_signal_handlers[signum];
Packit a4aae4
Packit a4aae4
    SignalHandler::d_signal_handlers[signum] = eh;
Packit a4aae4
Packit a4aae4
    // Register the dispatcher to handle this signal. See Stevens, Advanced
Packit a4aae4
    // Programming in the UNIX Environment, p.298.
Packit a4aae4
#ifndef WIN32
Packit a4aae4
    struct sigaction sa;
Packit a4aae4
    sa.sa_handler = dispatcher;
Packit a4aae4
    sigemptyset(&sa.sa_mask);
Packit a4aae4
    sa.sa_flags = 0;
Packit a4aae4
Packit a4aae4
    // Try to suppress restarting system calls if we're handling an alarm.
Packit a4aae4
    // This lets alarms block I/O calls that would normally restart. 07/18/03
Packit a4aae4
    // jhrg
Packit a4aae4
    if (signum == SIGALRM) {
Packit a4aae4
#ifdef SA_INTERUPT
Packit a4aae4
        sa.sa_flags |= SA_INTERUPT;
Packit a4aae4
#endif
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
#ifdef SA_RESTART
Packit a4aae4
        sa.sa_flags |= SA_RESTART;
Packit a4aae4
#endif
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    struct sigaction osa; // extract the old handler/action
Packit a4aae4
Packit a4aae4
    if (sigaction(signum, &sa, &osa) < 0)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Could not register a signal handler.");
Packit a4aae4
Packit a4aae4
    // Take care of the case where this interface is used to register a
Packit a4aae4
    // handler more than once. We want to make sure that the dispatcher is
Packit a4aae4
    // not installed as the 'old handler' because that results in an infinite
Packit a4aae4
    // loop. 02/10/04 jhrg
Packit a4aae4
    if (override)
Packit a4aae4
        SignalHandler::d_old_handlers[signum] = SIG_IGN;
Packit a4aae4
    else if (osa.sa_handler != dispatcher)
Packit a4aae4
        SignalHandler::d_old_handlers[signum] = osa.sa_handler;
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
    return old_eh;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Remove the event hander.
Packit a4aae4
    @param signum The signal number of the handler to remove.
Packit a4aae4
    @return The old event handler */
Packit a4aae4
EventHandler *
Packit a4aae4
SignalHandler::remove_handler(int signum)
Packit a4aae4
{
Packit a4aae4
    EventHandler *old_eh = SignalHandler::d_signal_handlers[signum];
Packit a4aae4
Packit a4aae4
    SignalHandler::d_signal_handlers[signum] = 0;
Packit a4aae4
Packit a4aae4
    return old_eh;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
} // namespace libdap