Commit a5e103c0 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac3591' (logging lockfile fix)

Conflicts:
	ChangeLog
	Makefile.am
	src/lib/dhcpsrv/daemon.cc
parents 4f942fc1 d4556e1d
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012,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
......@@ -12,11 +12,16 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// This file requires LOCKFILE_DIR to be defined. It points to the default
// directory where lockfile will be created.
#include <log/interprocess/interprocess_sync_file.h>
#include <string>
#include <cerrno>
#include <cstring>
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <string.h>
......@@ -46,33 +51,32 @@ InterprocessSyncFile::do_lock(int cmd, short l_type) {
if (fd_ == -1) {
std::string lockfile_path = LOCKFILE_DIR;
const char* const env = getenv("KEA_FROM_BUILD");
const char* const env = getenv("KEA_LOCKFILE_DIR");
if (env != NULL) {
lockfile_path = env;
}
const char* const env2 = getenv("KEA_FROM_BUILD_LOCALSTATEDIR");
if (env2 != NULL) {
lockfile_path = env2;
}
const char* const env3 = getenv("KEA_LOCKFILE_DIR_FROM_BUILD");
if (env3 != NULL) {
lockfile_path = env3;
}
lockfile_path += "/" + task_name_ + "_lockfile";
// Open the lockfile in the constructor so it doesn't do the access
// checks every time a message is logged.
const mode_t mode = umask(0111);
fd_ = open(lockfile_path.c_str(), O_CREAT | O_RDWR, 0660);
const mode_t mode = umask(S_IXUSR | S_IXGRP | S_IXOTH); // 0111
fd_ = open(lockfile_path.c_str(), O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); // 0660
umask(mode);
if (fd_ == -1) {
isc_throw(InterprocessSyncFileError,
"Unable to use interprocess sync lockfile ("
<< std::strerror(errno) << "): " << lockfile_path);
std::stringstream tmp;
// We failed to create a lockfile. This means that the logging
// system is unusable. We need to report the issue using plain
// print to stderr.
tmp << "Unable to use interprocess sync lockfile ("
<< std::strerror(errno) << "): " << lockfile_path;
std::cerr << tmp.str() << std::endl;
// And then throw exception as usual.
isc_throw(InterprocessSyncFileError, tmp.str());
}
}
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-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
......@@ -16,10 +16,13 @@
#include <util/unittests/run_all.h>
#include <stdlib.h>
// This file uses TEST_DATA_TOPBUILDDIR macro, which must point to a writeable
// directory. It will be used for creating a logger lockfile.
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
setenv("KEA_LOCKFILE_DIR_FROM_BUILD", TEST_DATA_TOPBUILDDIR, 1);
setenv("KEA_LOCKFILE_DIR", TEST_DATA_TOPBUILDDIR, 1);
return (isc::util::unittests::run_all());
}
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -18,8 +18,10 @@
#include <stdarg.h>
#include <stdio.h>
#include <cstring>
#include <boost/lexical_cast.hpp>
#include <boost/static_assert.hpp>
#include <boost/algorithm/string.hpp>
#include <log4cplus/configurator.h>
#include <log4cplus/loggingmacros.h>
......@@ -33,6 +35,7 @@
#include <log/message_dictionary.h>
#include <log/message_types.h>
#include <log/interprocess/interprocess_sync_file.h>
#include <log/interprocess/interprocess_sync_null.h>
#include <util/strutil.h>
......@@ -45,6 +48,20 @@ using namespace std;
namespace isc {
namespace log {
/// @brief detects whether file locking is enabled or disabled
///
/// The lockfile is enabled by default. The only way to disable it is to
/// set KEA_LOCKFILE_DIR variable to 'none'.
/// @return true if lockfile is enabled, false otherwise
bool lockfileEnabled() {
const char* const env = getenv("KEA_LOCKFILE_DIR");
if (env && boost::iequals(string(env), string("none"))) {
return (false);
}
return (true);
}
// Constructor. The setting of logger_ must be done when the variable is
// constructed (instead of being left to the body of the function); at least
// one compiler requires that all member variables be constructed before the
......@@ -52,9 +69,13 @@ namespace log {
// default constructor.
LoggerImpl::LoggerImpl(const string& name) :
name_(expandLoggerName(name)),
logger_(log4cplus::Logger::getInstance(name_)),
sync_(new interprocess::InterprocessSyncFile("logger"))
logger_(log4cplus::Logger::getInstance(name_))
{
if (lockfileEnabled()) {
sync_ = new interprocess::InterprocessSyncFile("logger");
} else {
sync_ = new interprocess::InterprocessSyncNull("logger");
}
}
// Destructor. (Here because of virtual declaration.)
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -15,6 +15,7 @@
#include <string>
#include <log/logger_support.h>
#include <log/logger_manager.h>
#include <log/logger_name.h>
using namespace std;
......@@ -50,5 +51,75 @@ initLogger(const string& root, isc::log::Severity severity, int dbglevel,
LoggerManager::init(root, severity, dbglevel, file, buffer);
}
// Reset characteristics of the root logger to that set by the environment
// variables KEA_LOGGER_SEVERITY, KEA_LOGGER_DBGLEVEL and KEA_LOGGER_DESTINATION.
void
setDefaultLoggingOutput(bool verbose) {
using namespace isc::log;
// Constants: not declared static as this is function is expected to be
// called once only
const string DEVNULL = "/dev/null";
const string STDOUT = "stdout";
const string STDERR = "stderr";
const string SYSLOG = "syslog";
const string SYSLOG_COLON = "syslog:";
// Get the destination. If not specified, assume /dev/null. (The default
// severity for unit tests is DEBUG, which generates a lot of output.
// Routing the logging to /dev/null will suppress that, whilst still
// ensuring that the code paths are tested.)
const char* destination = getenv("KEA_LOGGER_DESTINATION");
const string dest((destination == NULL) ? DEVNULL : destination);
// Prepare the objects to define the logging specification
LoggerSpecification spec(getRootLoggerName(),
keaLoggerSeverity(verbose ? isc::log::DEBUG :
isc::log::INFO),
keaLoggerDbglevel(isc::log::MAX_DEBUG_LEVEL));
OutputOption option;
// Set up output option according to destination specification
if (dest == STDOUT) {
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDOUT;
} else if (dest == STDERR) {
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDERR;
} else if (dest == SYSLOG) {
option.destination = OutputOption::DEST_SYSLOG;
// Use default specified in OutputOption constructor for the
// syslog destination
} else if (dest.find(SYSLOG_COLON) == 0) {
option.destination = OutputOption::DEST_SYSLOG;
// Must take account of the string actually being "syslog:"
if (dest == SYSLOG_COLON) {
cerr << "**ERROR** value for KEA_LOGGER_DESTINATION of " <<
SYSLOG_COLON << " is invalid, " << SYSLOG <<
" will be used instead\n";
// Use default for logging facility
} else {
// Everything else in the string is the facility name
option.facility = dest.substr(SYSLOG_COLON.size());
}
} else {
// Not a recognised destination, assume a file.
option.destination = OutputOption::DEST_FILE;
option.filename = dest;
}
// ... and set the destination
spec.addOutputOption(option);
LoggerManager manager;
manager.process(spec);
}
} // namespace log
} // namespace isc
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -69,6 +69,17 @@ void initLogger(const std::string& root,
int dbglevel = 0, const char* file = NULL,
bool buffer = false);
/// \brief Reset root logger characteristics
///
/// This is a simplified interface into the resetting of the characteristics
/// of the root logger. It is aimed for use in unit tests and initial
/// phase of bring up before logging configuration is parsed and applied.
/// It uses KEA_LOGGER_DESTINATION environment variable to specify
/// logging destination.
/// @param verbose defines whether logging should be verbose or not
void setDefaultLoggingOutput(bool verbose = true);
} // namespace log
} // namespace isc
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -75,77 +75,6 @@ keaLoggerDbglevel(int defdbglevel) {
return (defdbglevel);
}
// Reset characteristics of the root logger to that set by the environment
// variables KEA_LOGGER_SEVERITY, KEA_LOGGER_DBGLEVEL and KEA_LOGGER_DESTINATION.
void
resetUnitTestRootLogger() {
using namespace isc::log;
// Constants: not declared static as this is function is expected to be
// called once only
const string DEVNULL = "/dev/null";
const string STDOUT = "stdout";
const string STDERR = "stderr";
const string SYSLOG = "syslog";
const string SYSLOG_COLON = "syslog:";
// Get the destination. If not specified, assume /dev/null. (The default
// severity for unit tests is DEBUG, which generates a lot of output.
// Routing the logging to /dev/null will suppress that, whilst still
// ensuring that the code paths are tested.)
const char* destination = getenv("KEA_LOGGER_DESTINATION");
const string dest((destination == NULL) ? DEVNULL : destination);
// Prepare the objects to define the logging specification
LoggerSpecification spec(getRootLoggerName(),
keaLoggerSeverity(isc::log::DEBUG),
keaLoggerDbglevel(isc::log::MAX_DEBUG_LEVEL));
OutputOption option;
// Set up output option according to destination specification
if (dest == STDOUT) {
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDOUT;
} else if (dest == STDERR) {
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDERR;
} else if (dest == SYSLOG) {
option.destination = OutputOption::DEST_SYSLOG;
// Use default specified in OutputOption constructor for the
// syslog destination
} else if (dest.find(SYSLOG_COLON) == 0) {
option.destination = OutputOption::DEST_SYSLOG;
// Must take account of the string actually being "syslog:"
if (dest == SYSLOG_COLON) {
cerr << "**ERROR** value for KEA_LOGGER_DESTINATION of " <<
SYSLOG_COLON << " is invalid, " << SYSLOG <<
" will be used instead\n";
// Use default for logging facility
} else {
// Everything else in the string is the facility name
option.facility = dest.substr(SYSLOG_COLON.size());
}
} else {
// Not a recognised destination, assume a file.
option.destination = OutputOption::DEST_FILE;
option.filename = dest;
}
// ... and set the destination
spec.addOutputOption(option);
LoggerManager manager;
manager.process(spec);
}
// Logger Run-Time Initialization via Environment Variables
void initLogger(isc::log::Severity severity, int dbglevel) {
......@@ -162,7 +91,7 @@ void initLogger(isc::log::Severity severity, int dbglevel) {
const char* localfile = getenv("KEA_LOGGER_LOCALMSG");
// Set a directory for creating lockfiles when running tests
setenv("KEA_LOCKFILE_DIR_FROM_BUILD", TOP_BUILDDIR, 1);
setenv("KEA_LOCKFILE_DIR", TOP_BUILDDIR, 0);
// Initialize logging
initLogger(root, isc::log::DEBUG, isc::log::MAX_DEBUG_LEVEL, localfile);
......@@ -172,7 +101,7 @@ void initLogger(isc::log::Severity severity, int dbglevel) {
// in the environment variables. (The two-step approach is used as the
// setUnitTestRootLoggerCharacteristics() function is used in several
// places in the Kea tests, and it avoid duplicating code.)
resetUnitTestRootLogger();
isc::log::setDefaultLoggingOutput();
}
} // namespace log
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -70,6 +70,10 @@ namespace log {
/// be overridden by the tester. It is not intended for use in production
/// code.
///
/// @note: Do NOT use this function in production code as it creates
/// lockfile in the build dir. That's ok for tests, but not
/// ok for production code.
///
/// @todo: Rename. This function overloads the initLogger() function that can
/// be used to initialize production programs. This may lead to confusion.
void initLogger(isc::log::Severity severity = isc::log::DEBUG,
......@@ -107,17 +111,6 @@ isc::log::Severity keaLoggerSeverity(isc::log::Severity defseverity);
/// \return Debug level to use.
int keaLoggerDbglevel(int defdbglevel);
/// \brief Reset root logger characteristics
///
/// This is a simplified interface into the resetting of the characteristics
/// of the root logger. It is aimed for use in unit tests and resets the
/// characteristics of the root logger to use a severity, debug level and
/// destination set by the environment variables KEA_LOGGER_SEVERITY,
/// KEA_LOGGER_DBGLEVEL and KEA_LOGGER_DESTINATION.
void
resetUnitTestRootLogger();
} // namespace log
} // namespace isc
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -30,7 +30,7 @@ class LoggerLevelImplTest : public ::testing::Test {
protected:
LoggerLevelImplTest() {
// Ensure logging set to default for unit tests
resetUnitTestRootLogger();
setDefaultLoggingOutput();
}
~LoggerLevelImplTest()
......
......@@ -31,7 +31,7 @@ protected:
LoggerLevelTest() {
// Logger initialization is done in main(). As logging tests may
// alter the default logging output, it is reset here.
resetUnitTestRootLogger();
setDefaultLoggingOutput();
}
~LoggerLevelTest() {
LoggerManager::reset();
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,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
......@@ -23,7 +23,7 @@ protected:
LoggerSupportTest() {
// Logger initialization is done in main(). As logging tests may
// alter the default logging output, it is reset here.
resetUnitTestRootLogger();
setDefaultLoggingOutput();
}
~LoggerSupportTest() {
}
......
......@@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS=$(KEA_CXXFLAGS)
noinst_SCRIPTS = dhcp_test_lib.sh
if HAVE_GTEST
noinst_LTLIBRARIES = libkea-testutils.la
......
......@@ -421,3 +421,94 @@ version_test() {
test_finish 1
fi
}
# This test verifies that the server is using logger variable
# KEA_LOCKFILE_DIR properly (it should be used to point out to the directory,
# where lockfile should be created. Also, "none" value means to not create
# the lockfile at all).
logger_vars_test() {
test_name=${1} # Test name
# Log the start of the test and print test name.
test_start ${test_name}
# Remove dangling Kea instances and remove log files.
cleanup
# Create bogus configuration file. We don't really want the server to start,
# just want it to log something and die. Empty config is an easy way to
# enforce that behavior.
create_config "{ }"
printf "Please ignore any config error messages.\n"
# Remember old KEA_LOCKFILE_DIR
KEA_LOCKFILE_DIR_OLD=${KEA_LOCKFILE_DIR}
# Set lockfile directory to current directory.
KEA_LOCKFILE_DIR=.
# Start Kea.
start_kea ${bin_path}/${bin}
# Wait for Kea to process the invalid configuration and die.
sleep 1
# Check if it is still running. It should have terminated.
get_pids ${bin}
if [ ${_GET_PIDS_NUM} -ne 0 ]; then
printf "ERROR: expected Kea process to not start. Found %d processes"
printf " running.\n" ${_GET_PIDS_NUM}
# Revert to the old KEA_LOCKFILE_DIR value
KEA_LOCKFILE_DIR=${KEA_LOCKFILE_DIR_OLD}
clean_exit 1
fi
if [ ! -f "./logger_lockfile" ]; then
printf "ERROR: Expect ${bin} to create logger_lockfile in the\n"
printf "current directory, but no such file exists.\n"
# Revert to the old KEA_LOCKFILE_DIR value
KEA_LOCKFILE_DIR=${KEA_LOCKFILE_DIR__OLD}
clean_exit 1
fi
# Remove the lock file
rm -f ./logger_lockfile
# Tell Kea to NOT create logfiles at all
KEA_LOCKFILE_DIR="none"
# Start Kea.
start_kea ${bin_path}/${bin}
# Wait for Kea to process the invalid configuration and die.
sleep 1
# Check if it is still running. It should have terminated.
get_pids ${bin}
if [ ${_GET_PIDS_NUM} -ne 0 ]; then
printf "ERROR: expected Kea process to not start. Found %d processes"
printf " running.\n" ${_GET_PIDS_NUM}
# Revert to the old KEA_LOCKFILE_DIR value
KEA_LOCKFILE_DIR=${KEA_LOCKFILE_DIR_OLD}
clean_exit 1
fi
if [ -f "./logger_lockfile" ]; then
printf "ERROR: Expect ${bin} to NOT create logger_lockfile in the\n"
printf "current directory, but the file exists."
# Revert to the old KEA_LOCKFILE_DIR value
KEA_LOCKFILE_DIR=${KEA_LOCKFILE_DIR_OLD}
clean_exit 1
fi
# Revert to the old KEA_LOCKFILE_DIR value
printf "Reverting KEA_LOCKFILE_DIR to ${KEA_LOCKFILE_DIR_OLD}\n"
KEA_LOCKFILE_DIR=${KEA_LOCKFILE_DIR_OLD}
test_finish 0
}
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012,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
......@@ -16,10 +16,13 @@
#include <util/unittests/run_all.h>
#include <stdlib.h>
// This file uses TEST_DATA_TOPBUILDDIR macro, which must point to a writeable
// directory. It will be used for creating a logger lockfile.
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
setenv("KEA_LOCKFILE_DIR_FROM_BUILD", TEST_DATA_TOPBUILDDIR, 1);
setenv("KEA_LOCKFILE_DIR", TEST_DATA_TOPBUILDDIR, 1);
return (isc::util::unittests::run_all());
}
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