diff --git a/configure.ac b/configure.ac index 09de67fd2b..5bbba16025 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,20 @@ AC_CONFIG_MACRO_DIR([m4macros]) # Checks for programs. AC_PROG_CXX +# Enable low-performing debugging facilities? This option optionally +# enables some debugging aids that perform slowly and hence aren't built +# by default. +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--enable-debug], + [enable debugging (default is no)]), + [case "${enableval}" in + yes) debug_enabled=yes ;; + no) debug_enabled=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; + esac],[debug_enabled=no]) +AM_CONDITIONAL([DEBUG_ENABLED], [test x$debug_enabled = xyes]) +AM_COND_IF([DEBUG_ENABLED], [AC_DEFINE([ENABLE_DEBUG], [1], [Enable low-performing debugging facilities?])]) + # Libtool configuration # @@ -1112,6 +1126,7 @@ AC_CONFIG_FILES([Makefile src/bin/bindctl/Makefile src/bin/bindctl/tests/Makefile src/bin/cfgmgr/Makefile + src/bin/cfgmgr/local_plugins/Makefile src/bin/cfgmgr/plugins/Makefile src/bin/cfgmgr/plugins/tests/Makefile src/bin/cfgmgr/tests/Makefile @@ -1421,6 +1436,7 @@ Features: $enable_features Developer: + Enable Debugging: $debug_enabled Google Tests: $enable_gtest Valgrind: $found_valgrind C++ Code Coverage: $USE_LCOV diff --git a/src/bin/cfgmgr/Makefile.am b/src/bin/cfgmgr/Makefile.am index e9e0ccaf85..9c73f79ee6 100644 --- a/src/bin/cfgmgr/Makefile.am +++ b/src/bin/cfgmgr/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . plugins tests +SUBDIRS = . plugins local_plugins tests pkglibexecdir = $(libexecdir)/@PACKAGE@ diff --git a/src/bin/cfgmgr/local_plugins/Makefile.am b/src/bin/cfgmgr/local_plugins/Makefile.am new file mode 100644 index 0000000000..2f4dd39767 --- /dev/null +++ b/src/bin/cfgmgr/local_plugins/Makefile.am @@ -0,0 +1,11 @@ +# Nothing is installed from this directory. This local_plugins +# directory overrides the plugins directory when lettuce is run, and the +# spec file here is used to serve the static zone from the source tree +# for testing (instead of installation prefix). + +noinst_DATA = datasrc.spec + +datasrc.spec: ../plugins/datasrc.spec.pre + $(SED) -e "s|@@STATIC_ZONE_FILE@@|$(abs_top_builddir)/src/lib/datasrc/static.zone|;s|@@SQLITE3_DATABASE_FILE@@|$(abs_top_builddir)/local.zone.sqlite3|" ../plugins/datasrc.spec.pre >$@ + +CLEANFILES = datasrc.spec diff --git a/src/bin/cfgmgr/plugins/Makefile.am b/src/bin/cfgmgr/plugins/Makefile.am index e6ed1275c8..5967abd4ac 100644 --- a/src/bin/cfgmgr/plugins/Makefile.am +++ b/src/bin/cfgmgr/plugins/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = tests EXTRA_DIST = README logging.spec tsig_keys.spec datasrc.spec: datasrc.spec.pre - $(SED) -e "s|@@PKGDATADIR@@|$(pkgdatadir)|;s|@@LOCALSTATEDIR@@|$(localstatedir)|" datasrc.spec.pre >$@ + $(SED) -e "s|@@STATIC_ZONE_FILE@@|$(pkgdatadir)/static.zone|;s|@@SQLITE3_DATABASE_FILE@@|$(localstatedir)/$(PACKAGE)/zone.sqlite3|" datasrc.spec.pre >$@ config_plugindir = @prefix@/share/@PACKAGE@/config_plugins config_plugin_DATA = logging.spec tsig_keys.spec datasrc.spec diff --git a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in index f2c6a974a2..6d5bd77e75 100644 --- a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in +++ b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in @@ -12,7 +12,7 @@ { "type": "sqlite3", "params": { - "database_file": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3" + "database_file": "@@SQLITE3_DATABASE_FILE@@" } } ], @@ -20,7 +20,7 @@ { "type": "static", "cache-enable": false, - "params": "@@PKGDATADIR@@/static.zone" + "params": "@@STATIC_ZONE_FILE@@" } ] }, diff --git a/src/lib/python/bind10_config.py.in b/src/lib/python/bind10_config.py.in index b8975cf625..6db64e26c0 100644 --- a/src/lib/python/bind10_config.py.in +++ b/src/lib/python/bind10_config.py.in @@ -51,7 +51,7 @@ def reload(): # tree the programs in the tree (not installed ones) will be used. # # B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for - # tests where we want to use variuos types of configuration within the test + # tests where we want to use various types of configuration within the test # environment. (We may want to make it even more generic so that the path # is passed from the boss process) if "B10_FROM_SOURCE" in os.environ: @@ -60,6 +60,8 @@ def reload(): else: DATA_PATH = os.environ["B10_FROM_SOURCE"] PLUGIN_PATHS = [os.environ["B10_FROM_SOURCE"] + + '/src/bin/cfgmgr/local_plugins', + os.environ["B10_FROM_SOURCE"] + '/src/bin/cfgmgr/plugins'] programdirs = ['auth', 'cfgmgr', 'cmdctl', 'ddns', 'dhcp6', 'msgq', 'resolver', 'sockcreator', 'stats', 'xfrin', 'xfrout', diff --git a/src/lib/util/threads/sync.cc b/src/lib/util/threads/sync.cc index c98a7a6b06..46a56465a4 100644 --- a/src/lib/util/threads/sync.cc +++ b/src/lib/util/threads/sync.cc @@ -12,6 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include "config.h" + #include "sync.h" #include @@ -31,12 +33,16 @@ namespace thread { class Mutex::Impl { public: - Impl() : - locked_count(0) + Impl() +#ifdef ENABLE_DEBUG + : locked_count(0) +#endif // ENABLE_DEBUG {} + pthread_mutex_t mutex; - // Only in debug mode +#ifdef ENABLE_DEBUG size_t locked_count; +#endif // ENABLE_DEBUG }; namespace { @@ -70,12 +76,20 @@ Mutex::Mutex() : isc_throw(isc::InvalidOperation, std::strerror(result)); } Deinitializer deinitializer(attributes); - // TODO: Distinguish if debug mode is enabled in compilation. - // If so, it should be PTHREAD_MUTEX_NORMAL or NULL + + // If debug mode is enabled in compilation, use the slower + // error-checking mutexes that detect deadlocks. Otherwise, use fast + // mutexes which don't. See the pthread_mutexattr_settype() POSIX + // documentation which describes these type attributes. +#ifdef ENABLE_DEBUG result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK); +#else + result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_NORMAL); +#endif // ENABLE_DEBUG if (result != 0) { isc_throw(isc::InvalidOperation, std::strerror(result)); } + auto_ptr impl(new Impl); result = pthread_mutex_init(&impl->mutex, &attributes); switch (result) { @@ -93,12 +107,17 @@ Mutex::Mutex() : Mutex::~Mutex() { if (impl_ != NULL) { const int result = pthread_mutex_destroy(&impl_->mutex); + +#ifdef ENABLE_DEBUG const bool locked = impl_->locked_count != 0; +#endif // ENABLE_DEBUG + delete impl_; // We don't want to throw from the destructor. Also, if this ever // fails, something is really screwed up a lot. assert(result == 0); +#ifdef ENABLE_DEBUG // We should not try to destroy a locked mutex, bad threaded monsters // could get loose if we ever do and it is also forbidden by pthreads. @@ -106,28 +125,18 @@ Mutex::~Mutex() { // pthread_mutex_destroy should check for it already. But it seems // there are systems that don't check it. assert(!locked); +#endif // ENABLE_DEBUG } } +#ifdef ENABLE_DEBUG + void Mutex::postLockAction() { - // This assertion would fail only in non-debugging mode, in which case - // this method wouldn't be called either, so we simply assert the - // condition. assert(impl_->locked_count == 0); ++impl_->locked_count; } -void -Mutex::lock() { - assert(impl_ != NULL); - const int result = pthread_mutex_lock(&impl_->mutex); - if (result != 0) { - isc_throw(isc::InvalidOperation, std::strerror(result)); - } - postLockAction(); // Only in debug mode -} - void Mutex::preUnlockAction(bool throw_ok) { if (impl_->locked_count == 0) { @@ -141,20 +150,35 @@ Mutex::preUnlockAction(bool throw_ok) { --impl_->locked_count; } -void -Mutex::unlock() { - assert(impl_ != NULL); - preUnlockAction(false); // Only in debug mode. Ensure no throw. - const int result = pthread_mutex_unlock(&impl_->mutex); - assert(result == 0); // This should never be possible -} - -// TODO: Disable in non-debug build bool Mutex::locked() const { return (impl_->locked_count != 0); } +#endif // ENABLE_DEBUG + +void +Mutex::lock() { + assert(impl_ != NULL); + const int result = pthread_mutex_lock(&impl_->mutex); + if (result != 0) { + isc_throw(isc::InvalidOperation, std::strerror(result)); + } +#ifdef ENABLE_DEBUG + postLockAction(); // Only in debug mode +#endif // ENABLE_DEBUG +} + +void +Mutex::unlock() { + assert(impl_ != NULL); +#ifdef ENABLE_DEBUG + preUnlockAction(false); // Only in debug mode. Ensure no throw. +#endif // ENABLE_DEBUG + const int result = pthread_mutex_unlock(&impl_->mutex); + assert(result == 0); // This should never be possible +} + class CondVar::Impl { public: Impl() { @@ -187,10 +211,13 @@ CondVar::~CondVar() { void CondVar::wait(Mutex& mutex) { +#ifdef ENABLE_DEBUG mutex.preUnlockAction(true); // Only in debug mode const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex); mutex.postLockAction(); // Only in debug mode - +#else + const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex); +#endif // pthread_cond_wait should normally succeed unless mutex is completely // broken. if (result != 0) { diff --git a/src/lib/util/threads/sync.h b/src/lib/util/threads/sync.h index ff56999f99..87c78be94d 100644 --- a/src/lib/util/threads/sync.h +++ b/src/lib/util/threads/sync.h @@ -112,6 +112,9 @@ private: // Commonly called after acquiring the lock, checking and updating // internal state for debug. + // + // Note that this method is only available when the build is + // configured with debugging support. void postLockAction(); // Commonly called before releasing the lock, checking and updating @@ -121,6 +124,9 @@ private: // fails; otherwise it aborts the process. This parameter must be set // to false if the call to this shouldn't result in an exception (e.g. // when called from a destructor). + // + // Note that this method is only available when the build is + // configured with debugging support. void preUnlockAction(bool throw_ok); class Impl; diff --git a/src/lib/util/threads/tests/condvar_unittest.cc b/src/lib/util/threads/tests/condvar_unittest.cc index 77a8980704..8b1fe5445e 100644 --- a/src/lib/util/threads/tests/condvar_unittest.cc +++ b/src/lib/util/threads/tests/condvar_unittest.cc @@ -149,11 +149,15 @@ TEST_F(CondVarTest, DISABLED_destroyWhileWait) { }, ""); } +#ifdef ENABLE_DEBUG + TEST_F(CondVarTest, badWait) { // In our implementation, wait() requires acquiring the lock beforehand. EXPECT_THROW(condvar_.wait(mutex_), isc::InvalidOperation); } +#endif // ENABLE_DEBUG + TEST_F(CondVarTest, emptySignal) { // It's okay to call signal when no one waits. EXPECT_NO_THROW(condvar_.signal()); diff --git a/src/lib/util/threads/tests/lock_unittest.cc b/src/lib/util/threads/tests/lock_unittest.cc index 9c17e6fc65..498d01e00a 100644 --- a/src/lib/util/threads/tests/lock_unittest.cc +++ b/src/lib/util/threads/tests/lock_unittest.cc @@ -26,20 +26,26 @@ using namespace isc::util::thread; namespace { -// If we try to lock the debug mutex multiple times, it should throw. +#ifdef ENABLE_DEBUG + +// If we try to lock the debug mutex multiple times, it should +// throw. This test will complete properly only when pthread debugging +// facilities are enabled by configuring the code for debug build. TEST(MutexTest, lockMultiple) { - // TODO: Once we support non-debug mutexes, disable the test if we compile - // with them. Mutex mutex; EXPECT_FALSE(mutex.locked()); // Debug-only build + Mutex::Locker l1(mutex); EXPECT_TRUE(mutex.locked()); // Debug-only build + EXPECT_THROW({ Mutex::Locker l2(mutex); // Attempt to lock again. }, isc::InvalidOperation); EXPECT_TRUE(mutex.locked()); // Debug-only build } +#endif // ENABLE_DEBUG + // Destroying a locked mutex is a bad idea as well #ifdef EXPECT_DEATH TEST(MutexTest, destroyLocked) { diff --git a/tests/lettuce/configurations/auth/.gitignore b/tests/lettuce/configurations/auth/.gitignore new file mode 100644 index 0000000000..07f1b7db04 --- /dev/null +++ b/tests/lettuce/configurations/auth/.gitignore @@ -0,0 +1 @@ +/auth_basic.config diff --git a/tests/lettuce/configurations/auth/auth_basic.config.orig b/tests/lettuce/configurations/auth/auth_basic.config.orig new file mode 100644 index 0000000000..4067fb1b27 --- /dev/null +++ b/tests/lettuce/configurations/auth/auth_basic.config.orig @@ -0,0 +1,22 @@ +{ + "version": 2, + "Logging": { + "loggers": [ { + "debuglevel": 99, + "severity": "DEBUG", + "name": "*" + } ] + }, + "Auth": { + "listen_on": [ { + "port": 47806, + "address": "127.0.0.1" + } ] + }, + "Boss": { + "components": { + "b10-auth": { "kind": "needed", "special": "auth" }, + "b10-cmdctl": { "special": "cmdctl", "kind": "needed" } + } + } +} diff --git a/tests/lettuce/features/auth_basic.feature b/tests/lettuce/features/auth_basic.feature new file mode 100644 index 0000000000..4667b6441b --- /dev/null +++ b/tests/lettuce/features/auth_basic.feature @@ -0,0 +1,20 @@ +Feature: Basic Authoritative DNS server + This feature set is for testing the execution of the b10-auth + component using its default datasource configurations. This + will start it and perform queries against it. + + Scenario: Query builtin bind zone + Given I have bind10 running with configuration auth/auth_basic.config + And wait for bind10 stderr message BIND10_STARTED_CC + And wait for bind10 stderr message CMDCTL_STARTED + And wait for bind10 stderr message AUTH_SERVER_STARTED + + bind10 module Auth should be running + And bind10 module Resolver should not be running + + A query for example.com should have rcode REFUSED + A query for version.bind type TXT class CH should have rcode NOERROR + A query for authors.bind type TXT class CH should have rcode NOERROR + + # TODO: to be compatible with BIND 9 + # A query for nonexistent.bind type TXT class CH should have rcode REFUSED diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py index 8720e2d994..2bfe7d4f14 100644 --- a/tests/lettuce/features/terrain/terrain.py +++ b/tests/lettuce/features/terrain/terrain.py @@ -49,6 +49,8 @@ copylist = [ "configurations/example.org.config"], ["configurations/bindctl/bindctl.config.orig", "configurations/bindctl/bindctl.config"], + ["configurations/auth/auth_basic.config.orig", + "configurations/auth/auth_basic.config"], ["configurations/resolver/resolver_basic.config.orig", "configurations/resolver/resolver_basic.config"], ["configurations/multi_instance/multi_auth.config.orig",