// -*- 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 // // 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 #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include //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