Commit f6f0154f authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3405] Globally check for duplicated signals in SignalSets.

parent 7e2adb23
......@@ -21,9 +21,25 @@ using namespace isc::util::io;
namespace {
/// @brief Returns a pointer to static collection of signals.
/// @brief Returns a pointer to the global set of registered signals.
///
/// @return Static collection of signals.
/// Multiple instances of @c SignalSet may use this pointer to access
/// and update the set.
///
/// @return Pointer to the global set of registered signals. This pointer
/// is always initialized and points to a valid object.
std::set<int>* getRegisteredSignals() {
static std::set<int> registered_signals;
return (&registered_signals);
}
/// @brief Returns a pointer to static collection of signals received.
///
/// Multiple instances of @c SignalSet may use this pointer to access
/// and update the queue of signals received.
///
/// @return Static collection of signals received. This pointer is always
/// initialized and points to a valid object.
std::list<int>* getSignalStates() {
static std::list<int> states;
return (&states);
......@@ -84,20 +100,22 @@ SignalSet::SignalSet(const int sig0, const int sig1, const int sig2) {
SignalSet::~SignalSet() {
// Set default signal handlers.
clear();
try {
clear();
} catch (...) {
// Not a good thing to throw from a destructor. in fact this should
// not throw an exception because we just unregister the signals
// that we have previously registered. So the signal codes are fine.
}
}
void
SignalSet::add(const int sig) {
std::pair<Pool::iterator, bool> ret = registered_signals_.insert(sig);
if (!ret.second) {
isc_throw(SignalSetError, "attempt to register a duplicate signal "
<< sig);
}
insert(sig);
struct sigaction sa;
sa.sa_handler = internalHandler;
if (sigaction(sig, &sa, 0) < 0) {
registered_signals_.erase(sig);
erase(sig);
isc_throw(SignalSetError, "failed to register a signal handler for"
" signal " << sig << ": " << strerror(errno));
}
......@@ -105,8 +123,12 @@ SignalSet::add(const int sig) {
void
SignalSet::clear() {
Pool all_signals = registered_signals_;
for (Pool::const_iterator it = all_signals.begin();
// Iterate over a copy of the registered signal set because the
// remove function is erasing the elements and we don't want to
// erase the elements we are iterating over. This would cause
// a segfault.
std::set<int> all_signals = local_signals_;
for (std::set<int>::const_iterator it = all_signals.begin();
it != all_signals.end(); ++it) {
remove(*it);
}
......@@ -121,6 +143,17 @@ SignalSet::getNext() const {
return (*states->begin());
}
void
SignalSet::erase(const int sig) {
if (local_signals_.find(sig) == local_signals_.end()) {
isc_throw(SignalSetError, "failed to unregister signal " << sig
<< " from a signal set: signal is not owned by the"
" signal set");
}
getRegisteredSignals()->erase(sig);
local_signals_.erase(sig);
}
void
SignalSet::handleNext(SignalHandler signal_handler) {
block();
......@@ -137,11 +170,23 @@ SignalSet::handleNext(SignalHandler signal_handler) {
unblock();
}
void
SignalSet::insert(const int sig) {
std::set<int>* global_signals = getRegisteredSignals();
if ((global_signals->find(sig) != global_signals->end()) ||
(local_signals_.find(sig) != local_signals_.end())) {
isc_throw(SignalSetError, "attempt to register a duplicate signal "
<< sig);
}
global_signals->insert(sig);
local_signals_.insert(sig);
}
void
SignalSet::maskSignals(const int mask) const {
sigset_t new_set;
for (Pool::const_iterator it = registered_signals_.begin();
it != registered_signals_.end(); ++it) {
for (std::set<int>::const_iterator it = getRegisteredSignals()->begin();
it != getRegisteredSignals()->end(); ++it) {
sigaddset(&new_set, *it);
}
sigprocmask(mask, &new_set, 0);
......@@ -149,14 +194,18 @@ SignalSet::maskSignals(const int mask) const {
void
SignalSet::remove(const int sig) {
if (registered_signals_.find(sig) != registered_signals_.end()) {
// Unregister only if we own this signal.
if (local_signals_.find(sig) != local_signals_.end()) {
struct sigaction sa;
sa.sa_handler = SIG_DFL;
if (sigaction(sig, &sa, 0) < 0) {
isc_throw(SignalSetError, "unable to restore original signal"
" handler for signal: " << sig);
}
registered_signals_.erase(sig);
erase(sig);
} else {
isc_throw(SignalSetError, "failed to unregister signal " << sig
<< ": this signal is not owned by the signal set");
}
}
......
......@@ -53,11 +53,11 @@ typedef boost::function<void(int signum)> SignalHandler;
/// spent on handling the signal and process interruption. The process
/// can later check the signals received and call the handlers on its
/// descretion by calling a @c isc::util::io::SignalSet::handleNext function.
///
/// @note This class is not thread safe. It uses static variables and
/// functions to track a global state of signal registration and received
/// signals' queue.
class SignalSet : public boost::noncopyable {
private:
/// @brief Defines a type representing a collection of signals.
typedef std::set<int> Pool;
public:
/// @brief Constructor installing one signal.
......@@ -84,6 +84,11 @@ public:
/// the signal is invalid.
SignalSet(const int sig0, const int sig1, const int sig2);
/// @brief Destructor.
///
/// Removes installed handlers.
~SignalSet();
/// @brief Installs the handler for the specified signal.
///
/// This function adds a signal to the set. When the signal is received
......@@ -132,6 +137,22 @@ private:
maskSignals(SIG_BLOCK);
}
/// @brief Removes the signal from the set.
///
/// This function removes only a signal which is owned by this signal set.
///
/// @param sig Signal to be removed.
/// @throw SignalSetError if the signal being removed is not owned by this
/// signal set.
void erase(const int sig);
/// @brief Insert a signal to the set.
///
/// @param sig Signal to be inserted.
/// @throw SignalSetError if a signal being inserted has already been
/// registered in this or other signal set.
void insert(const int sig);
/// @brief Applies a mask to all signals in the set.
///
/// This function is used by @c SignalSet::block and @c SignalSet::unblock
......@@ -147,8 +168,8 @@ private:
maskSignals(SIG_UNBLOCK);
}
/// @brief Holds a collection of installed signals.
Pool registered_signals_;
/// @brief Stores the set of signals registered in this signal set.
std::set<int> local_signals_;
};
}
......
......@@ -26,7 +26,8 @@ using namespace isc::util::io;
class SignalSetTest : public ::testing::Test {
public:
SignalSetTest() {
SignalSetTest()
: signal_set_() {
handler_calls_ = 0;
signum_ = -1;
}
......@@ -48,13 +49,14 @@ public:
static int handler_calls_;
static int signum_;
boost::shared_ptr<SignalSet> signal_set_;
SignalSetPtr signal_set_;
};
int SignalSetTest::handler_calls_ = 0;
int SignalSetTest::signum_ = -1;
/// Check that the signals are recorded by the signal handlers.
TEST_F(SignalSetTest, twoSignals) {
// Register handlers for two signals.
signal_set_.reset(new SignalSet(SIGHUP, SIGINT));
......@@ -92,5 +94,46 @@ TEST_F(SignalSetTest, twoSignals) {
EXPECT_NO_THROW(signal_set_->remove(SIGINT));
}
// Check that each signal set removes only the signals that it has been used
// to register.
TEST_F(SignalSetTest, twoSignalSets) {
// Register handlers for SIGHUP using one signal set.
ASSERT_NO_THROW(signal_set_.reset(new SignalSet(SIGHUP)));
// Define another signal set and register a different signal.
SignalSetPtr other_set;
ASSERT_NO_THROW(other_set.reset(new SignalSet(SIGINT)));
// The SIGHUP has been already registsred with the other signal
// set, so it should not be possible to register it again.
ASSERT_THROW(other_set->add(SIGHUP), SignalSetError);
// It shouldn't be possible to remove the signal registered in a different
// signal set.
ASSERT_THROW(other_set->remove(SIGHUP), SignalSetError);
// Remove all signals from the first signal set. The signals registered
// with the other signal signal set should be preserved.
ASSERT_NO_THROW(signal_set_->clear());
// Check indirectly that the SIGINT is still registered. An attempt to
// register registered signal should result in failure.
EXPECT_THROW(other_set->add(SIGINT), SignalSetError);
// But, we should be able to regsiter SIGHUP.
EXPECT_NO_THROW(other_set->add(SIGHUP));
}
/// Check that it is not possible to duplicate signals.
TEST_F(SignalSetTest, duplicates) {
ASSERT_NO_THROW(signal_set_.reset(new SignalSet(SIGHUP)));
// It shouldn't be possible to register the same signal.
EXPECT_THROW(signal_set_->add(SIGHUP), SignalSetError);
// But ok to register a different one.
EXPECT_NO_THROW(signal_set_->add(SIGTERM));
// Now, let's define other signal set.
SignalSetPtr other;
// SIGINT hasn't been registered in the first signal set
// so it should be fine to register.
ASSERT_NO_THROW(other.reset(new SignalSet(SIGINT)));
// SIGHUP has been already registered in the first signal set so
// an attempt to register it again should result in a failure.
EXPECT_THROW(other->add(SIGHUP), SignalSetError);
}
} // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment