src/lib/testutils/Makefile
src/lib/testutils/testdata/Makefile
src/lib/util/io/Makefile
+ src/lib/util/io/tests/Makefile
src/lib/util/Makefile
src/lib/util/python/doxygen2pydoc.py
src/lib/util/python/gen_wiredata.py
+SUBDIRS = . tests
AM_CXXFLAGS = $(B10_CXXFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
lib_LTLIBRARIES = libkea-util-io.la
libkea_util_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc
libkea_util_io_la_SOURCES += socketsession.h socketsession.cc sockaddr_util.h
+libkea_util_io_la_SOURCES += signal_set.cc signal_set.h
libkea_util_io_la_SOURCES += pktinfo_utilities.h
libkea_util_io_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/io/signal_set.h>
+
+#include <list>
+
+using namespace isc;
+using namespace isc::util::io;
+
+namespace {
+
+std::list<int>* getSignalStates() {
+ static std::list<int> states;
+ return (&states);
+}
+
+void internalHandler(int sig) {
+ std::list<int>* states = getSignalStates();
+ for (std::list<int>::const_iterator it = states->begin();
+ it != states->end(); ++it) {
+ if (sig == *it) {
+ return;
+ }
+ }
+ states->push_back(sig);
+}
+
+}
+
+namespace isc {
+namespace util {
+namespace io {
+
+SignalSet::SignalSet(const int sig0) {
+ add(sig0);
+}
+
+SignalSet::SignalSet(const int sig0, const int sig1) {
+ add(sig0);
+ add(sig1);
+}
+
+SignalSet::SignalSet(const int sig0, const int sig1, const int sig2) {
+ add(sig0);
+ add(sig1);
+ add(sig2);
+}
+
+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);
+ }
+ struct sigaction sa;
+ sa.sa_handler = internalHandler;
+ if (sigaction(sig, &sa, 0) < 0) {
+ registered_signals_.erase(sig);
+ isc_throw(SignalSetError, "failed to register a signal handler for"
+ " signal " << sig << ": " << strerror(errno));
+ }
+}
+
+void
+SignalSet::clear() {
+ Pool all_signals = registered_signals_;
+ for (Pool::const_iterator it = all_signals.begin();
+ it != all_signals.end(); ++it) {
+ remove(*it);
+ }
+}
+
+int
+SignalSet::getNext() const {
+ std::list<int>* states = getSignalStates();
+ if (states->empty()) {
+ return (-1);
+ }
+ return (*states->begin());
+}
+
+void
+SignalSet::handleNext(SignalHandler signal_handler) {
+ block();
+ int signum = getNext();
+ if (signum >= 0) {
+ popNext();
+ try {
+ signal_handler(signum);
+ } catch (...) {
+ unblock();
+ throw;
+ }
+ }
+ unblock();
+}
+
+void
+SignalSet::maskSignals(const int mask) const {
+ sigset_t new_set;
+ for (Pool::const_iterator it = registered_signals_.begin();
+ it != registered_signals_.end(); ++it) {
+ sigaddset(&new_set, *it);
+ }
+ sigprocmask(mask, &new_set, 0);
+}
+
+void
+SignalSet::popNext() const {
+ std::list<int>* states = getSignalStates();
+ if (!states->empty()) {
+ states->pop_front();
+ }
+}
+
+void
+SignalSet::remove(const int sig) {
+ if (registered_signals_.find(sig) != registered_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);
+ }
+}
+
+} // end of isc::util::io
+} // end of isc::util
+} // end of isc
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SIGNAL_SET_H
+#define SIGNAL_SET_H
+
+#include <exceptions/exceptions.h>
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+#include <set>
+#include <signal.h>
+
+namespace isc {
+namespace util {
+namespace io {
+
+class SignalSetError : public Exception {
+public:
+ SignalSetError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+typedef boost::function<void(int signum)> SignalHandler;
+
+class SignalSet : public boost::noncopyable {
+private:
+ typedef std::set<int> Pool;
+
+public:
+
+ SignalSet(const int sig0);
+
+ SignalSet(const int sig0, const int sig1);
+
+ SignalSet(const int sig0, const int sig1, const int sig2);
+
+ /// @throw SignalSetError if signal being added duplicates an existing
+ /// signal.
+ void add(const int sig);
+
+ void clear();
+
+ int getNext() const;
+
+ void handleNext(SignalHandler signal_handler);
+
+ void remove(const int sig);
+
+private:
+
+ void block() const {
+ maskSignals(SIG_BLOCK);
+ }
+
+ Pool::iterator erase(const int signal);
+
+ void maskSignals(const int mask) const;
+
+ void popNext() const;
+
+ void unblock() const {
+ maskSignals(SIG_UNBLOCK);
+ }
+
+ Pool registered_signals_;
+};
+
+}
+}
+}
+
+#endif // SIGNAL_SET_H
+
--- /dev/null
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += signal_set_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la
+run_unittests_LDADD += $(GTEST_LDADD)
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/logger_support.h>
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ return (result);
+}
--- /dev/null
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/io/signal_set.h>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <signal.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::util::io;
+
+class SignalSetTest : public ::testing::Test {
+public:
+
+ SignalSetTest()
+ : handler_calls_(0),
+ signum_ (-1) {
+ }
+
+ ~SignalSetTest() {
+ if (signal_set_) {
+ signal_set_->clear();
+ }
+ }
+
+ void handleNext() {
+ signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler,
+ this, _1));
+ }
+
+ void testHandler(int signum) {
+ signum_ = signum;
+ ++handler_calls_;
+ }
+
+ int handler_calls_;
+ int signum_;
+ boost::shared_ptr<SignalSet> signal_set_;
+
+};
+
+TEST_F(SignalSetTest, twoSignals) {
+ // Register handlers for two signals.
+ signal_set_.reset(new SignalSet(SIGHUP, SIGINT));
+ // Send SIGHUP signal to the process.
+ ASSERT_EQ(0, raise(SIGHUP));
+ // The SIGHUP should be the next one in the queue to be handled.
+ EXPECT_EQ(SIGHUP, signal_set_->getNext());
+ // But, no handlers should have been called yet.
+ EXPECT_EQ(0, handler_calls_);
+ // Send a different signal.
+ ASSERT_EQ(0, raise(SIGINT));
+ // The SIGHUP hasn't been handled yet so it should still be the first
+ // one in the queue.
+ EXPECT_EQ(SIGHUP, signal_set_->getNext());
+ // No handlers have been called yet.
+ EXPECT_EQ(0, handler_calls_);
+ // Raise another SIGHUP before the first one has been handled. The
+ // second one should be dropped.
+ ASSERT_EQ(0, raise(SIGHUP));
+ // Execute the first handler (for SIGHUP).
+ handleNext();
+ // The handler should have been called once and the signal
+ // handled should be SIGHUP.
+ EXPECT_EQ(1, handler_calls_);
+ EXPECT_EQ(SIGHUP, signum_);
+ // Next signal to be handled should be SIGINT.
+ EXPECT_EQ(SIGINT, signal_set_->getNext());
+ handleNext();
+ EXPECT_EQ(2, handler_calls_);
+ EXPECT_EQ(SIGINT, signum_);
+ // There should be no more waiting handlers.
+ EXPECT_EQ(-1, signal_set_->getNext());
+ // Make sure that signals can be unregistered.
+ EXPECT_NO_THROW(signal_set_->remove(SIGHUP));
+ EXPECT_NO_THROW(signal_set_->remove(SIGINT));
+}
+
+} // end of anonymous namespace