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