Path: blob/main/contrib/kyua/utils/signals/interrupts.cpp
48199 views
// Copyright 2012 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/signals/interrupts.hpp"2930extern "C" {31#include <sys/types.h>3233#include <signal.h>34#include <unistd.h>35}3637#include <cstdlib>38#include <cstring>39#include <set>4041#include "utils/logging/macros.hpp"42#include "utils/process/operations.hpp"43#include "utils/sanity.hpp"44#include "utils/signals/exceptions.hpp"45#include "utils/signals/programmer.hpp"4647namespace signals = utils::signals;48namespace process = utils::process;495051namespace {525354/// The interrupt signal that fired, or -1 if none.55static volatile int fired_signal = -1;565758/// Collection of PIDs.59typedef std::set< pid_t > pids_set;606162/// List of processes to kill upon reception of a signal.63static pids_set pids_to_kill;646566/// Programmer status for the SIGHUP signal.67static std::unique_ptr< signals::programmer > sighup_handler;68/// Programmer status for the SIGINT signal.69static std::unique_ptr< signals::programmer > sigint_handler;70/// Programmer status for the SIGTERM signal.71static std::unique_ptr< signals::programmer > sigterm_handler;727374/// Signal mask to restore after exiting a signal inhibited section.75static sigset_t global_old_sigmask;767778/// Whether there is an interrupts_handler object in existence or not.79static bool interrupts_handler_active = false;808182/// Whether there is an interrupts_inhibiter object in existence or not.83static std::size_t interrupts_inhibiter_active = 0;848586/// Generic handler to capture interrupt signals.87///88/// From this handler, we record that an interrupt has happened so that89/// check_interrupt() can know whether there execution has to be stopped or not.90/// We also terminate any of our child processes (started by the91/// utils::process::children class) so that any ongoing wait(2) system calls92/// terminate.93///94/// \param signo The signal that caused this handler to be called.95static void96signal_handler(const int signo)97{98static const char* message = "[-- Signal caught; please wait for "99"cleanup --]\n";100if (::write(STDERR_FILENO, message, std::strlen(message)) == -1) {101// We are exiting: the message printed here is only for informational102// purposes. If we fail to print it (which probably means something103// is really bad), there is not much we can do within the signal104// handler, so just ignore this.105}106107fired_signal = signo;108109for (pids_set::const_iterator iter = pids_to_kill.begin();110iter != pids_to_kill.end(); ++iter) {111process::terminate_group(*iter);112}113}114115116/// Installs signal handlers for potential interrupts.117///118/// \pre Must not have been called before.119/// \post The various sig*_handler global variables are atomically updated.120static void121setup_handlers(void)122{123PRE(sighup_handler.get() == NULL);124PRE(sigint_handler.get() == NULL);125PRE(sigterm_handler.get() == NULL);126127// Create the handlers on the stack first so that, if any of them fails, the128// stack unwinding cleans things up.129std::unique_ptr< signals::programmer > tmp_sighup_handler(130new signals::programmer(SIGHUP, signal_handler));131std::unique_ptr< signals::programmer > tmp_sigint_handler(132new signals::programmer(SIGINT, signal_handler));133std::unique_ptr< signals::programmer > tmp_sigterm_handler(134new signals::programmer(SIGTERM, signal_handler));135136// Now, update the global pointers, which is an operation that cannot fail.137sighup_handler = std::move(tmp_sighup_handler);138sigint_handler = std::move(tmp_sigint_handler);139sigterm_handler = std::move(tmp_sigterm_handler);140}141142143/// Uninstalls the signal handlers installed by setup_handlers().144static void145cleanup_handlers(void)146{147sighup_handler->unprogram(); sighup_handler.reset();148sigint_handler->unprogram(); sigint_handler.reset();149sigterm_handler->unprogram(); sigterm_handler.reset();150}151152153154/// Masks the signals installed by setup_handlers().155///156/// \param[out] old_sigmask The old signal mask to save via the157/// \code oset \endcode argument with sigprocmask(2).158static void159mask_signals(sigset_t* old_sigmask)160{161sigset_t mask;162sigemptyset(&mask);163sigaddset(&mask, SIGALRM);164sigaddset(&mask, SIGHUP);165sigaddset(&mask, SIGINT);166sigaddset(&mask, SIGTERM);167const int ret = ::sigprocmask(SIG_BLOCK, &mask, old_sigmask);168INV(ret != -1);169}170171172/// Resets the signal masking put in place by mask_signals().173///174/// \param[in] old_sigmask The old signal mask to restore via the175/// \code set \endcode argument with sigprocmask(2).176static void177unmask_signals(sigset_t* old_sigmask)178{179const int ret = ::sigprocmask(SIG_SETMASK, old_sigmask, NULL);180INV(ret != -1);181}182183184} // anonymous namespace185186187/// Constructor that sets up the signal handlers.188signals::interrupts_handler::interrupts_handler(void) :189_programmed(false)190{191PRE(!interrupts_handler_active);192setup_handlers();193_programmed = true;194interrupts_handler_active = true;195}196197198/// Destructor that removes the signal handlers.199///200/// Given that this is a destructor and it can't report errors back to the201/// caller, the caller must attempt to call unprogram() on its own.202signals::interrupts_handler::~interrupts_handler(void)203{204if (_programmed) {205LW("Destroying still-programmed signals::interrupts_handler object");206try {207unprogram();208} catch (const error& e) {209UNREACHABLE;210}211}212}213214215/// Unprograms all signals captured by the interrupts handler.216///217/// \throw system_error If the unprogramming of any signal fails.218void219signals::interrupts_handler::unprogram(void)220{221PRE(_programmed);222223// Modify the control variables first before unprogramming the handlers. If224// we fail to do the latter, we do not want to try again because we will not225// succeed (and we'll cause a crash due to failed preconditions).226_programmed = false;227interrupts_handler_active = false;228229cleanup_handlers();230fired_signal = -1;231}232233234/// Constructor that sets up signal masking.235signals::interrupts_inhibiter::interrupts_inhibiter(void)236{237sigset_t old_sigmask;238mask_signals(&old_sigmask);239if (interrupts_inhibiter_active == 0) {240global_old_sigmask = old_sigmask;241}242++interrupts_inhibiter_active;243}244245246/// Destructor that removes signal masking.247signals::interrupts_inhibiter::~interrupts_inhibiter(void)248{249if (interrupts_inhibiter_active > 1) {250--interrupts_inhibiter_active;251} else {252interrupts_inhibiter_active = false;253unmask_signals(&global_old_sigmask);254}255}256257258/// Checks if an interrupt has fired.259///260/// Calls to this function should be sprinkled in strategic places through the261/// code protected by an interrupts_handler object.262///263/// Only one call to this function will raise an exception per signal received.264/// This is to allow executing cleanup actions without reraising interrupt265/// exceptions unless the user has fired another interrupt.266///267/// \throw interrupted_error If there has been an interrupt.268void269signals::check_interrupt(void)270{271if (fired_signal != -1) {272const int original_fired_signal = fired_signal;273fired_signal = -1;274throw interrupted_error(original_fired_signal);275}276}277278279/// Registers a child process to be killed upon reception of an interrupt.280///281/// \pre Must be called with interrupts being inhibited. The caller must ensure282/// that the call call to fork() and the addition of the PID happen atomically.283///284/// \param pid The PID of the child process. Must not have been yet regsitered.285void286signals::add_pid_to_kill(const pid_t pid)287{288PRE(interrupts_inhibiter_active);289PRE(pids_to_kill.find(pid) == pids_to_kill.end());290pids_to_kill.insert(pid);291}292293294/// Unregisters a child process previously registered via add_pid_to_kill().295///296/// \pre Must be called with interrupts being inhibited. This is not necessary,297/// but pushing this to the caller simplifies our logic and provides consistency298/// with the add_pid_to_kill() call.299///300/// \param pid The PID of the child process. Must have been registered301/// previously, and the process must have already been awaited for.302void303signals::remove_pid_to_kill(const pid_t pid)304{305PRE(interrupts_inhibiter_active);306PRE(pids_to_kill.find(pid) != pids_to_kill.end());307pids_to_kill.erase(pid);308}309310311