Path: blob/main/contrib/kyua/utils/signals/interrupts_test.cpp
48178 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 <signal.h>32#include <unistd.h>33}3435#include <cstdlib>36#include <iostream>3738#include <atf-c++.hpp>3940#include "utils/format/macros.hpp"41#include "utils/fs/path.hpp"42#include "utils/process/child.ipp"43#include "utils/process/status.hpp"44#include "utils/signals/exceptions.hpp"45#include "utils/signals/programmer.hpp"4647namespace fs = utils::fs;48namespace process = utils::process;49namespace signals = utils::signals;505152namespace {535455/// Set to the signal that fired; -1 if none.56static volatile int fired_signal = -1;575859/// Test handler for signals.60///61/// \post fired_signal is set to the signal that triggered the handler.62///63/// \param signo The signal that triggered the handler.64static void65signal_handler(const int signo)66{67PRE(fired_signal == -1 || fired_signal == signo);68fired_signal = signo;69}707172/// Child process that pauses waiting to be killed.73static void74pause_child(void)75{76sigset_t mask;77sigemptyset(&mask);78// We loop waiting for signals because we want the parent process to send us79// a SIGKILL that we cannot handle, not just any non-deadly signal.80for (;;) {81std::cerr << F("Waiting for any signal; pid=%s\n") % ::getpid();82::sigsuspend(&mask);83std::cerr << F("Signal received; pid=%s\n") % ::getpid();84}85}868788/// Checks that interrupts_handler() handles a particular signal.89///90/// This indirectly checks the check_interrupt() function, which is not part of91/// the class but is tightly related.92///93/// \param signo The signal to check.94/// \param explicit_unprogram Whether to call interrupts_handler::unprogram()95/// explicitly before letting the object go out of scope.96static void97check_interrupts_handler(const int signo, const bool explicit_unprogram)98{99fired_signal = -1;100101signals::programmer test_handler(signo, signal_handler);102103{104signals::interrupts_handler interrupts;105106// No pending interrupts at first.107signals::check_interrupt();108109// Send us an interrupt and check for it.110::kill(getpid(), signo);111ATF_REQUIRE_THROW_RE(signals::interrupted_error,112F("Interrupted by signal %s") % signo,113signals::check_interrupt());114115// Interrupts should have been cleared now, so this should not throw.116signals::check_interrupt();117118// Check to see if a second interrupt is detected.119::kill(getpid(), signo);120ATF_REQUIRE_THROW_RE(signals::interrupted_error,121F("Interrupted by signal %s") % signo,122signals::check_interrupt());123124// And ensure the interrupt was cleared again.125signals::check_interrupt();126127if (explicit_unprogram) {128interrupts.unprogram();129}130}131132ATF_REQUIRE_EQ(-1, fired_signal);133::kill(getpid(), signo);134ATF_REQUIRE_EQ(signo, fired_signal);135136test_handler.unprogram();137}138139140/// Checks that interrupts_inhibiter() handles a particular signal.141///142/// \param signo The signal to check.143static void144check_interrupts_inhibiter(const int signo)145{146signals::programmer test_handler(signo, signal_handler);147148{149signals::interrupts_inhibiter inhibiter;150{151signals::interrupts_inhibiter nested_inhibiter;152::kill(::getpid(), signo);153ATF_REQUIRE_EQ(-1, fired_signal);154}155::kill(::getpid(), signo);156ATF_REQUIRE_EQ(-1, fired_signal);157}158ATF_REQUIRE_EQ(signo, fired_signal);159160test_handler.unprogram();161}162163164} // anonymous namespace165166167ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sighup);168ATF_TEST_CASE_BODY(interrupts_handler__sighup)169{170// We run this twice in sequence to ensure that we can actually program two171// interrupts handlers in a row.172check_interrupts_handler(SIGHUP, true);173check_interrupts_handler(SIGHUP, false);174}175176177ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sigint);178ATF_TEST_CASE_BODY(interrupts_handler__sigint)179{180// We run this twice in sequence to ensure that we can actually program two181// interrupts handlers in a row.182check_interrupts_handler(SIGINT, true);183check_interrupts_handler(SIGINT, false);184}185186187ATF_TEST_CASE_WITHOUT_HEAD(interrupts_handler__sigterm);188ATF_TEST_CASE_BODY(interrupts_handler__sigterm)189{190// We run this twice in sequence to ensure that we can actually program two191// interrupts handlers in a row.192check_interrupts_handler(SIGTERM, true);193check_interrupts_handler(SIGTERM, false);194}195196197ATF_TEST_CASE(interrupts_handler__kill_children);198ATF_TEST_CASE_HEAD(interrupts_handler__kill_children)199{200set_md_var("timeout", "10");201}202ATF_TEST_CASE_BODY(interrupts_handler__kill_children)203{204std::unique_ptr< process::child > child1(process::child::fork_files(205pause_child, fs::path("/dev/stdout"), fs::path("/dev/stderr")));206std::unique_ptr< process::child > child2(process::child::fork_files(207pause_child, fs::path("/dev/stdout"), fs::path("/dev/stderr")));208209signals::interrupts_handler interrupts;210211// Our children pause until the reception of a signal. Interrupting212// ourselves will cause the signal to be re-delivered to our children due to213// the interrupts_handler semantics. If this does not happen, the wait214// calls below would block indefinitely and cause our test to time out.215::kill(::getpid(), SIGHUP);216217const process::status status1 = child1->wait();218ATF_REQUIRE(status1.signaled());219ATF_REQUIRE_EQ(SIGKILL, status1.termsig());220const process::status status2 = child2->wait();221ATF_REQUIRE(status2.signaled());222ATF_REQUIRE_EQ(SIGKILL, status2.termsig());223}224225226ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigalrm);227ATF_TEST_CASE_BODY(interrupts_inhibiter__sigalrm)228{229check_interrupts_inhibiter(SIGALRM);230}231232233ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sighup);234ATF_TEST_CASE_BODY(interrupts_inhibiter__sighup)235{236check_interrupts_inhibiter(SIGHUP);237}238239240ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigint);241ATF_TEST_CASE_BODY(interrupts_inhibiter__sigint)242{243check_interrupts_inhibiter(SIGINT);244}245246247ATF_TEST_CASE_WITHOUT_HEAD(interrupts_inhibiter__sigterm);248ATF_TEST_CASE_BODY(interrupts_inhibiter__sigterm)249{250check_interrupts_inhibiter(SIGTERM);251}252253254ATF_INIT_TEST_CASES(tcs)255{256ATF_ADD_TEST_CASE(tcs, interrupts_handler__sighup);257ATF_ADD_TEST_CASE(tcs, interrupts_handler__sigint);258ATF_ADD_TEST_CASE(tcs, interrupts_handler__sigterm);259ATF_ADD_TEST_CASE(tcs, interrupts_handler__kill_children);260261ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sigalrm);262ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sighup);263ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sigint);264ATF_ADD_TEST_CASE(tcs, interrupts_inhibiter__sigterm);265}266267268