summaryrefslogtreecommitdiff
path: root/lib/common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common')
-rw-r--r--lib/common/Box.h59
-rw-r--r--lib/common/BoxException.h1
-rw-r--r--lib/common/BoxPlatform.h37
-rw-r--r--lib/common/BoxTime.cpp46
-rw-r--r--lib/common/BoxTime.h3
-rw-r--r--lib/common/Configuration.cpp335
-rw-r--r--lib/common/Configuration.h76
-rw-r--r--lib/common/DebugAssertFailed.cpp4
-rw-r--r--lib/common/DebugMemLeakFinder.cpp58
-rw-r--r--lib/common/DebugPrintf.cpp4
-rw-r--r--lib/common/EventWatchFilesystemObject.cpp43
-rw-r--r--lib/common/FdGetLine.h2
-rw-r--r--lib/common/FileModificationTime.h23
-rw-r--r--lib/common/FileStream.cpp110
-rw-r--r--lib/common/FileStream.h21
-rw-r--r--lib/common/Guards.h4
-rw-r--r--lib/common/IOStream.cpp36
-rw-r--r--lib/common/IOStream.h10
-rw-r--r--lib/common/IOStreamGetLine.h2
-rw-r--r--lib/common/Logging.cpp233
-rw-r--r--lib/common/Logging.h138
-rw-r--r--lib/common/NamedLock.cpp8
-rw-r--r--lib/common/PartialReadStream.cpp3
-rw-r--r--lib/common/ReadLoggingStream.cpp28
-rw-r--r--lib/common/ReadLoggingStream.h19
-rw-r--r--lib/common/SelfFlushingStream.h71
-rw-r--r--lib/common/Test.cpp418
-rw-r--r--lib/common/Test.h430
-rw-r--r--lib/common/Timer.cpp308
-rw-r--r--lib/common/Timer.h36
-rw-r--r--lib/common/Utils.cpp179
-rw-r--r--lib/common/Utils.h5
-rw-r--r--lib/common/WaitForEvent.h2
-rwxr-xr-xlib/common/makeexception.pl.in12
34 files changed, 2001 insertions, 763 deletions
diff --git a/lib/common/Box.h b/lib/common/Box.h
index d0e7ab1e..1124a062 100644
--- a/lib/common/Box.h
+++ b/lib/common/Box.h
@@ -17,13 +17,14 @@
#include "BoxPlatform.h"
-// uncomment this line to enable full memory leak finding on all malloc-ed blocks (at least, ones used by the STL)
+// uncomment this line to enable full memory leak finding on all
+// malloc-ed blocks (at least, ones used by the STL)
//#define MEMLEAKFINDER_FULL_MALLOC_MONITORING
-#ifndef NDEBUG
- #ifdef HAVE_EXECINFO_H
- #define SHOW_BACKTRACE_ON_EXCEPTION
- #endif
+// Show backtraces on exceptions in release builds until further notice
+// (they are only logged at TRACE level anyway)
+#ifdef HAVE_EXECINFO_H
+ #define SHOW_BACKTRACE_ON_EXCEPTION
#endif
#ifdef SHOW_BACKTRACE_ON_EXCEPTION
@@ -36,14 +37,23 @@
#include "CommonException.h"
#include "Logging.h"
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
extern bool AssertFailuresToSyslog;
#define ASSERT_FAILS_TO_SYSLOG_ON {AssertFailuresToSyslog = true;}
void BoxDebugAssertFailed(const char *cond, const char *file, int line);
- #define ASSERT(cond) {if(!(cond)) {BoxDebugAssertFailed(#cond, __FILE__, __LINE__); THROW_EXCEPTION(CommonException, AssertFailed)}}
+ #define ASSERT(cond) \
+ { \
+ if(!(cond)) \
+ { \
+ BoxDebugAssertFailed(#cond, __FILE__, __LINE__); \
+ THROW_EXCEPTION_MESSAGE(CommonException, \
+ AssertFailed, #cond); \
+ } \
+ }
- // Note that syslog tracing is independent of BoxDebugTraceOn, but stdout tracing is not
+ // Note that syslog tracing is independent of BoxDebugTraceOn,
+ // but stdout tracing is not
extern bool BoxDebugTraceToSyslog;
#define TRACE_TO_SYSLOG(x) {BoxDebugTraceToSyslog = x;}
extern bool BoxDebugTraceToStdout;
@@ -52,15 +62,6 @@
extern bool BoxDebugTraceOn;
int BoxDebug_printf(const char *format, ...);
int BoxDebugTrace(const char *format, ...);
- #define TRACE0(msg) {BoxDebugTrace("%s", msg);}
- #define TRACE1(msg, a0) {BoxDebugTrace(msg, a0);}
- #define TRACE2(msg, a0, a1) {BoxDebugTrace(msg, a0, a1);}
- #define TRACE3(msg, a0, a1, a2) {BoxDebugTrace(msg, a0, a1, a2);}
- #define TRACE4(msg, a0, a1, a2, a3) {BoxDebugTrace(msg, a0, a1, a2, a3);}
- #define TRACE5(msg, a0, a1, a2, a3, a4) {BoxDebugTrace(msg, a0, a1, a2, a3, a4);}
- #define TRACE6(msg, a0, a1, a2, a3, a4, a5) {BoxDebugTrace(msg, a0, a1, a2, a3, a4, a5);}
- #define TRACE7(msg, a0, a1, a2, a3, a4, a5, a6) {BoxDebugTrace(msg, a0, a1, a2, a3, a4, a5, a6);}
- #define TRACE8(msg, a0, a1, a2, a3, a4, a5, a6, a7) {BoxDebugTrace(msg, a0, a1, a2, a3, a4, a5, a6, a7);}
#ifndef PLATFORM_DISABLE_MEM_LEAK_TESTING
#define BOX_MEMORY_LEAK_TESTING
@@ -76,16 +77,6 @@
#define TRACE_TO_SYSLOG(x) {}
#define TRACE_TO_STDOUT(x) {}
- #define TRACE0(msg)
- #define TRACE1(msg, a0)
- #define TRACE2(msg, a0, a1)
- #define TRACE3(msg, a0, a1, a2)
- #define TRACE4(msg, a0, a1, a2, a3)
- #define TRACE5(msg, a0, a1, a2, a3, a4)
- #define TRACE6(msg, a0, a1, a2, a3, a4, a5)
- #define TRACE7(msg, a0, a1, a2, a3, a4, a5, a6)
- #define TRACE8(msg, a0, a1, a2, a3, a4, a5, a6, a7)
-
// Box Backup builds release get extra information for exception logging
#define EXCEPTION_CODENAMES_EXTENDED
#define EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION
@@ -113,13 +104,21 @@
#define THROW_EXCEPTION(type, subtype) \
{ \
OPTIONAL_DO_BACKTRACE \
- BOX_WARNING("Exception thrown: " #type "(" #subtype ") at " \
- __FILE__ "(" << __LINE__ << ")") \
+ BOX_WARNING("Exception thrown: " #type "(" #subtype ") " \
+ "at " __FILE__ "(" << __LINE__ << ")") \
throw type(type::subtype); \
}
-// extra macros for converting to network byte order
+#define THROW_EXCEPTION_MESSAGE(type, subtype, message) \
+ { \
+ OPTIONAL_DO_BACKTRACE \
+ BOX_WARNING("Exception thrown: " #type "(" #subtype ") " \
+ " (" message ") at " \
+ __FILE__ "(" << __LINE__ << ")") \
+ throw type(type::subtype, message); \
+ }
+// extra macros for converting to network byte order
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
diff --git a/lib/common/BoxException.h b/lib/common/BoxException.h
index eb992f57..a8f5d7a6 100644
--- a/lib/common/BoxException.h
+++ b/lib/common/BoxException.h
@@ -11,6 +11,7 @@
#define BOXEXCEPTION__H
#include <exception>
+#include <string>
// --------------------------------------------------------------------------
//
diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h
index 2f0096aa..c625a7c7 100644
--- a/lib/common/BoxPlatform.h
+++ b/lib/common/BoxPlatform.h
@@ -29,9 +29,15 @@
#endif
#ifdef WIN32
- // need msvcrt version 6.1 or higher for _gmtime64()
- // must define this before importing <sys/types.h>
- #define __MSVCRT_VERSION__ 0x0601
+ #ifdef __MSVCRT_VERSION__
+ #if __MSVCRT_VERSION__ < 0x0601
+ #error Must include Box.h before sys/types.h
+ #endif
+ #else
+ // need msvcrt version 6.1 or higher for _gmtime64()
+ // must define this before importing <sys/types.h>
+ #define __MSVCRT_VERSION__ 0x0601
+ #endif
#endif
#ifdef HAVE_SYS_TYPES_H
@@ -66,10 +72,14 @@
#endif
// Find out if credentials on UNIX sockets can be obtained
-#ifndef HAVE_GETPEEREID
- #if !HAVE_DECL_SO_PEERCRED
- #define PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
- #endif
+#ifdef HAVE_GETPEEREID
+ //
+#elif HAVE_DECL_SO_PEERCRED
+ //
+#elif defined HAVE_UCRED_H && HAVE_GETPEERUCRED
+ //
+#else
+ #define PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
#endif
#ifdef HAVE_DEFINE_PRAGMA
@@ -150,7 +160,7 @@
#endif
// for Unix compatibility with Windows :-)
-#if !HAVE_DECL_O_BINARY
+#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -162,14 +172,15 @@
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
- #include "emu.h"
#endif
-// Solaris has no dirfd(x) macro or function, and we need one.
-// We cannot define macros with arguments directly using AC_DEFINE,
-// so do it here instead of in configure.ac.
+#include "emu.h"
+
+// Solaris has no dirfd(x) macro or function, and we need one for
+// intercept tests. We cannot define macros with arguments directly
+// using AC_DEFINE, so do it here instead of in configure.ac.
-#if ! HAVE_DECL_DIRFD
+#if ! defined PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE && ! HAVE_DECL_DIRFD
#ifdef HAVE_DIR_D_FD
#define dirfd(x) (x)->d_fd
#elif defined HAVE_DIR_DD_FD
diff --git a/lib/common/BoxTime.cpp b/lib/common/BoxTime.cpp
index 1ddcffd4..d05c0a6c 100644
--- a/lib/common/BoxTime.cpp
+++ b/lib/common/BoxTime.cpp
@@ -39,8 +39,8 @@ box_time_t GetCurrentBoxTime()
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
{
- BOX_ERROR("Failed to gettimeofday(), dropping "
- "precision: " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to gettimeofday(), "
+ "dropping precision");
}
else
{
@@ -52,3 +52,45 @@ box_time_t GetCurrentBoxTime()
return SecondsToBoxTime(time(0));
}
+
+std::string FormatTime(box_time_t time, bool includeDate, bool showMicros)
+{
+ std::ostringstream buf;
+
+ time_t seconds = BoxTimeToSeconds(time);
+ int micros = BoxTimeToMicroSeconds(time) % MICRO_SEC_IN_SEC;
+
+ struct tm tm_now, *tm_ptr = &tm_now;
+
+ #ifdef WIN32
+ if ((tm_ptr = localtime(&seconds)) != NULL)
+ #else
+ if (localtime_r(&seconds, &tm_now) != NULL)
+ #endif
+ {
+ buf << std::setfill('0');
+
+ if (includeDate)
+ {
+ buf << std::setw(4) << (tm_ptr->tm_year + 1900) << "-" <<
+ std::setw(2) << (tm_ptr->tm_mon + 1) << "-" <<
+ std::setw(2) << (tm_ptr->tm_mday) << " ";
+ }
+
+ buf << std::setw(2) << tm_ptr->tm_hour << ":" <<
+ std::setw(2) << tm_ptr->tm_min << ":" <<
+ std::setw(2) << tm_ptr->tm_sec;
+
+ if (showMicros)
+ {
+ buf << "." << std::setw(6) << micros;
+ }
+ }
+ else
+ {
+ buf << strerror(errno);
+ }
+
+ return buf.str();
+}
+
diff --git a/lib/common/BoxTime.h b/lib/common/BoxTime.h
index e62a77ab..6681bbbd 100644
--- a/lib/common/BoxTime.h
+++ b/lib/common/BoxTime.h
@@ -40,4 +40,7 @@ inline uint64_t BoxTimeToMicroSeconds(box_time_t Time)
return Time;
}
+std::string FormatTime(box_time_t time, bool includeDate,
+ bool showMicros = false);
+
#endif // BOXTIME__H
diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp
index 4d76e0e0..7d2e0bac 100644
--- a/lib/common/Configuration.cpp
+++ b/lib/common/Configuration.cpp
@@ -9,8 +9,11 @@
#include "Box.h"
-#include <stdlib.h>
#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sstream>
#include "Configuration.h"
#include "CommonException.h"
@@ -29,7 +32,105 @@ inline bool iw(int c)
static const char *sValueBooleanStrings[] = {"yes", "true", "no", "false", 0};
static const bool sValueBooleanValue[] = {true, true, false, false};
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ std::string name,
+ int flags,
+ void *testFunction
+)
+: mName(name),
+ mHasDefaultValue(false),
+ mFlags(flags),
+ mTestFunction(testFunction)
+{ }
+
+// to allow passing NULL for default ListenAddresses
+
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ std::string name,
+ int flags,
+ NoDefaultValue_t t,
+ void *testFunction
+)
+: mName(name),
+ mHasDefaultValue(false),
+ mFlags(flags),
+ mTestFunction(testFunction)
+{ }
+
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ std::string name,
+ int flags,
+ std::string defaultValue,
+ void *testFunction
+)
+: mName(name),
+ mDefaultValue(defaultValue),
+ mHasDefaultValue(true),
+ mFlags(flags),
+ mTestFunction(testFunction)
+{ }
+
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ std::string name,
+ int flags,
+ const char *defaultValue,
+ void *testFunction
+)
+: mName(name),
+ mDefaultValue(defaultValue),
+ mHasDefaultValue(true),
+ mFlags(flags),
+ mTestFunction(testFunction)
+{ }
+
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ std::string name,
+ int flags,
+ int defaultValue,
+ void *testFunction
+)
+: mName(name),
+ mHasDefaultValue(true),
+ mFlags(flags),
+ mTestFunction(testFunction)
+{
+ ASSERT(flags & ConfigTest_IsInt);
+ std::ostringstream val;
+ val << defaultValue;
+ mDefaultValue = val.str();
+}
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ std::string name,
+ int flags,
+ bool defaultValue,
+ void *testFunction
+)
+: mName(name),
+ mHasDefaultValue(true),
+ mFlags(flags),
+ mTestFunction(testFunction)
+{
+ ASSERT(flags & ConfigTest_IsBool);
+ mDefaultValue = defaultValue ? "yes" : "no";
+}
+
+ConfigurationVerifyKey::ConfigurationVerifyKey
+(
+ const ConfigurationVerifyKey& rToCopy
+)
+: mName(rToCopy.mName),
+ mDefaultValue(rToCopy.mDefaultValue),
+ mHasDefaultValue(rToCopy.mHasDefaultValue),
+ mFlags(rToCopy.mFlags),
+ mTestFunction(rToCopy.mTestFunction)
+{ }
// --------------------------------------------------------------------------
//
@@ -55,8 +156,8 @@ Configuration::Configuration(const std::string &rName)
// --------------------------------------------------------------------------
Configuration::Configuration(const Configuration &rToCopy)
: mName(rToCopy.mName),
- mSubConfigurations(rToCopy.mSubConfigurations),
- mKeys(rToCopy.mKeys)
+ mKeys(rToCopy.mKeys),
+ mSubConfigurations(rToCopy.mSubConfigurations)
{
}
@@ -98,32 +199,29 @@ std::auto_ptr<Configuration> Configuration::LoadAndVerify(
FdGetLine getline(file);
// Object to create
- Configuration *pconfig = new Configuration(std::string("<root>"));
+ std::auto_ptr<Configuration> apConfig(
+ new Configuration(std::string("<root>")));
try
{
// Load
- LoadInto(*pconfig, getline, rErrorMsg, true);
+ LoadInto(*apConfig, getline, rErrorMsg, true);
if(!rErrorMsg.empty())
{
// An error occured, return now
- //TRACE1("Error message from LoadInto: %s", rErrorMsg.c_str());
- TRACE0("Error at Configuration::LoadInfo\n");
- delete pconfig;
- pconfig = 0;
+ BOX_ERROR("Error in Configuration::LoadInto: " <<
+ rErrorMsg);
return std::auto_ptr<Configuration>(0);
}
// Verify?
if(pVerify)
{
- if(!Verify(*pconfig, *pVerify, std::string(), rErrorMsg))
+ if(!apConfig->Verify(*pVerify, std::string(), rErrorMsg))
{
- //TRACE1("Error message from Verify: %s", rErrorMsg.c_str());
- TRACE0("Error at Configuration::Verify\n");
- delete pconfig;
- pconfig = 0;
+ BOX_ERROR("Error verifying configuration: " <<
+ rErrorMsg);
return std::auto_ptr<Configuration>(0);
}
}
@@ -131,13 +229,11 @@ std::auto_ptr<Configuration> Configuration::LoadAndVerify(
catch(...)
{
// Clean up
- delete pconfig;
- pconfig = 0;
throw;
}
// Success. Return result.
- return std::auto_ptr<Configuration>(pconfig);
+ return apConfig;
}
@@ -173,10 +269,10 @@ bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::s
if(startBlockExpected)
{
// New config object
- Configuration config(blockName);
+ Configuration subConfig(blockName);
// Continue processing into this block
- if(!LoadInto(config, rGetLine, rErrorMsg, false))
+ if(!LoadInto(subConfig, rGetLine, rErrorMsg, false))
{
// Abort error
return false;
@@ -185,11 +281,12 @@ bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::s
startBlockExpected = false;
// Store...
- rConfig.mSubConfigurations.push_back(std::pair<std::string, Configuration>(blockName, config));
+ rConfig.AddSubConfig(blockName, subConfig);
}
else
{
- rErrorMsg += "Unexpected start block in " + rConfig.mName + "\n";
+ rErrorMsg += "Unexpected start block in " +
+ rConfig.mName + "\n";
}
}
else
@@ -200,7 +297,7 @@ bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::s
if(RootLevel)
{
// error -- root level doesn't have a close
- rErrorMsg += "Root level has close block -- forget to terminate subblock?\n";
+ rErrorMsg += "Root level has close block -- forgot to terminate subblock?\n";
// but otherwise ignore
}
else
@@ -246,24 +343,11 @@ bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::s
{
std::string key(line.substr(0, keyend));
std::string value(line.substr(valuestart));
- //TRACE2("KEY: |%s|=|%s|\n", key.c_str(), value.c_str());
-
- // Check for duplicate values
- if(rConfig.mKeys.find(key) != rConfig.mKeys.end())
- {
- // Multi-values allowed here, but checked later on
- rConfig.mKeys[key] += MultiValueSeparator;
- rConfig.mKeys[key] += value;
- }
- else
- {
- // Store
- rConfig.mKeys[key] = value;
- }
+ rConfig.AddKeyValue(key, value);
}
else
{
- rErrorMsg += "Invalid key in block "+rConfig.mName+"\n";
+ rErrorMsg += "Invalid configuration key: " + line + "\n";
}
}
else
@@ -286,40 +370,60 @@ bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::s
return true;
}
+void Configuration::AddKeyValue(const std::string& rKey,
+ const std::string& rValue)
+{
+ // Check for duplicate values
+ if(mKeys.find(rKey) != mKeys.end())
+ {
+ // Multi-values allowed here, but checked later on
+ mKeys[rKey] += MultiValueSeparator;
+ mKeys[rKey] += rValue;
+ }
+ else
+ {
+ // Store
+ mKeys[rKey] = rValue;
+ }
+}
+
+void Configuration::AddSubConfig(const std::string& rName,
+ const Configuration& rSubConfig)
+{
+ mSubConfigurations.push_back(
+ std::pair<std::string, Configuration>(rName, rSubConfig));
+}
+
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::KeyExists(const char *)
+// Name: Configuration::KeyExists(const std::string&)
// Purpose: Checks to see if a key exists
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
-bool Configuration::KeyExists(const char *pKeyName) const
+bool Configuration::KeyExists(const std::string& rKeyName) const
{
- if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
-
- return mKeys.find(pKeyName) != mKeys.end();
+ return mKeys.find(rKeyName) != mKeys.end();
}
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::GetKeyValue(const char *)
+// Name: Configuration::GetKeyValue(const std::string&)
// Purpose: Returns the value of a configuration variable
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
-const std::string &Configuration::GetKeyValue(const char *pKeyName) const
+const std::string &Configuration::GetKeyValue(const std::string& rKeyName) const
{
- if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
-
- std::map<std::string, std::string>::const_iterator i(mKeys.find(pKeyName));
+ std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
if(i == mKeys.end())
{
- BOX_ERROR("Missing configuration key: " << pKeyName);
+ BOX_ERROR("Missing configuration key: " << rKeyName);
THROW_EXCEPTION(CommonException, ConfigNoKey)
}
else
@@ -332,16 +436,14 @@ const std::string &Configuration::GetKeyValue(const char *pKeyName) const
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::GetKeyValueInt(const char *)
+// Name: Configuration::GetKeyValueInt(const std::string& rKeyName)
// Purpose: Gets a key value as an integer
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
-int Configuration::GetKeyValueInt(const char *pKeyName) const
+int Configuration::GetKeyValueInt(const std::string& rKeyName) const
{
- if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
-
- std::map<std::string, std::string>::const_iterator i(mKeys.find(pKeyName));
+ std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
if(i == mKeys.end())
{
@@ -362,16 +464,14 @@ int Configuration::GetKeyValueInt(const char *pKeyName) const
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::GetKeyValueBool(const char *) const
+// Name: Configuration::GetKeyValueBool(const std::string&)
// Purpose: Gets a key value as a boolean
// Created: 17/2/04
//
// --------------------------------------------------------------------------
-bool Configuration::GetKeyValueBool(const char *pKeyName) const
+bool Configuration::GetKeyValueBool(const std::string& rKeyName) const
{
- if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
-
- std::map<std::string, std::string>::const_iterator i(mKeys.find(pKeyName));
+ std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
if(i == mKeys.end())
{
@@ -428,22 +528,21 @@ std::vector<std::string> Configuration::GetKeyNames() const
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::SubConfigurationExists(const char *)
+// Name: Configuration::SubConfigurationExists(const
+// std::string&)
// Purpose: Checks to see if a sub configuration exists
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
-bool Configuration::SubConfigurationExists(const char *pSubName) const
+bool Configuration::SubConfigurationExists(const std::string& rSubName) const
{
- if(pSubName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
-
// Attempt to find it...
std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
for(; i != mSubConfigurations.end(); ++i)
{
// This the one?
- if(i->first == pSubName)
+ if(i->first == rSubName)
{
// Yes.
return true;
@@ -458,22 +557,52 @@ bool Configuration::SubConfigurationExists(const char *pSubName) const
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::GetSubConfiguration(const char *)
+// Name: Configuration::GetSubConfiguration(const
+// std::string&)
// Purpose: Gets a sub configuration
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
-const Configuration &Configuration::GetSubConfiguration(const char *pSubName) const
+const Configuration &Configuration::GetSubConfiguration(const std::string&
+ rSubName) const
{
- if(pSubName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
-
// Attempt to find it...
std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
for(; i != mSubConfigurations.end(); ++i)
{
// This the one?
- if(i->first == pSubName)
+ if(i->first == rSubName)
+ {
+ // Yes.
+ return i->second;
+ }
+ }
+
+ THROW_EXCEPTION(CommonException, ConfigNoSubConfig)
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Configuration::GetSubConfiguration(const
+// std::string&)
+// Purpose: Gets a sub configuration for editing
+// Created: 2008/08/12
+//
+// --------------------------------------------------------------------------
+Configuration &Configuration::GetSubConfigurationEditable(const std::string&
+ rSubName)
+{
+ // Attempt to find it...
+
+ for(SubConfigListType::iterator
+ i = mSubConfigurations.begin();
+ i != mSubConfigurations.end(); ++i)
+ {
+ // This the one?
+ if(i->first == rSubName)
{
// Yes.
return i->second;
@@ -510,12 +639,14 @@ std::vector<std::string> Configuration::GetSubConfigurationNames() const
// --------------------------------------------------------------------------
//
// Function
-// Name: Configuration::Verify(const Configuration &, const ConfigurationVerify &, const std::string &, std::string &)
-// Purpose: Return list of sub configuration names
+// Name: Configuration::Verify(const ConfigurationVerify &, const std::string &, std::string &)
+// Purpose: Checks that the configuration is valid according to the
+// supplied verifier
// Created: 2003/07/24
//
// --------------------------------------------------------------------------
-bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rVerify, const std::string &rLevel, std::string &rErrorMsg)
+bool Configuration::Verify(const ConfigurationVerify &rVerify,
+ const std::string &rLevel, std::string &rErrorMsg)
{
bool ok = true;
@@ -528,15 +659,14 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
do
{
// Can the key be found?
- ASSERT(pvkey->mpName);
- if(rConfig.KeyExists(pvkey->mpName))
+ if(KeyExists(pvkey->Name()))
{
// Get value
- const std::string &rval = rConfig.GetKeyValue(pvkey->mpName);
+ const std::string &rval = GetKeyValue(pvkey->Name());
const char *val = rval.c_str();
// Check it's a number?
- if((pvkey->Tests & ConfigTest_IsInt) == ConfigTest_IsInt)
+ if((pvkey->Flags() & ConfigTest_IsInt) == ConfigTest_IsInt)
{
// Test it...
char *end;
@@ -545,12 +675,12 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
{
// not a good value
ok = false;
- rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) is not a valid integer.\n";
+ rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid integer.\n";
}
}
// Check it's a bool?
- if((pvkey->Tests & ConfigTest_IsBool) == ConfigTest_IsBool)
+ if((pvkey->Flags() & ConfigTest_IsBool) == ConfigTest_IsBool)
{
// See if it's one of the allowed strings.
bool found = false;
@@ -568,37 +698,38 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
if(!found)
{
ok = false;
- rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) is not a valid boolean value.\n";
+ rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid boolean value.\n";
}
}
// Check for multi valued statments where they're not allowed
- if((pvkey->Tests & ConfigTest_MultiValueAllowed) == 0)
+ if((pvkey->Flags() & ConfigTest_MultiValueAllowed) == 0)
{
// Check to see if this key is a multi-value -- it shouldn't be
if(rval.find(MultiValueSeparator) != rval.npos)
{
ok = false;
- rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) multi value not allowed (duplicated key?).\n";
+ rErrorMsg += rLevel + mName +"." + pvkey->Name() + " (key) multi value not allowed (duplicated key?).\n";
}
}
}
else
{
// Is it required to exist?
- if((pvkey->Tests & ConfigTest_Exists) == ConfigTest_Exists)
+ if((pvkey->Flags() & ConfigTest_Exists) == ConfigTest_Exists)
{
// Should exist, but doesn't.
ok = false;
- rErrorMsg += rLevel + rConfig.mName + "." + pvkey->mpName + " (key) is missing.\n";
+ rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is missing.\n";
}
- else if(pvkey->mpDefaultValue)
+ else if(pvkey->HasDefaultValue())
{
- rConfig.mKeys[std::string(pvkey->mpName)] = std::string(pvkey->mpDefaultValue);
+ mKeys[pvkey->Name()] =
+ pvkey->DefaultValue();
}
}
- if((pvkey->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
+ if((pvkey->Flags() & ConfigTest_LastEntry) == ConfigTest_LastEntry)
{
// No more!
todo = false;
@@ -610,22 +741,22 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
} while(todo);
// Check for additional keys
- for(std::map<std::string, std::string>::const_iterator i = rConfig.mKeys.begin();
- i != rConfig.mKeys.end(); ++i)
+ for(std::map<std::string, std::string>::const_iterator i = mKeys.begin();
+ i != mKeys.end(); ++i)
{
// Is the name in the list?
const ConfigurationVerifyKey *scan = rVerify.mpKeys;
bool found = false;
while(scan)
{
- if(scan->mpName == i->first)
+ if(scan->Name() == i->first)
{
found = true;
break;
}
// Next?
- if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
+ if((scan->Flags() & ConfigTest_LastEntry) == ConfigTest_LastEntry)
{
break;
}
@@ -636,7 +767,7 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
{
// Shouldn't exist, but does.
ok = false;
- rErrorMsg += rLevel + rConfig.mName + "." + i->first + " (key) is not a known key. Check spelling and placement.\n";
+ rErrorMsg += rLevel + mName + "." + i->first + " (key) is not a known key. Check spelling and placement.\n";
}
}
}
@@ -650,8 +781,7 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
const ConfigurationVerify *scan = rVerify.mpSubConfigurations;
while(scan)
{
- ASSERT(scan->mpName);
- if(scan->mpName[0] == '*')
+ if(scan->mName.length() > 0 && scan->mName[0] == '*')
{
wildcardverify = scan;
}
@@ -659,24 +789,25 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
// Required?
if((scan->Tests & ConfigTest_Exists) == ConfigTest_Exists)
{
- if(scan->mpName[0] == '*')
+ if(scan->mName.length() > 0 &&
+ scan->mName[0] == '*')
{
// Check something exists
- if(rConfig.mSubConfigurations.size() < 1)
+ if(mSubConfigurations.size() < 1)
{
// A sub config should exist, but doesn't.
ok = false;
- rErrorMsg += rLevel + rConfig.mName + ".* (block) is missing (a block must be present).\n";
+ rErrorMsg += rLevel + mName + ".* (block) is missing (a block must be present).\n";
}
}
else
{
// Check real thing exists
- if(!rConfig.SubConfigurationExists(scan->mpName))
+ if(!SubConfigurationExists(scan->mName))
{
// Should exist, but doesn't.
ok = false;
- rErrorMsg += rLevel + rConfig.mName + "." + scan->mpName + " (block) is missing.\n";
+ rErrorMsg += rLevel + mName + "." + scan->mName + " (block) is missing.\n";
}
}
}
@@ -690,8 +821,9 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
}
// Go through the sub configurations, one by one
- for(std::list<std::pair<std::string, Configuration> >::const_iterator i(rConfig.mSubConfigurations.begin());
- i != rConfig.mSubConfigurations.end(); ++i)
+ for(SubConfigListType::iterator
+ i = mSubConfigurations.begin();
+ i != mSubConfigurations.end(); ++i)
{
// Can this be found?
const ConfigurationVerify *subverify = 0;
@@ -701,7 +833,7 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
ASSERT(name);
while(scan)
{
- if(strcmp(scan->mpName, name) == 0)
+ if(scan->mName == name)
{
// found it!
subverify = scan;
@@ -725,7 +857,8 @@ bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rV
if(subverify)
{
// override const-ness here...
- if(!Verify((Configuration&)i->second, *subverify, rConfig.mName + '.', rErrorMsg))
+ if(!i->second.Verify(*subverify, mName + '.',
+ rErrorMsg))
{
ok = false;
}
diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h
index 64e7568e..2babd753 100644
--- a/lib/common/Configuration.h
+++ b/lib/common/Configuration.h
@@ -29,20 +29,51 @@ enum
class ConfigurationVerifyKey
{
public:
- const char *mpName; // "*" for all other keys (not implemented yet)
- const char *mpDefaultValue; // default for when it's not present
- int Tests;
- void *TestFunction; // set to zero for now, will implement later
+ typedef enum
+ {
+ NoDefaultValue = 1
+ } NoDefaultValue_t;
+
+ ConfigurationVerifyKey(std::string name, int flags,
+ void *testFunction = NULL);
+ // to allow passing ConfigurationVerifyKey::NoDefaultValue
+ // for default ListenAddresses
+ ConfigurationVerifyKey(std::string name, int flags,
+ NoDefaultValue_t t, void *testFunction = NULL);
+ ConfigurationVerifyKey(std::string name, int flags,
+ std::string defaultValue, void *testFunction = NULL);
+ ConfigurationVerifyKey(std::string name, int flags,
+ const char* defaultValue, void *testFunction = NULL);
+ ConfigurationVerifyKey(std::string name, int flags,
+ int defaultValue, void *testFunction = NULL);
+ ConfigurationVerifyKey(std::string name, int flags,
+ bool defaultValue, void *testFunction = NULL);
+ const std::string& Name() const { return mName; }
+ const std::string& DefaultValue() const { return mDefaultValue; }
+ const bool HasDefaultValue() const { return mHasDefaultValue; }
+ const int Flags() const { return mFlags; }
+ const void* TestFunction() const { return mTestFunction; }
+ ConfigurationVerifyKey(const ConfigurationVerifyKey& rToCopy);
+
+private:
+ ConfigurationVerifyKey& operator=(const ConfigurationVerifyKey&
+ noAssign);
+
+ std::string mName; // "*" for all other keys (not implemented yet)
+ std::string mDefaultValue; // default for when it's not present
+ bool mHasDefaultValue;
+ int mFlags;
+ void *mTestFunction; // set to zero for now, will implement later
};
class ConfigurationVerify
{
public:
- const char *mpName; // "*" for all other sub config names
+ std::string mName; // "*" for all other sub config names
const ConfigurationVerify *mpSubConfigurations;
const ConfigurationVerifyKey *mpKeys;
int Tests;
- void *TestFunction; // set to zero for now, will implement later
+ void *TestFunction; // set to zero for now, will implement later
};
class FdGetLine;
@@ -57,9 +88,8 @@ class FdGetLine;
// --------------------------------------------------------------------------
class Configuration
{
-private:
- Configuration(const std::string &rName);
public:
+ Configuration(const std::string &rName);
Configuration(const Configuration &rToCopy);
~Configuration();
@@ -79,26 +109,36 @@ public:
std::string &rErrorMsg)
{ return LoadAndVerify(rFilename, 0, rErrorMsg); }
- bool KeyExists(const char *pKeyName) const;
- const std::string &GetKeyValue(const char *pKeyName) const;
- int GetKeyValueInt(const char *pKeyName) const;
- bool GetKeyValueBool(const char *pKeyName) const;
+ bool KeyExists(const std::string& rKeyName) const;
+ const std::string &GetKeyValue(const std::string& rKeyName) const;
+ int GetKeyValueInt(const std::string& rKeyName) const;
+ bool GetKeyValueBool(const std::string& rKeyName) const;
std::vector<std::string> GetKeyNames() const;
- bool SubConfigurationExists(const char *pSubName) const;
- const Configuration &GetSubConfiguration(const char *pSubName) const;
+ bool SubConfigurationExists(const std::string& rSubName) const;
+ const Configuration &GetSubConfiguration(const std::string& rSubName) const;
+ Configuration &GetSubConfigurationEditable(const std::string& rSubName);
std::vector<std::string> GetSubConfigurationNames() const;
+ void AddKeyValue(const std::string& rKey, const std::string& rValue);
+ void AddSubConfig(const std::string& rName, const Configuration& rSubConfig);
+
+ bool Verify(const ConfigurationVerify &rVerify, std::string &rErrorMsg)
+ {
+ return Verify(rVerify, std::string(), rErrorMsg);
+ }
+
+private:
std::string mName;
+ // Order of keys not preserved
+ std::map<std::string, std::string> mKeys;
// Order of sub blocks preserved
typedef std::list<std::pair<std::string, Configuration> > SubConfigListType;
SubConfigListType mSubConfigurations;
- // Order of keys, not preserved
- std::map<std::string, std::string> mKeys;
-private:
static bool LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel);
- static bool Verify(Configuration &rConfig, const ConfigurationVerify &rVerify, const std::string &rLevel, std::string &rErrorMsg);
+ bool Verify(const ConfigurationVerify &rVerify, const std::string &rLevel,
+ std::string &rErrorMsg);
};
#endif // CONFIGURATION__H
diff --git a/lib/common/DebugAssertFailed.cpp b/lib/common/DebugAssertFailed.cpp
index cceab0ef..e498d641 100644
--- a/lib/common/DebugAssertFailed.cpp
+++ b/lib/common/DebugAssertFailed.cpp
@@ -7,7 +7,7 @@
//
// --------------------------------------------------------------------------
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
#include "Box.h"
@@ -33,5 +33,5 @@ void BoxDebugAssertFailed(const char *cond, const char *file, int line)
}
-#endif // NDEBUG
+#endif // BOX_RELEASE_BUILD
diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp
index 87cdf00d..230d7163 100644
--- a/lib/common/DebugMemLeakFinder.cpp
+++ b/lib/common/DebugMemLeakFinder.cpp
@@ -8,7 +8,7 @@
// --------------------------------------------------------------------------
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
#include "Box.h"
@@ -75,6 +75,13 @@ namespace
void memleakfinder_init()
{
ASSERT(!memleakfinder_initialised);
+
+ {
+ // allocates a permanent buffer on Solaris.
+ // not a leak?
+ std::ostringstream oss;
+ }
+
memleakfinder_initialised = true;
}
@@ -146,7 +153,9 @@ void *memleakfinder_realloc(void *ptr, size_t size)
std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr));
if(ptr && i == sMallocBlocks.end())
{
- TRACE1("Block %x realloc(), but not in list. Error? Or allocated in startup static objects?\n", ptr);
+ BOX_WARNING("Block " << ptr << " realloc()ated, but not "
+ "in list. Error? Or allocated in startup static "
+ "objects?");
}
void *b = ::realloc(ptr, size);
@@ -193,7 +202,9 @@ void memleakfinder_free(void *ptr)
}
else
{
- TRACE1("Block %p freed, but not known. Error? Or allocated in startup static allocation?\n", ptr);
+ BOX_WARNING("Block " << ptr << " freed, but not "
+ "known. Error? Or allocated in startup "
+ "static allocation?");
}
if(sTrackMallocInSection)
@@ -293,16 +304,21 @@ void memleakfinder_traceblocksinsection()
std::map<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.find(*s));
if(i == sMallocBlocks.end())
{
- TRACE0("Logical error in section block finding\n");
+ BOX_WARNING("Logical error in section block finding");
}
else
{
- TRACE4("Block %p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line);
+ BOX_TRACE("Block " << i->first << " size " <<
+ i->second.size << " allocated at " <<
+ i->second.file << ":" << i->second.line);
}
}
for(std::map<void *, ObjectInfo>::const_iterator i(sSectionObjectBlocks.begin()); i != sSectionObjectBlocks.end(); ++i)
{
- TRACE5("Object%s %p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line);
+ BOX_TRACE("Object" << (i->second.array?" []":"") << " " <<
+ i->first << " size " << i->second.size <<
+ " allocated at " << i->second.file <<
+ ":" << i->second.line);
}
}
@@ -335,13 +351,27 @@ void memleakfinder_reportleaks_file(FILE *file)
ASSERT(!sTrackingDataDestroyed);
- for(std::map<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i)
+ for(std::map<void *, MallocBlockInfo>::const_iterator
+ i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i)
{
- if(is_leak(i->first)) ::fprintf(file, "Block 0x%p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line);
+ if(is_leak(i->first))
+ {
+ ::fprintf(file, "Block %p size %d allocated at "
+ "%s:%d\n", i->first, i->second.size,
+ i->second.file, i->second.line);
+ }
}
- for(std::map<void *, ObjectInfo>::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i)
+
+ for(std::map<void *, ObjectInfo>::const_iterator
+ i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i)
{
- if(is_leak(i->first)) ::fprintf(file, "Object%s 0x%p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line);
+ if(is_leak(i->first))
+ {
+ ::fprintf(file, "Object%s %p size %d allocated at "
+ "%s:%d\n", i->second.array?" []":"",
+ i->first, i->second.size, i->second.file,
+ i->second.line);
+ }
}
}
@@ -390,8 +420,10 @@ extern "C" void memleakfinder_atexit()
void memleakfinder_setup_exit_report(const char *filename, const char *markertext)
{
- ::strcpy(atexit_filename, filename);
- ::strcpy(atexit_markertext, markertext);
+ ::strncpy(atexit_filename, filename, sizeof(atexit_filename)-1);
+ ::strncpy(atexit_markertext, markertext, sizeof(atexit_markertext)-1);
+ atexit_filename[sizeof(atexit_filename)-1] = 0;
+ atexit_markertext[sizeof(atexit_markertext)-1] = 0;
if(!atexit_registered)
{
atexit(memleakfinder_atexit);
@@ -516,4 +548,4 @@ void operator delete(void *ptr) throw ()
internal_delete(ptr);
}
-#endif // NDEBUG
+#endif // BOX_RELEASE_BUILD
diff --git a/lib/common/DebugPrintf.cpp b/lib/common/DebugPrintf.cpp
index 8d75f458..1335d473 100644
--- a/lib/common/DebugPrintf.cpp
+++ b/lib/common/DebugPrintf.cpp
@@ -7,7 +7,7 @@
//
// --------------------------------------------------------------------------
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
#include "Box.h"
@@ -80,4 +80,4 @@ int BoxDebugTrace(const char *format, ...)
}
-#endif // NDEBUG
+#endif // BOX_RELEASE_BUILD
diff --git a/lib/common/EventWatchFilesystemObject.cpp b/lib/common/EventWatchFilesystemObject.cpp
index 84781113..43533fc8 100644
--- a/lib/common/EventWatchFilesystemObject.cpp
+++ b/lib/common/EventWatchFilesystemObject.cpp
@@ -26,9 +26,10 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: EventWatchFilesystemObject::EventWatchFilesystemObject(const char *)
-// Purpose: Constructor -- opens the file object
-// Created: 12/3/04
+// Name: EventWatchFilesystemObject::EventWatchFilesystemObject
+// (const char *)
+// Purpose: Constructor -- opens the file object
+// Created: 12/3/04
//
// --------------------------------------------------------------------------
EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename)
@@ -39,9 +40,8 @@ EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename)
#ifdef HAVE_KQUEUE
if(mDescriptor == -1)
{
- BOX_ERROR("EventWatchFilesystemObject: "
- "Failed to open file '" << Filename << "': " <<
- strerror(errno));
+ BOX_LOG_SYS_ERROR("EventWatchFilesystemObject: "
+ "Failed to open file '" << Filename << "'");
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
#else
@@ -53,9 +53,9 @@ EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename)
// --------------------------------------------------------------------------
//
// Function
-// Name: EventWatchFilesystemObject::~EventWatchFilesystemObject()
-// Purpose: Destructor
-// Created: 12/3/04
+// Name: EventWatchFilesystemObject::~EventWatchFilesystemObject()
+// Purpose: Destructor
+// Created: 12/3/04
//
// --------------------------------------------------------------------------
EventWatchFilesystemObject::~EventWatchFilesystemObject()
@@ -70,12 +70,14 @@ EventWatchFilesystemObject::~EventWatchFilesystemObject()
// --------------------------------------------------------------------------
//
// Function
-// Name: EventWatchFilesystemObject::EventWatchFilesystemObject(const EventWatchFilesystemObject &)
-// Purpose: Copy constructor
-// Created: 12/3/04
+// Name: EventWatchFilesystemObject::EventWatchFilesystemObject
+// (const EventWatchFilesystemObject &)
+// Purpose: Copy constructor
+// Created: 12/3/04
//
// --------------------------------------------------------------------------
-EventWatchFilesystemObject::EventWatchFilesystemObject(const EventWatchFilesystemObject &rToCopy)
+EventWatchFilesystemObject::EventWatchFilesystemObject(
+ const EventWatchFilesystemObject &rToCopy)
: mDescriptor(::dup(rToCopy.mDescriptor))
{
if(mDescriptor == -1)
@@ -89,17 +91,20 @@ EventWatchFilesystemObject::EventWatchFilesystemObject(const EventWatchFilesyste
// --------------------------------------------------------------------------
//
// Function
-// Name: EventWatchFilesystemObject::FillInKEvent(struct kevent &, int)
-// Purpose: For WaitForEvent
-// Created: 12/3/04
+// Name: EventWatchFilesystemObject::FillInKEvent(struct kevent &, int)
+// Purpose: For WaitForEvent
+// Created: 12/3/04
//
// --------------------------------------------------------------------------
-void EventWatchFilesystemObject::FillInKEvent(struct kevent &rEvent, int Flags) const
+void EventWatchFilesystemObject::FillInKEvent(struct kevent &rEvent,
+ int Flags) const
{
- EV_SET(&rEvent, mDescriptor, EVFILT_VNODE, EV_CLEAR, NOTE_DELETE | NOTE_WRITE, 0, (void*)this);
+ EV_SET(&rEvent, mDescriptor, EVFILT_VNODE, EV_CLEAR,
+ NOTE_DELETE | NOTE_WRITE, 0, (void*)this);
}
#else
-void EventWatchFilesystemObject::FillInPoll(int &fd, short &events, int Flags) const
+void EventWatchFilesystemObject::FillInPoll(int &fd, short &events,
+ int Flags) const
{
THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform)
}
diff --git a/lib/common/FdGetLine.h b/lib/common/FdGetLine.h
index a18007a3..df43c3c9 100644
--- a/lib/common/FdGetLine.h
+++ b/lib/common/FdGetLine.h
@@ -12,7 +12,7 @@
#include <string>
-#ifdef NDEBUG
+#ifdef BOX_RELEASE_BUILD
#define FDGETLINE_BUFFER_SIZE 1024
#elif defined WIN32
// need enough space for at least one unicode character
diff --git a/lib/common/FileModificationTime.h b/lib/common/FileModificationTime.h
index a84df579..5f13c015 100644
--- a/lib/common/FileModificationTime.h
+++ b/lib/common/FileModificationTime.h
@@ -14,7 +14,7 @@
#include "BoxTime.h"
-inline box_time_t FileModificationTime(struct stat &st)
+inline box_time_t FileModificationTime(EMU_STRUCT_STAT &st)
{
#ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC
box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL);
@@ -26,19 +26,26 @@ inline box_time_t FileModificationTime(struct stat &st)
return datamodified;
}
-inline box_time_t FileAttrModificationTime(struct stat &st)
+inline box_time_t FileAttrModificationTime(EMU_STRUCT_STAT &st)
{
-#ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC
- box_time_t statusmodified = ((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL);
-#else
- box_time_t statusmodified = (((int64_t)st.st_ctimespec.tv_nsec) / NANO_SEC_IN_USEC_LL)
- + (((int64_t)st.st_ctimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL));
+ box_time_t statusmodified =
+#ifdef HAVE_STRUCT_STAT_ST_MTIMESPEC
+ (((int64_t)st.st_ctimespec.tv_nsec) / (NANO_SEC_IN_USEC_LL)) +
+ (((int64_t)st.st_ctimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL));
+#elif defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ (((int64_t)st.st_ctim.tv_nsec) / (NANO_SEC_IN_USEC_LL)) +
+ (((int64_t)st.st_ctim.tv_sec) * (MICRO_SEC_IN_SEC_LL));
+#elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ (((int64_t)st.st_ctimensec) / (NANO_SEC_IN_USEC_LL)) +
+ (((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL));
+#else // no nanoseconds anywhere
+ (((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL));
#endif
return statusmodified;
}
-inline box_time_t FileModificationTimeMaxModAndAttr(struct stat &st)
+inline box_time_t FileModificationTimeMaxModAndAttr(EMU_STRUCT_STAT &st)
{
#ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC
box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL);
diff --git a/lib/common/FileStream.cpp b/lib/common/FileStream.cpp
index e0806e10..d6a3c5da 100644
--- a/lib/common/FileStream.cpp
+++ b/lib/common/FileStream.cpp
@@ -24,13 +24,40 @@
// Created: 2003/07/31
//
// --------------------------------------------------------------------------
-FileStream::FileStream(const char *Filename, int flags, int mode)
+FileStream::FileStream(const std::string& rFilename, int flags, int mode)
#ifdef WIN32
- : mOSFileHandle(::openfile(Filename, flags, mode)),
+ : mOSFileHandle(::openfile(rFilename.c_str(), flags, mode)),
#else
- : mOSFileHandle(::open(Filename, flags, mode)),
+ : mOSFileHandle(::open(rFilename.c_str(), flags, mode)),
#endif
- mIsEOF(false)
+ mIsEOF(false),
+ mFileName(rFilename)
+{
+ AfterOpen();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: FileStream::FileStream(const char *, int, int)
+// Purpose: Alternative constructor, takes a const char *,
+// avoids const strings being interpreted as handles!
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+FileStream::FileStream(const char *pFilename, int flags, int mode)
+#ifdef WIN32
+ : mOSFileHandle(::openfile(pFilename, flags, mode)),
+#else
+ : mOSFileHandle(::open(pFilename, flags, mode)),
+#endif
+ mIsEOF(false),
+ mFileName(pFilename)
+{
+ AfterOpen();
+}
+
+void FileStream::AfterOpen()
{
#ifdef WIN32
if(mOSFileHandle == INVALID_HANDLE_VALUE)
@@ -46,12 +73,16 @@ FileStream::FileStream(const char *Filename, int flags, int mode)
}
else
{
+ #ifdef WIN32
+ BOX_LOG_WIN_WARNING_NUMBER("Failed to open file: " <<
+ mFileName, winerrno);
+ #else
+ BOX_LOG_SYS_WARNING("Failed to open file: " <<
+ mFileName);
+ #endif
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
}
-#ifdef WIN32
- this->fileName = Filename;
-#endif
}
@@ -65,7 +96,8 @@ FileStream::FileStream(const char *Filename, int flags, int mode)
// --------------------------------------------------------------------------
FileStream::FileStream(tOSFileHandle FileDescriptor)
: mOSFileHandle(FileDescriptor),
- mIsEOF(false)
+ mIsEOF(false),
+ mFileName("HANDLE")
{
#ifdef WIN32
if(mOSFileHandle == INVALID_HANDLE_VALUE)
@@ -77,9 +109,6 @@ FileStream::FileStream(tOSFileHandle FileDescriptor)
BOX_ERROR("FileStream: called with invalid file handle");
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
-#ifdef WIN32
- this->fileName = "HANDLE";
-#endif
}
#if 0
@@ -150,27 +179,32 @@ int FileStream::Read(void *pBuffer, int NBytes, int Timeout)
NULL
);
- if ( valid )
+ if(valid)
{
r = numBytesRead;
}
- else if (GetLastError() == ERROR_BROKEN_PIPE)
+ else if(GetLastError() == ERROR_BROKEN_PIPE)
{
r = 0;
}
else
{
- BOX_ERROR("Failed to read from file: " <<
- GetErrorMessage(GetLastError()));
+ BOX_LOG_WIN_ERROR("Failed to read from file: " << mFileName);
r = -1;
}
#else
int r = ::read(mOSFileHandle, pBuffer, NBytes);
+ if(r == -1)
+ {
+ BOX_LOG_SYS_ERROR("Failed to read from file: " << mFileName);
+ }
#endif
+
if(r == -1)
{
THROW_EXCEPTION(CommonException, OSFileReadError)
}
+
if(r == 0)
{
mIsEOF = true;
@@ -190,8 +224,8 @@ int FileStream::Read(void *pBuffer, int NBytes, int Timeout)
// --------------------------------------------------------------------------
IOStream::pos_type FileStream::BytesLeftToRead()
{
- struct stat st;
- if(::fstat(mOSFileHandle, &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_FSTAT(mOSFileHandle, &st) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
@@ -233,6 +267,7 @@ void FileStream::Write(const void *pBuffer, int NBytes)
#else
if(::write(mOSFileHandle, pBuffer, NBytes) != NBytes)
{
+ BOX_LOG_SYS_ERROR("Failed to write to file: " << mFileName);
THROW_EXCEPTION(CommonException, OSFileWriteError)
}
#endif
@@ -368,3 +403,44 @@ bool FileStream::StreamClosed()
return mIsEOF;
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: FileStream::CompareWith(IOStream&, int)
+// Purpose: Compare bytes in this file with other stream's data
+// Created: 2009/01/03
+//
+// --------------------------------------------------------------------------
+bool FileStream::CompareWith(IOStream& rOther, int Timeout)
+{
+ // Size
+ IOStream::pos_type mySize = BytesLeftToRead();
+ IOStream::pos_type otherSize = 0;
+
+ // Test the contents
+ char buf1[2048];
+ char buf2[2048];
+ while(StreamDataLeft() && rOther.StreamDataLeft())
+ {
+ int readSize = rOther.Read(buf1, sizeof(buf1), Timeout);
+ otherSize += readSize;
+
+ if(Read(buf2, readSize) != readSize ||
+ ::memcmp(buf1, buf2, readSize) != 0)
+ {
+ return false;
+ }
+ }
+
+ // Check read all the data from the server and file -- can't be
+ // equal if local and remote aren't the same length. Can't use
+ // StreamDataLeft() test on local file, because if it's the same
+ // size, it won't know it's EOF yet.
+
+ if(rOther.StreamDataLeft() || otherSize != mySize)
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/lib/common/FileStream.h b/lib/common/FileStream.h
index 721bf3dd..7c4118cd 100644
--- a/lib/common/FileStream.h
+++ b/lib/common/FileStream.h
@@ -31,13 +31,17 @@
class FileStream : public IOStream
{
public:
- FileStream(const char *Filename,
-#ifdef WIN32
+ FileStream(const std::string& rFilename,
int flags = (O_RDONLY | O_BINARY),
-#else
- int flags = O_RDONLY,
-#endif
int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
+
+ // Ensure that const char * name doesn't end up as a handle
+ // on Windows!
+
+ FileStream(const char *pFilename,
+ int flags = (O_RDONLY | O_BINARY),
+ int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
+
FileStream(tOSFileHandle FileDescriptor);
virtual ~FileStream();
@@ -52,15 +56,16 @@ public:
virtual bool StreamDataLeft();
virtual bool StreamClosed();
+ bool CompareWith(IOStream& rOther, int Timeout = IOStream::TimeOutInfinite);
+
private:
tOSFileHandle mOSFileHandle;
bool mIsEOF;
FileStream(const FileStream &rToCopy) { /* do not call */ }
+ void AfterOpen();
-#ifdef WIN32
// for debugging..
- std::string fileName;
-#endif
+ std::string mFileName;
};
diff --git a/lib/common/Guards.h b/lib/common/Guards.h
index d2fb84e0..cd2e4628 100644
--- a/lib/common/Guards.h
+++ b/lib/common/Guards.h
@@ -37,8 +37,8 @@ public:
{
if(mOSFileHandle < 0)
{
- BOX_ERROR("FileHandleGuard: failed to open file '" <<
- rFilename << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("FileHandleGuard: failed to open "
+ "file '" << rFilename << "'");
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
}
diff --git a/lib/common/IOStream.cpp b/lib/common/IOStream.cpp
index 3c7be561..fc9d0bc3 100644
--- a/lib/common/IOStream.cpp
+++ b/lib/common/IOStream.cpp
@@ -29,19 +29,6 @@ IOStream::IOStream()
// --------------------------------------------------------------------------
//
// Function
-// Name: IOStream::IOStream(const IOStream &)
-// Purpose: Copy constructor (exceptions)
-// Created: 2003/07/31
-//
-// --------------------------------------------------------------------------
-IOStream::IOStream(const IOStream &rToCopy)
-{
- THROW_EXCEPTION(CommonException, NotSupported)
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
// Name: IOStream::~IOStream()
// Purpose: Destructor
// Created: 2003/07/31
@@ -238,4 +225,27 @@ bool IOStream::CopyStreamTo(IOStream &rCopyTo, int Timeout, int BufferSize)
return true; // completed
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: IOStream::Flush(int Timeout)
+// Purpose: Read and discard all remaining data in stream.
+// Useful for protocol streams which must be flushed
+// to avoid breaking the protocol.
+// Created: 2008/08/20
+//
+// --------------------------------------------------------------------------
+void IOStream::Flush(int Timeout)
+{
+ char buffer[4096];
+ while(StreamDataLeft())
+ {
+ Read(buffer, sizeof(buffer), Timeout);
+ }
+}
+
+void IOStream::Write(const char *pBuffer)
+{
+ Write(pBuffer, strlen(pBuffer));
+}
diff --git a/lib/common/IOStream.h b/lib/common/IOStream.h
index 042ccca4..0b1cedd3 100644
--- a/lib/common/IOStream.h
+++ b/lib/common/IOStream.h
@@ -22,9 +22,13 @@ class IOStream
{
public:
IOStream();
- IOStream(const IOStream &rToCopy);
virtual ~IOStream();
-
+
+private:
+ IOStream(const IOStream &rToCopy); /* forbidden */
+ IOStream& operator=(const IOStream &rToCopy); /* forbidden */
+
+public:
enum
{
TimeOutInfinite = -1,
@@ -44,6 +48,7 @@ public:
virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite) = 0;
virtual pos_type BytesLeftToRead(); // may return IOStream::SizeOfStreamUnknown (and will for most stream types)
virtual void Write(const void *pBuffer, int NBytes) = 0;
+ virtual void Write(const char *pBuffer);
virtual void WriteAllBuffered();
virtual pos_type GetPosition() const;
virtual void Seek(pos_type Offset, int SeekType);
@@ -57,6 +62,7 @@ public:
// Utility functions
bool ReadFullBuffer(void *pBuffer, int NBytes, int *pNBytesRead, int Timeout = IOStream::TimeOutInfinite);
bool CopyStreamTo(IOStream &rCopyTo, int Timeout = IOStream::TimeOutInfinite, int BufferSize = 1024);
+ void Flush(int Timeout = IOStream::TimeOutInfinite);
static int ConvertSeekTypeToOSWhence(int SeekType);
};
diff --git a/lib/common/IOStreamGetLine.h b/lib/common/IOStreamGetLine.h
index cf152e5a..9a5d1818 100644
--- a/lib/common/IOStreamGetLine.h
+++ b/lib/common/IOStreamGetLine.h
@@ -14,7 +14,7 @@
#include "IOStream.h"
-#ifdef NDEBUG
+#ifdef BOX_RELEASE_BUILD
#define IOSTREAMGETLINE_BUFFER_SIZE 1024
#else
#define IOSTREAMGETLINE_BUFFER_SIZE 4
diff --git a/lib/common/Logging.cpp b/lib/common/Logging.cpp
index 2b81b52b..1f872d93 100644
--- a/lib/common/Logging.cpp
+++ b/lib/common/Logging.cpp
@@ -15,12 +15,15 @@
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
+#ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+#endif
-#include "Logging.h"
-
+#include <cstring>
#include <iomanip>
#include "BoxTime.h"
+#include "Logging.h"
bool Logging::sLogToSyslog = false;
bool Logging::sLogToConsole = false;
@@ -32,6 +35,7 @@ Console* Logging::spConsole = NULL;
Syslog* Logging::spSyslog = NULL;
Log::Level Logging::sGlobalLevel = Log::EVERYTHING;
Logging Logging::sGlobalLogging; //automatic initialisation
+std::string Logging::sProgramName;
Logging::Logging()
{
@@ -148,12 +152,54 @@ void Logging::Log(Log::Level level, const std::string& rFile,
}
}
+void Logging::LogToSyslog(Log::Level level, const std::string& rFile,
+ int line, const std::string& rMessage)
+{
+ if (!sLogToSyslog)
+ {
+ return;
+ }
+
+ if (level > sGlobalLevel)
+ {
+ return;
+ }
+
+ std::string newMessage;
+
+ if (sContextSet)
+ {
+ newMessage += "[" + sContext + "] ";
+ }
+
+ newMessage += rMessage;
+
+ spSyslog->Log(level, rFile, line, newMessage);
+}
+
void Logging::SetContext(std::string context)
{
sContext = context;
sContextSet = true;
}
+Log::Level Logging::GetNamedLevel(const std::string& rName)
+{
+ if (rName == "nothing") { return Log::NOTHING; }
+ else if (rName == "fatal") { return Log::FATAL; }
+ else if (rName == "error") { return Log::ERROR; }
+ else if (rName == "warning") { return Log::WARNING; }
+ else if (rName == "notice") { return Log::NOTICE; }
+ else if (rName == "info") { return Log::INFO; }
+ else if (rName == "trace") { return Log::TRACE; }
+ else if (rName == "everything") { return Log::EVERYTHING; }
+ else
+ {
+ BOX_ERROR("Unknown verbosity level: " << rName);
+ return Log::INVALID;
+ }
+}
+
void Logging::ClearContext()
{
sContextSet = false;
@@ -161,6 +207,8 @@ void Logging::ClearContext()
void Logging::SetProgramName(const std::string& rProgramName)
{
+ sProgramName = rProgramName;
+
for (std::vector<Logger*>::iterator i = sLoggers.begin();
i != sLoggers.end(); i++)
{
@@ -168,12 +216,23 @@ void Logging::SetProgramName(const std::string& rProgramName)
}
}
+void Logging::SetFacility(int facility)
+{
+ spSyslog->SetFacility(facility);
+}
+
Logger::Logger()
: mCurrentLevel(Log::EVERYTHING)
{
Logging::Add(this);
}
+Logger::Logger(Log::Level Level)
+: mCurrentLevel(Level)
+{
+ Logging::Add(this);
+}
+
Logger::~Logger()
{
Logging::Remove(this);
@@ -182,12 +241,17 @@ Logger::~Logger()
bool Console::sShowTime = false;
bool Console::sShowTimeMicros = false;
bool Console::sShowTag = false;
+bool Console::sShowPID = false;
std::string Console::sTag;
-void Console::SetTag(const std::string& rTag)
+void Console::SetProgramName(const std::string& rProgramName)
+{
+ sTag = rProgramName;
+}
+
+void Console::SetShowTag(bool enabled)
{
- sTag = rTag;
- sShowTag = true;
+ sShowTag = enabled;
}
void Console::SetShowTime(bool enabled)
@@ -200,6 +264,11 @@ void Console::SetShowTimeMicros(bool enabled)
sShowTimeMicros = enabled;
}
+void Console::SetShowPID(bool enabled)
+{
+ sShowPID = enabled;
+}
+
bool Console::Log(Log::Level level, const std::string& rFile,
int line, std::string& rMessage)
{
@@ -215,69 +284,64 @@ bool Console::Log(Log::Level level, const std::string& rFile,
target = stderr;
}
- std::string msg;
+ std::ostringstream buf;
if (sShowTime)
{
- box_time_t time_now = GetCurrentBoxTime();
- time_t seconds = BoxTimeToSeconds(time_now);
- int micros = BoxTimeToMicroSeconds(time_now) % MICRO_SEC_IN_SEC;
-
- struct tm tm_now, *tm_ptr = &tm_now;
+ buf << FormatTime(GetCurrentBoxTime(), false, sShowTimeMicros);
+ buf << " ";
+ }
- #ifdef WIN32
- if ((tm_ptr = localtime(&seconds)) != NULL)
- #else
- if (localtime_r(&seconds, &tm_now) != NULL)
- #endif
+ if (sShowTag)
+ {
+ if (sShowPID)
{
- std::ostringstream buf;
-
- buf << std::setfill('0') <<
- std::setw(2) << tm_ptr->tm_hour << ":" <<
- std::setw(2) << tm_ptr->tm_min << ":" <<
- std::setw(2) << tm_ptr->tm_sec;
-
- if (sShowTimeMicros)
- {
- buf << "." << std::setw(6) << micros;
- }
-
- buf << " ";
- msg += buf.str();
+ buf << "[" << sTag << " " << getpid() << "] ";
}
else
{
- msg += strerror(errno);
- msg += " ";
+ buf << "[" << sTag << "] ";
}
}
-
- if (sShowTag)
+ else if (sShowPID)
{
- msg += "[" + sTag + "] ";
+ buf << "[" << getpid() << "] ";
}
if (level <= Log::FATAL)
{
- msg += "FATAL: ";
+ buf << "FATAL: ";
}
else if (level <= Log::ERROR)
{
- msg += "ERROR: ";
+ buf << "ERROR: ";
}
else if (level <= Log::WARNING)
{
- msg += "WARNING: ";
+ buf << "WARNING: ";
}
else if (level <= Log::NOTICE)
{
- msg += "NOTICE: ";
+ buf << "NOTICE: ";
}
-
- msg += rMessage;
+ else if (level <= Log::INFO)
+ {
+ buf << "INFO: ";
+ }
+ else if (level <= Log::TRACE)
+ {
+ buf << "TRACE: ";
+ }
+
+ buf << rMessage;
- fprintf(target, "%s\n", msg.c_str());
+ #ifdef WIN32
+ std::string output = buf.str();
+ ConvertUtf8ToConsole(output.c_str(), output);
+ fprintf(target, "%s\n", output.c_str());
+ #else
+ fprintf(target, "%s\n", buf.str().c_str());
+ #endif
return true;
}
@@ -295,6 +359,7 @@ bool Syslog::Log(Log::Level level, const std::string& rFile,
switch(level)
{
case Log::NOTHING: /* fall through */
+ case Log::INVALID: /* fall through */
case Log::FATAL: syslogLevel = LOG_CRIT; break;
case Log::ERROR: syslogLevel = LOG_ERR; break;
case Log::WARNING: syslogLevel = LOG_WARNING; break;
@@ -330,9 +395,9 @@ bool Syslog::Log(Log::Level level, const std::string& rFile,
return true;
}
-Syslog::Syslog()
+Syslog::Syslog() : mFacility(LOG_LOCAL6)
{
- ::openlog("Box Backup", LOG_PID, LOG_LOCAL6);
+ ::openlog("Box Backup", LOG_PID, mFacility);
}
Syslog::~Syslog()
@@ -344,5 +409,83 @@ void Syslog::SetProgramName(const std::string& rProgramName)
{
mName = rProgramName;
::closelog();
- ::openlog(mName.c_str(), LOG_PID, LOG_LOCAL6);
+ ::openlog(mName.c_str(), LOG_PID, mFacility);
+}
+
+void Syslog::SetFacility(int facility)
+{
+ mFacility = facility;
+ ::closelog();
+ ::openlog(mName.c_str(), LOG_PID, mFacility);
+}
+
+int Syslog::GetNamedFacility(const std::string& rFacility)
+{
+ #define CASE_RETURN(x) if (rFacility == #x) { return LOG_ ## x; }
+ CASE_RETURN(LOCAL0)
+ CASE_RETURN(LOCAL1)
+ CASE_RETURN(LOCAL2)
+ CASE_RETURN(LOCAL3)
+ CASE_RETURN(LOCAL4)
+ CASE_RETURN(LOCAL5)
+ CASE_RETURN(LOCAL6)
+ CASE_RETURN(DAEMON)
+ #undef CASE_RETURN
+
+ BOX_ERROR("Unknown log facility '" << rFacility << "', "
+ "using default LOCAL6");
+ return LOG_LOCAL6;
+}
+
+bool FileLogger::Log(Log::Level Level, const std::string& rFile,
+ int line, std::string& rMessage)
+{
+ if (Level > GetLevel())
+ {
+ return true;
+ }
+
+ /* avoid infinite loop if this throws an exception */
+ Logging::Remove(this);
+
+ std::ostringstream buf;
+ buf << FormatTime(GetCurrentBoxTime(), true, false);
+ buf << " ";
+
+ if (Level <= Log::FATAL)
+ {
+ buf << "[FATAL] ";
+ }
+ else if (Level <= Log::ERROR)
+ {
+ buf << "[ERROR] ";
+ }
+ else if (Level <= Log::WARNING)
+ {
+ buf << "[WARNING] ";
+ }
+ else if (Level <= Log::NOTICE)
+ {
+ buf << "[NOTICE] ";
+ }
+ else if (Level <= Log::INFO)
+ {
+ buf << "[INFO] ";
+ }
+ else if (Level <= Log::TRACE)
+ {
+ buf << "[TRACE] ";
+ }
+
+ buf << rMessage << "\n";
+ std::string output = buf.str();
+
+ #ifdef WIN32
+ ConvertUtf8ToConsole(output.c_str(), output);
+ #endif
+
+ mLogFile.Write(output.c_str(), output.length());
+
+ Logging::Add(this);
+ return true;
}
diff --git a/lib/common/Logging.h b/lib/common/Logging.h
index 78db2bea..9bb2cf6c 100644
--- a/lib/common/Logging.h
+++ b/lib/common/Logging.h
@@ -10,10 +10,14 @@
#ifndef LOGGING__H
#define LOGGING__H
+#include <cerrno>
+#include <cstring>
#include <iomanip>
#include <sstream>
#include <vector>
+#include "FileStream.h"
+
/*
#define BOX_LOG(level, stuff) \
{ \
@@ -27,9 +31,16 @@
#define BOX_LOG(level, stuff) \
{ \
- std::ostringstream line; \
- line << stuff; \
- Logging::Log(level, __FILE__, __LINE__, line.str()); \
+ std::ostringstream _box_log_line; \
+ _box_log_line << stuff; \
+ Logging::Log(level, __FILE__, __LINE__, _box_log_line.str()); \
+}
+
+#define BOX_SYSLOG(level, stuff) \
+{ \
+ std::ostringstream _box_log_line; \
+ _box_log_line << stuff; \
+ Logging::LogToSyslog(level, __FILE__, __LINE__, _box_log_line.str()); \
}
#define BOX_FATAL(stuff) BOX_LOG(Log::FATAL, stuff)
@@ -41,15 +52,56 @@
if (Logging::IsEnabled(Log::TRACE)) \
{ BOX_LOG(Log::TRACE, stuff) }
-#define BOX_FORMAT_ACCOUNT(accno) \
+#define BOX_LOG_SYS_WARNING(stuff) \
+ BOX_WARNING(stuff << ": " << std::strerror(errno) << " (" << errno << ")")
+#define BOX_LOG_SYS_ERROR(stuff) \
+ BOX_ERROR(stuff << ": " << std::strerror(errno) << " (" << errno << ")")
+#define BOX_LOG_SYS_FATAL(stuff) \
+ BOX_FATAL(stuff << ": " << std::strerror(errno) << " (" << errno << ")")
+
+inline std::string GetNativeErrorMessage()
+{
+#ifdef WIN32
+ return GetErrorMessage(GetLastError());
+#else
+ std::ostringstream _box_log_line;
+ _box_log_line << std::strerror(errno) << " (" << errno << ")";
+ return _box_log_line.str();
+#endif
+}
+
+#ifdef WIN32
+ #define BOX_LOG_WIN_ERROR(stuff) \
+ BOX_ERROR(stuff << ": " << GetErrorMessage(GetLastError()))
+ #define BOX_LOG_WIN_WARNING(stuff) \
+ BOX_WARNING(stuff << ": " << GetErrorMessage(GetLastError()))
+ #define BOX_LOG_WIN_ERROR_NUMBER(stuff, number) \
+ BOX_ERROR(stuff << ": " << GetErrorMessage(number))
+ #define BOX_LOG_WIN_WARNING_NUMBER(stuff, number) \
+ BOX_WARNING(stuff << ": " << GetErrorMessage(number))
+ #define BOX_LOG_NATIVE_ERROR(stuff) BOX_LOG_WIN_ERROR(stuff)
+ #define BOX_LOG_NATIVE_WARNING(stuff) BOX_LOG_WIN_WARNING(stuff)
+#else
+ #define BOX_LOG_NATIVE_ERROR(stuff) BOX_LOG_SYS_ERROR(stuff)
+ #define BOX_LOG_NATIVE_WARNING(stuff) BOX_LOG_SYS_WARNING(stuff)
+#endif
+
+#define BOX_LOG_SOCKET_ERROR(_type, _name, _port, stuff) \
+ BOX_LOG_NATIVE_ERROR(stuff << " (type " << _type << ", name " << \
+ _name << ", port " << _port << ")")
+
+#define BOX_FORMAT_HEX32(number) \
std::hex << \
std::showbase << \
std::internal << \
std::setw(10) << \
std::setfill('0') << \
- (accno) << \
+ (number) << \
std::dec
+#define BOX_FORMAT_ACCOUNT(accno) \
+ BOX_FORMAT_HEX32(accno)
+
#define BOX_FORMAT_OBJECTID(objectid) \
std::hex << \
std::showbase << \
@@ -69,7 +121,8 @@ namespace Log
NOTICE,
INFO,
TRACE,
- EVERYTHING
+ EVERYTHING,
+ INVALID = -1
};
}
@@ -89,6 +142,7 @@ class Logger
public:
Logger();
+ Logger(Log::Level level);
virtual ~Logger();
virtual bool Log(Log::Level level, const std::string& rFile,
@@ -117,20 +171,22 @@ class Logger
class Console : public Logger
{
private:
+ static bool sShowTag;
static bool sShowTime;
static bool sShowTimeMicros;
- static bool sShowTag;
+ static bool sShowPID;
static std::string sTag;
public:
virtual bool Log(Log::Level level, const std::string& rFile,
int line, std::string& rMessage);
virtual const char* GetType() { return "Console"; }
- virtual void SetProgramName(const std::string& rProgramName) { }
+ virtual void SetProgramName(const std::string& rProgramName);
- static void SetTag(const std::string& rTag);
+ static void SetShowTag(bool enabled);
static void SetShowTime(bool enabled);
static void SetShowTimeMicros(bool enabled);
+ static void SetShowPID(bool enabled);
};
// --------------------------------------------------------------------------
@@ -146,6 +202,7 @@ class Syslog : public Logger
{
private:
std::string mName;
+ int mFacility;
public:
Syslog();
@@ -155,6 +212,8 @@ class Syslog : public Logger
int line, std::string& rMessage);
virtual const char* GetType() { return "Syslog"; }
virtual void SetProgramName(const std::string& rProgramName);
+ virtual void SetFacility(int facility);
+ static int GetNamedFacility(const std::string& rFacility);
};
// --------------------------------------------------------------------------
@@ -178,6 +237,7 @@ class Logging
static Syslog* spSyslog;
static Log::Level sGlobalLevel;
static Logging sGlobalLogging;
+ static std::string sProgramName;
public:
Logging ();
@@ -190,14 +250,74 @@ class Logging
static void Remove (Logger* pOldLogger);
static void Log(Log::Level level, const std::string& rFile,
int line, const std::string& rMessage);
+ static void LogToSyslog(Log::Level level, const std::string& rFile,
+ int line, const std::string& rMessage);
static void SetContext(std::string context);
static void ClearContext();
static void SetGlobalLevel(Log::Level level) { sGlobalLevel = level; }
+ static Log::Level GetGlobalLevel() { return sGlobalLevel; }
+ static Log::Level GetNamedLevel(const std::string& rName);
static bool IsEnabled(Log::Level level)
{
return (int)sGlobalLevel >= (int)level;
}
static void SetProgramName(const std::string& rProgramName);
+ static std::string GetProgramName() { return sProgramName; }
+ static void SetFacility(int facility);
+
+ class Guard
+ {
+ private:
+ Log::Level mOldLevel;
+
+ public:
+ Guard(Log::Level newLevel)
+ {
+ mOldLevel = Logging::GetGlobalLevel();
+ Logging::SetGlobalLevel(newLevel);
+ }
+ ~Guard()
+ {
+ Logging::SetGlobalLevel(mOldLevel);
+ }
+ };
+
+ class Tagger
+ {
+ private:
+ std::string mOldTag;
+
+ public:
+ Tagger(const std::string& rTempTag)
+ {
+ mOldTag = Logging::GetProgramName();
+ Logging::SetProgramName(mOldTag + " " + rTempTag);
+ }
+ ~Tagger()
+ {
+ Logging::SetProgramName(mOldTag);
+ }
+ };
+};
+
+class FileLogger : public Logger
+{
+ private:
+ FileStream mLogFile;
+ FileLogger(const FileLogger& forbidden)
+ : mLogFile("") { /* do not call */ }
+
+ public:
+ FileLogger(const std::string& rFileName, Log::Level Level)
+ : Logger(Level),
+ mLogFile(rFileName, O_WRONLY | O_CREAT | O_APPEND)
+ { }
+
+ virtual bool Log(Log::Level Level, const std::string& rFile,
+ int Line, std::string& rMessage);
+
+ virtual const char* GetType() { return "FileLogger"; }
+ virtual void SetProgramName(const std::string& rProgramName) { }
};
#endif // LOGGING__H
diff --git a/lib/common/NamedLock.cpp b/lib/common/NamedLock.cpp
index 16c580ba..b3d0009b 100644
--- a/lib/common/NamedLock.cpp
+++ b/lib/common/NamedLock.cpp
@@ -2,7 +2,8 @@
//
// File
// Name: NamedLock.cpp
-// Purpose: A global named lock, implemented as a lock file in file system
+// Purpose: A global named lock, implemented as a lock file in
+// file system
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
@@ -58,8 +59,9 @@ NamedLock::~NamedLock()
//
// Function
// Name: NamedLock::TryAndGetLock(const char *, int)
-// Purpose: Trys to get a lock on the name in the file system.
-// IMPORTANT NOTE: If a file exists with this name, it will be deleted.
+// Purpose: Tries to get a lock on the name in the file system.
+// IMPORTANT NOTE: If a file exists with this name, it
+// will be deleted.
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
diff --git a/lib/common/PartialReadStream.cpp b/lib/common/PartialReadStream.cpp
index 76096738..f2f79715 100644
--- a/lib/common/PartialReadStream.cpp
+++ b/lib/common/PartialReadStream.cpp
@@ -44,7 +44,8 @@ PartialReadStream::~PartialReadStream()
// Warn in debug mode
if(mBytesLeft != 0)
{
- TRACE1("PartialReadStream::~PartialReadStream when mBytesLeft = %d\n", mBytesLeft);
+ BOX_TRACE("PartialReadStream destroyed with " << mBytesLeft <<
+ " bytes remaining");
}
}
diff --git a/lib/common/ReadLoggingStream.cpp b/lib/common/ReadLoggingStream.cpp
index 9023f827..54c99c95 100644
--- a/lib/common/ReadLoggingStream.cpp
+++ b/lib/common/ReadLoggingStream.cpp
@@ -25,12 +25,13 @@
// Created: 2007/01/16
//
// --------------------------------------------------------------------------
-ReadLoggingStream::ReadLoggingStream(IOStream& rSource)
+ReadLoggingStream::ReadLoggingStream(IOStream& rSource, Logger& rLogger)
: mrSource(rSource),
mOffset(0),
mLength(mrSource.BytesLeftToRead()),
mTotalRead(0),
- mStartTime(GetCurrentBoxTime())
+ mStartTime(GetCurrentBoxTime()),
+ mrLogger(rLogger)
{ }
@@ -52,26 +53,21 @@ int ReadLoggingStream::Read(void *pBuffer, int NBytes, int Timeout)
mOffset += numBytesRead;
}
- if (mLength >= 0 && mTotalRead > 0)
+ if (mLength == 0)
{
- box_time_t timeNow = GetCurrentBoxTime();
- box_time_t elapsed = timeNow - mStartTime;
- box_time_t finish = (elapsed * mLength) / mTotalRead;
- box_time_t remain = finish - elapsed;
-
- BOX_TRACE("Read " << numBytesRead << " bytes at " << mOffset <<
- ", " << (mLength - mOffset) << " remain, eta " <<
- BoxTimeToSeconds(remain) << "s");
+ mrLogger.Log(numBytesRead, mOffset);
}
- else if (mLength >= 0 && mTotalRead == 0)
+ else if (mTotalRead == 0)
{
- BOX_TRACE("Read " << numBytesRead << " bytes at " << mOffset <<
- ", " << (mLength - mOffset) << " remain");
+ mrLogger.Log(numBytesRead, mOffset, mLength);
}
else
{
- BOX_TRACE("Read " << numBytesRead << " bytes at " << mOffset <<
- ", unknown bytes remaining");
+ box_time_t timeNow = GetCurrentBoxTime();
+ box_time_t elapsed = timeNow - mStartTime;
+ box_time_t finish = (elapsed * mLength) / mTotalRead;
+ // box_time_t remain = finish - elapsed;
+ mrLogger.Log(numBytesRead, mOffset, mLength, elapsed, finish);
}
return numBytesRead;
diff --git a/lib/common/ReadLoggingStream.h b/lib/common/ReadLoggingStream.h
index 15c3ef48..b23b542c 100644
--- a/lib/common/ReadLoggingStream.h
+++ b/lib/common/ReadLoggingStream.h
@@ -15,13 +15,27 @@
class ReadLoggingStream : public IOStream
{
+public:
+ class Logger
+ {
+ public:
+ virtual ~Logger() { }
+ virtual void Log(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed,
+ box_time_t finish) = 0;
+ virtual void Log(int64_t readSize, int64_t offset,
+ int64_t length) = 0;
+ virtual void Log(int64_t readSize, int64_t offset) = 0;
+ };
+
private:
IOStream& mrSource;
IOStream::pos_type mOffset, mLength, mTotalRead;
box_time_t mStartTime;
+ Logger& mrLogger;
public:
- ReadLoggingStream(IOStream& rSource);
+ ReadLoggingStream(IOStream& rSource, Logger& rLogger);
virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
virtual pos_type BytesLeftToRead();
@@ -35,7 +49,8 @@ public:
private:
ReadLoggingStream(const ReadLoggingStream &rToCopy)
- : mrSource(rToCopy.mrSource) { /* do not call */ }
+ : mrSource(rToCopy.mrSource), mrLogger(rToCopy.mrLogger)
+ { /* do not call */ }
};
#endif // READLOGGINGSTREAM__H
diff --git a/lib/common/SelfFlushingStream.h b/lib/common/SelfFlushingStream.h
new file mode 100644
index 00000000..36e9a4d3
--- /dev/null
+++ b/lib/common/SelfFlushingStream.h
@@ -0,0 +1,71 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: SelfFlushingStream.h
+// Purpose: A stream wrapper that always flushes the underlying
+// stream, to ensure protocol safety.
+// Created: 2008/08/20
+//
+// --------------------------------------------------------------------------
+
+#ifndef SELFFLUSHINGSTREAM__H
+#define SELFFLUSHINGSTREAM__H
+
+#include "IOStream.h"
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: SelfFlushingStream
+// Purpose: A stream wrapper that always flushes the underlying
+// stream, to ensure protocol safety.
+// Created: 2008/08/20
+//
+// --------------------------------------------------------------------------
+class SelfFlushingStream : public IOStream
+{
+public:
+ SelfFlushingStream(IOStream &rSource)
+ : mrSource(rSource) { }
+
+ SelfFlushingStream(const SelfFlushingStream &rToCopy)
+ : mrSource(rToCopy.mrSource) { }
+
+ ~SelfFlushingStream()
+ {
+ Flush();
+ }
+
+private:
+ // no copying from IOStream allowed
+ SelfFlushingStream(const IOStream& rToCopy);
+
+public:
+ virtual int Read(void *pBuffer, int NBytes,
+ int Timeout = IOStream::TimeOutInfinite)
+ {
+ return mrSource.Read(pBuffer, NBytes, Timeout);
+ }
+ virtual pos_type BytesLeftToRead()
+ {
+ return mrSource.BytesLeftToRead();
+ }
+ virtual void Write(const void *pBuffer, int NBytes)
+ {
+ mrSource.Write(pBuffer, NBytes);
+ }
+ virtual bool StreamDataLeft()
+ {
+ return mrSource.StreamDataLeft();
+ }
+ virtual bool StreamClosed()
+ {
+ return mrSource.StreamClosed();
+ }
+
+private:
+ IOStream &mrSource;
+};
+
+#endif // SELFFLUSHINGSTREAM__H
+
diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp
new file mode 100644
index 00000000..04d778c1
--- /dev/null
+++ b/lib/common/Test.cpp
@@ -0,0 +1,418 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: Test.cpp
+// Purpose: Useful stuff for tests
+// Created: 2008/04/05
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+#endif
+
+#include "Test.h"
+
+bool TestFileExists(const char *Filename)
+{
+ EMU_STRUCT_STAT st;
+ return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0;
+}
+
+bool TestFileNotEmpty(const char *Filename)
+{
+ EMU_STRUCT_STAT st;
+ return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0 &&
+ st.st_size > 0;
+}
+
+bool TestDirExists(const char *Filename)
+{
+ EMU_STRUCT_STAT st;
+ return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR;
+}
+
+// -1 if doesn't exist
+int TestGetFileSize(const char *Filename)
+{
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(Filename, &st) == 0)
+ {
+ return st.st_size;
+ }
+ return -1;
+}
+
+std::string ConvertPaths(const std::string& rOriginal)
+{
+#ifdef WIN32
+ // convert UNIX paths to native
+
+ std::string converted;
+ for (size_t i = 0; i < rOriginal.size(); i++)
+ {
+ if (rOriginal[i] == '/')
+ {
+ converted += '\\';
+ }
+ else
+ {
+ converted += rOriginal[i];
+ }
+ }
+ return converted;
+
+#else // !WIN32
+ return rOriginal;
+#endif
+}
+
+int RunCommand(const std::string& rCommandLine)
+{
+ return ::system(ConvertPaths(rCommandLine).c_str());
+}
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+bool ServerIsAlive(int pid)
+{
+ #ifdef WIN32
+
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
+ false, pid);
+ if (hProcess == NULL)
+ {
+ if (GetLastError() != ERROR_INVALID_PARAMETER)
+ {
+ BOX_ERROR("Failed to open process " << pid <<
+ ": " <<
+ GetErrorMessage(GetLastError()));
+ }
+ return false;
+ }
+
+ DWORD exitCode;
+ BOOL result = GetExitCodeProcess(hProcess, &exitCode);
+ CloseHandle(hProcess);
+
+ if (result == 0)
+ {
+ BOX_ERROR("Failed to get exit code for process " <<
+ pid << ": " <<
+ GetErrorMessage(GetLastError()))
+ return false;
+ }
+
+ if (exitCode == STILL_ACTIVE)
+ {
+ return true;
+ }
+
+ return false;
+
+ #else // !WIN32
+
+ if(pid == 0) return false;
+ return ::kill(pid, 0) != -1;
+
+ #endif // WIN32
+}
+
+int ReadPidFile(const char *pidFile)
+{
+ if(!TestFileNotEmpty(pidFile))
+ {
+ TEST_FAIL_WITH_MESSAGE("Server didn't save PID file "
+ "(perhaps one was already running?)");
+ return -1;
+ }
+
+ int pid = -1;
+
+ FILE *f = fopen(pidFile, "r");
+ if(f == NULL || fscanf(f, "%d", &pid) != 1)
+ {
+ TEST_FAIL_WITH_MESSAGE("Couldn't read PID file");
+ return -1;
+ }
+ fclose(f);
+
+ return pid;
+}
+
+int LaunchServer(const std::string& rCommandLine, const char *pidFile)
+{
+ ::fprintf(stdout, "Starting server: %s\n", rCommandLine.c_str());
+
+#ifdef WIN32
+
+ PROCESS_INFORMATION procInfo;
+
+ STARTUPINFO startInfo;
+ startInfo.cb = sizeof(startInfo);
+ startInfo.lpReserved = NULL;
+ startInfo.lpDesktop = NULL;
+ startInfo.lpTitle = NULL;
+ startInfo.dwFlags = 0;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpReserved2 = NULL;
+
+ std::string cmd = ConvertPaths(rCommandLine);
+ CHAR* tempCmd = strdup(cmd.c_str());
+
+ DWORD result = CreateProcess
+ (
+ NULL, // lpApplicationName, naughty!
+ tempCmd, // lpCommandLine
+ NULL, // lpProcessAttributes
+ NULL, // lpThreadAttributes
+ false, // bInheritHandles
+ 0, // dwCreationFlags
+ NULL, // lpEnvironment
+ NULL, // lpCurrentDirectory
+ &startInfo, // lpStartupInfo
+ &procInfo // lpProcessInformation
+ );
+
+ free(tempCmd);
+
+ if (result == 0)
+ {
+ DWORD err = GetLastError();
+ printf("Launch failed: %s: error %d\n", rCommandLine.c_str(),
+ (int)err);
+ TEST_FAIL_WITH_MESSAGE("Couldn't start server");
+ return -1;
+ }
+
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+
+ return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId);
+
+#else // !WIN32
+
+ if(RunCommand(rCommandLine) != 0)
+ {
+ TEST_FAIL_WITH_MESSAGE("Couldn't start server");
+ return -1;
+ }
+
+ return WaitForServerStartup(pidFile, 0);
+
+#endif // WIN32
+}
+
+int WaitForServerStartup(const char *pidFile, int pidIfKnown)
+{
+ #ifdef WIN32
+ if (pidFile == NULL)
+ {
+ return pidIfKnown;
+ }
+ #else
+ // on other platforms there is no other way to get
+ // the PID, so a NULL pidFile doesn't make sense.
+ ASSERT(pidFile != NULL);
+ #endif
+
+ // time for it to start up
+ ::fprintf(stdout, "Waiting for server to start: ");
+
+ for (int i = 0; i < 15; i++)
+ {
+ if (TestFileNotEmpty(pidFile))
+ {
+ break;
+ }
+
+ if (pidIfKnown && !ServerIsAlive(pidIfKnown))
+ {
+ break;
+ }
+
+ ::fprintf(stdout, ".");
+ ::fflush(stdout);
+ ::sleep(1);
+ }
+
+ // on Win32 we can check whether the process is alive
+ // without even checking the PID file
+
+ if (pidIfKnown && !ServerIsAlive(pidIfKnown))
+ {
+ ::fprintf(stdout, " server died!\n");
+ TEST_FAIL_WITH_MESSAGE("Server died!");
+ return -1;
+ }
+
+ if (!TestFileNotEmpty(pidFile))
+ {
+ ::fprintf(stdout, " timed out!\n");
+ TEST_FAIL_WITH_MESSAGE("Server didn't save PID file");
+ return -1;
+ }
+
+ ::fprintf(stdout, " done.\n");
+
+ // wait a second for the pid to be written to the file
+ ::sleep(1);
+
+ // read pid file
+ int pid = ReadPidFile(pidFile);
+
+ // On Win32 we can check whether the PID in the pidFile matches
+ // the one returned by the system, which it always should.
+
+ if (pidIfKnown && pid != pidIfKnown)
+ {
+ printf("Server wrote wrong pid to file (%s): expected %d "
+ "but found %d\n", pidFile, pidIfKnown, pid);
+ TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file");
+ return -1;
+ }
+
+ return pid;
+}
+
+void TestRemoteProcessMemLeaksFunc(const char *filename,
+ const char* file, int line)
+{
+#ifdef BOX_MEMORY_LEAK_TESTING
+ // Does the file exist?
+ if(!TestFileExists(filename))
+ {
+ if (failures == 0)
+ {
+ first_fail_file = file;
+ first_fail_line = line;
+ }
+ ++failures;
+ printf("FAILURE: MemLeak report not available (file %s) "
+ "at %s:%d\n", filename, file, line);
+ }
+ else
+ {
+ // Is it empty?
+ if(TestGetFileSize(filename) > 0)
+ {
+ if (failures == 0)
+ {
+ first_fail_file = file;
+ first_fail_line = line;
+ }
+ ++failures;
+ printf("FAILURE: Memory leaks found in other process "
+ "(file %s) at %s:%d\n==========\n",
+ filename, file, line);
+ FILE *f = fopen(filename, "r");
+ char linebuf[512];
+ while(::fgets(linebuf, sizeof(linebuf), f) != 0)
+ {
+ printf("%s", linebuf);
+ }
+ fclose(f);
+ printf("==========\n");
+ }
+
+ // Delete it
+ ::unlink(filename);
+ }
+#endif
+}
+
+void force_sync()
+{
+ TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
+ "force-sync") == 0);
+ TestRemoteProcessMemLeaks("bbackupctl.memleaks");
+}
+
+void wait_for_sync_start()
+{
+ TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
+ "wait-for-sync") == 0);
+ TestRemoteProcessMemLeaks("bbackupctl.memleaks");
+}
+
+void wait_for_sync_end()
+{
+ TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
+ "wait-for-end") == 0);
+ TestRemoteProcessMemLeaks("bbackupctl.memleaks");
+}
+
+void sync_and_wait()
+{
+ TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
+ "sync-and-wait") == 0);
+ TestRemoteProcessMemLeaks("bbackupctl.memleaks");
+}
+
+void terminate_bbackupd(int pid)
+{
+ TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
+ "terminate") == 0);
+ TestRemoteProcessMemLeaks("bbackupctl.memleaks");
+
+ for (int i = 0; i < 20; i++)
+ {
+ if (!ServerIsAlive(pid)) break;
+ fprintf(stdout, ".");
+ fflush(stdout);
+ sleep(1);
+ }
+
+ TEST_THAT(!ServerIsAlive(pid));
+ TestRemoteProcessMemLeaks("bbackupd.memleaks");
+}
+
+
+// Wait a given number of seconds for something to complete
+void wait_for_operation(int seconds)
+{
+ printf("Waiting: ");
+ fflush(stdout);
+ for(int l = 0; l < seconds; ++l)
+ {
+ sleep(1);
+ printf(".");
+ fflush(stdout);
+ }
+ printf(" done.\n");
+ fflush(stdout);
+}
+
+void safe_sleep(int seconds)
+{
+#ifdef WIN32
+ Sleep(seconds * 1000);
+#else
+ struct timespec ts;
+ memset(&ts, 0, sizeof(ts));
+ ts.tv_sec = seconds;
+ ts.tv_nsec = 0;
+ BOX_TRACE("sleeping for " << seconds << " seconds");
+ while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
+ {
+ BOX_TRACE("safe_sleep interrupted with " <<
+ ts.tv_sec << "." << ts.tv_nsec <<
+ " secs remaining, sleeping again");
+ /* sleep again */
+ }
+#endif
+}
+
+
diff --git a/lib/common/Test.h b/lib/common/Test.h
index 49b0ac66..f4766ddc 100644
--- a/lib/common/Test.h
+++ b/lib/common/Test.h
@@ -10,18 +10,6 @@
#ifndef TEST__H
#define TEST__H
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#ifdef HAVE_UNISTD_H
- #include <unistd.h>
-#endif
-
#include <string>
#ifdef WIN32
@@ -53,7 +41,8 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args;
first_fail_line = __LINE__; \
} \
failures++; \
- printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__); \
+ BOX_ERROR("**** TEST FAILURE: " << msg << " at " << __FILE__ << \
+ ":" << __LINE__); \
}
#define TEST_ABORT_WITH_MESSAGE(msg) {TEST_FAIL_WITH_MESSAGE(msg); return 1;}
@@ -88,367 +77,88 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args;
} \
}
-inline bool TestFileExists(const char *Filename)
-{
- struct stat st;
- return ::stat(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0;
-}
-
-inline bool TestDirExists(const char *Filename)
-{
- struct stat st;
- return ::stat(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR;
-}
-
-// -1 if doesn't exist
-inline int TestGetFileSize(const char *Filename)
-{
- struct stat st;
- if(::stat(Filename, &st) == 0)
- {
- return st.st_size;
- }
- return -1;
-}
-
-inline std::string ConvertPaths(const std::string& rOriginal)
-{
-#ifdef WIN32
- // convert UNIX paths to native
-
- std::string converted;
- for (size_t i = 0; i < rOriginal.size(); i++)
- {
- if (rOriginal[i] == '/')
- {
- converted += '\\';
- }
- else
- {
- converted += rOriginal[i];
- }
- }
- return converted;
-
-#else // !WIN32
- return rOriginal;
-#endif
-}
-
-inline int RunCommand(const std::string& rCommandLine)
-{
- return ::system(ConvertPaths(rCommandLine).c_str());
-}
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-
-inline bool ServerIsAlive(int pid)
-{
-#ifdef WIN32
- HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
- if (hProcess == NULL)
- {
- if (GetLastError() != ERROR_INVALID_PARAMETER)
- {
- printf("Failed to open process %d: error %d\n",
- pid, (int)GetLastError());
- }
- return false;
- }
- CloseHandle(hProcess);
- return true;
-#else // !WIN32
- if(pid == 0) return false;
- return ::kill(pid, 0) != -1;
-#endif // WIN32
+// utility macro for comparing two strings in a line
+#define TEST_EQUAL(_expected, _found) \
+{ \
+ std::ostringstream _oss1; \
+ _oss1 << _expected; \
+ std::string _exp_str = _oss1.str(); \
+ \
+ std::ostringstream _oss2; \
+ _oss2 << _found; \
+ std::string _found_str = _oss2.str(); \
+ \
+ if(_exp_str != _found_str) \
+ { \
+ printf("Expected <%s> but found <%s>\n", \
+ _exp_str.c_str(), _found_str.c_str()); \
+ \
+ std::ostringstream _oss3; \
+ _oss3 << #_found << " != " << #_expected; \
+ \
+ TEST_FAIL_WITH_MESSAGE(_oss3.str().c_str()); \
+ } \
}
-inline int ReadPidFile(const char *pidFile)
-{
- if(!TestFileExists(pidFile))
- {
- TEST_FAIL_WITH_MESSAGE("Server didn't save PID file "
- "(perhaps one was already running?)");
- return -1;
- }
-
- int pid = -1;
-
- FILE *f = fopen(pidFile, "r");
- if(f == NULL || fscanf(f, "%d", &pid) != 1)
- {
- TEST_FAIL_WITH_MESSAGE("Couldn't read PID file");
- return -1;
- }
- fclose(f);
-
- return pid;
+// utility macro for comparing two strings in a line
+#define TEST_EQUAL_LINE(_expected, _found, _line) \
+{ \
+ std::ostringstream _oss1; \
+ _oss1 << _expected; \
+ std::string _exp_str = _oss1.str(); \
+ \
+ std::ostringstream _oss2; \
+ _oss2 << _found; \
+ std::string _found_str = _oss2.str(); \
+ \
+ if(_exp_str != _found_str) \
+ { \
+ std::string _line_str = _line; \
+ printf("Expected <%s> but found <%s> in <%s>\n", \
+ _exp_str.c_str(), _found_str.c_str(), _line_str.c_str()); \
+ \
+ std::ostringstream _oss3; \
+ _oss3 << #_found << " != " << #_expected << " in " << _line; \
+ \
+ TEST_FAIL_WITH_MESSAGE(_oss3.str().c_str()); \
+ } \
}
-inline int LaunchServer(const std::string& rCommandLine, const char *pidFile)
-{
-#ifdef WIN32
-
- PROCESS_INFORMATION procInfo;
-
- STARTUPINFO startInfo;
- startInfo.cb = sizeof(startInfo);
- startInfo.lpReserved = NULL;
- startInfo.lpDesktop = NULL;
- startInfo.lpTitle = NULL;
- startInfo.dwFlags = 0;
- startInfo.cbReserved2 = 0;
- startInfo.lpReserved2 = NULL;
-
- std::string cmd = ConvertPaths(rCommandLine);
- CHAR* tempCmd = strdup(cmd.c_str());
-
- DWORD result = CreateProcess
- (
- NULL, // lpApplicationName, naughty!
- tempCmd, // lpCommandLine
- NULL, // lpProcessAttributes
- NULL, // lpThreadAttributes
- false, // bInheritHandles
- 0, // dwCreationFlags
- NULL, // lpEnvironment
- NULL, // lpCurrentDirectory
- &startInfo, // lpStartupInfo
- &procInfo // lpProcessInformation
- );
-
- free(tempCmd);
-
- if (result == 0)
- {
- DWORD err = GetLastError();
- printf("Launch failed: %s: error %d\n", rCommandLine.c_str(),
- (int)err);
- return -1;
- }
-
- CloseHandle(procInfo.hProcess);
- CloseHandle(procInfo.hThread);
-
-#else // !WIN32
-
- if(RunCommand(rCommandLine) != 0)
- {
- printf("Server: %s\n", rCommandLine.c_str());
- TEST_FAIL_WITH_MESSAGE("Couldn't start server");
- return -1;
- }
-
-#endif // WIN32
-
- #ifdef WIN32
- // on other platforms there is no other way to get
- // the PID, so a NULL pidFile doesn't make sense.
-
- if (pidFile == NULL)
- {
- return (int)procInfo.dwProcessId;
- }
- #endif
-
- // time for it to start up
- ::fprintf(stdout, "Starting server: %s\n", rCommandLine.c_str());
- ::fprintf(stdout, "Waiting for server to start: ");
- for (int i = 0; i < 15; i++)
- {
- if (TestFileExists(pidFile))
- {
- break;
- }
-
- #ifdef WIN32
- if (!ServerIsAlive((int)procInfo.dwProcessId))
- {
- break;
- }
- #endif
-
- ::fprintf(stdout, ".");
- ::fflush(stdout);
- ::sleep(1);
- }
-
- #ifdef WIN32
- // on Win32 we can check whether the process is alive
- // without even checking the PID file
-
- if (!ServerIsAlive((int)procInfo.dwProcessId))
- {
- ::fprintf(stdout, "server died!\n");
- TEST_FAIL_WITH_MESSAGE("Server died!");
- return -1;
- }
- #endif
-
- if (!TestFileExists(pidFile))
- {
- ::fprintf(stdout, "timed out!\n");
- TEST_FAIL_WITH_MESSAGE("Server didn't save PID file");
- return -1;
+// utility macro for testing a line
+#define TEST_LINE(_condition, _line) \
+ TEST_THAT(_condition); \
+ if (!(_condition)) \
+ { \
+ printf("Test failed on <%s>\n", _line.c_str()); \
}
- ::fprintf(stdout, "done.\n");
-
- // wait a second for the pid to be written to the file
- ::sleep(1);
+bool TestFileExists(const char *Filename);
+bool TestDirExists(const char *Filename);
- // read pid file
- int pid = ReadPidFile(pidFile);
-
- #ifdef WIN32
- // On Win32 we can check whether the PID in the pidFile matches
- // the one returned by the system, which it always should.
-
- if (pid != (int)procInfo.dwProcessId)
- {
- printf("Server wrote wrong pid to file (%s): expected %d "
- "but found %d\n", pidFile,
- (int)procInfo.dwProcessId, pid);
- TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file");
- return -1;
- }
- #endif
-
- return pid;
-}
+// -1 if doesn't exist
+int TestGetFileSize(const char *Filename);
+std::string ConvertPaths(const std::string& rOriginal);
+int RunCommand(const std::string& rCommandLine);
+bool ServerIsAlive(int pid);
+int ReadPidFile(const char *pidFile);
+int LaunchServer(const std::string& rCommandLine, const char *pidFile);
+int WaitForServerStartup(const char *pidFile, int pidIfKnown);
#define TestRemoteProcessMemLeaks(filename) \
TestRemoteProcessMemLeaksFunc(filename, __FILE__, __LINE__)
-inline void TestRemoteProcessMemLeaksFunc(const char *filename,
- const char* file, int line)
-{
-#ifdef BOX_MEMORY_LEAK_TESTING
- // Does the file exist?
- if(!TestFileExists(filename))
- {
- if (failures == 0)
- {
- first_fail_file = file;
- first_fail_line = line;
- }
- ++failures;
- printf("FAILURE: MemLeak report not available (file %s) "
- "at %s:%d\n", filename, file, line);
- }
- else
- {
- // Is it empty?
- if(TestGetFileSize(filename) > 0)
- {
- if (failures == 0)
- {
- first_fail_file = file;
- first_fail_line = line;
- }
- ++failures;
- printf("FAILURE: Memory leaks found in other process "
- "(file %s) at %s:%d\n==========\n",
- filename, file, line);
- FILE *f = fopen(filename, "r");
- char linebuf[512];
- while(::fgets(linebuf, sizeof(linebuf), f) != 0)
- {
- printf("%s", linebuf);
- }
- fclose(f);
- printf("==========\n");
- }
-
- // Delete it
- ::unlink(filename);
- }
-#endif
-}
-
-inline void force_sync()
-{
- TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
- "force-sync") == 0);
- TestRemoteProcessMemLeaks("bbackupctl.memleaks");
-}
-
-inline void wait_for_sync_start()
-{
- TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
- "wait-for-sync") == 0);
- TestRemoteProcessMemLeaks("bbackupctl.memleaks");
-}
-
-inline void wait_for_sync_end()
-{
- TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
- "wait-for-end") == 0);
- TestRemoteProcessMemLeaks("bbackupctl.memleaks");
-}
-
-inline void sync_and_wait()
-{
- TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
- "sync-and-wait") == 0);
- TestRemoteProcessMemLeaks("bbackupctl.memleaks");
-}
-
-inline void terminate_bbackupd(int pid)
-{
- TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
- "terminate") == 0);
- TestRemoteProcessMemLeaks("bbackupctl.memleaks");
-
- for (int i = 0; i < 20; i++)
- {
- if (!ServerIsAlive(pid)) break;
- fprintf(stdout, ".");
- fflush(stdout);
- sleep(1);
- }
-
- TEST_THAT(!ServerIsAlive(pid));
- TestRemoteProcessMemLeaks("bbackupd.memleaks");
-}
+void TestRemoteProcessMemLeaksFunc(const char *filename,
+ const char* file, int line);
+void force_sync();
+void wait_for_sync_start();
+void wait_for_sync_end();
+void sync_and_wait();
+void terminate_bbackupd(int pid);
// Wait a given number of seconds for something to complete
-inline void wait_for_operation(int seconds)
-{
- printf("Waiting: ");
- fflush(stdout);
- for(int l = 0; l < seconds; ++l)
- {
- sleep(1);
- printf(".");
- fflush(stdout);
- }
- printf(" done.\n");
- fflush(stdout);
-}
-
-inline void safe_sleep(int seconds)
-{
-#ifdef WIN32
- Sleep(seconds * 1000);
-#else
- struct timespec ts;
- memset(&ts, 0, sizeof(ts));
- ts.tv_sec = seconds;
- ts.tv_nsec = 0;
- BOX_TRACE("sleeping for " << seconds << " seconds");
- while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
- {
- BOX_TRACE("safe_sleep interrupted with " <<
- ts.tv_sec << "." << ts.tv_nsec <<
- " secs remaining, sleeping again");
- /* sleep again */
- }
-#endif
-}
+void wait_for_operation(int seconds);
+void safe_sleep(int seconds);
#endif // TEST__H
diff --git a/lib/common/Timer.cpp b/lib/common/Timer.cpp
index 16133ecc..137ad45f 100644
--- a/lib/common/Timer.cpp
+++ b/lib/common/Timer.cpp
@@ -8,9 +8,14 @@
//
// --------------------------------------------------------------------------
+#ifdef WIN32
+ #define _WIN32_WINNT 0x0500
+#endif
+
#include "Box.h"
#include <signal.h>
+#include <cstring>
#include "Timer.h"
#include "Logging.h"
@@ -20,6 +25,9 @@
std::vector<Timer*>* Timers::spTimers = NULL;
bool Timers::sRescheduleNeeded = false;
+#define TIMER_ID "timer " << mName << " (" << this << ") "
+#define TIMER_ID_OF(t) "timer " << (t).GetName() << " (" << &(t) << ") "
+
typedef void (*sighandler_t)(int);
// --------------------------------------------------------------------------
@@ -35,9 +43,7 @@ void Timers::Init()
ASSERT(!spTimers);
#if defined WIN32 && ! defined PLATFORM_CYGWIN
- // no support for signals at all
- InitTimer();
- SetTimerHandler(Timers::SignalHandler);
+ // no init needed
#else
struct sigaction newact, oldact;
newact.sa_handler = Timers::SignalHandler;
@@ -72,9 +78,7 @@ void Timers::Cleanup()
}
#if defined WIN32 && ! defined PLATFORM_CYGWIN
- // no support for signals at all
- FiniTimer();
- SetTimerHandler(NULL);
+ // no cleanup needed
#else
struct itimerval timeout;
memset(&timeout, 0, sizeof(timeout));
@@ -149,9 +153,22 @@ void Timers::Remove(Timer& rTimer)
Reschedule();
}
+void Timers::RequestReschedule()
+{
+ sRescheduleNeeded = true;
+}
+
+void Timers::RescheduleIfNeeded()
+{
+ if (sRescheduleNeeded)
+ {
+ Reschedule();
+ }
+}
+
#define FORMAT_MICROSECONDS(t) \
(int)(t / 1000000) << "." << \
- (int)(t % 1000000)
+ (int)(t % 1000000) << " seconds"
// --------------------------------------------------------------------------
//
@@ -195,6 +212,9 @@ void Timers::Reschedule()
// we will do it anyway.
sRescheduleNeeded = false;
+#ifdef WIN32
+ // win32 timers need no management
+#else
box_time_t timeNow = GetCurrentBoxTime();
// scan for, trigger and remove expired timers. Removal requires
@@ -212,8 +232,14 @@ void Timers::Reschedule()
if (timeToExpiry <= 0)
{
+ /*
BOX_TRACE("timer " << *i << " has expired, "
"triggering it");
+ */
+ BOX_TRACE(TIMER_ID_OF(**i) "has expired, "
+ "triggering " <<
+ FORMAT_MICROSECONDS(-timeToExpiry) <<
+ " late");
rTimer.OnExpire();
spTimers->erase(i);
restart = true;
@@ -221,10 +247,12 @@ void Timers::Reschedule()
}
else
{
+ /*
BOX_TRACE("timer " << *i << " has not "
"expired, triggering in " <<
FORMAT_MICROSECONDS(timeToExpiry) <<
" seconds");
+ */
}
}
}
@@ -233,6 +261,7 @@ void Timers::Reschedule()
// Scan to find the next one to fire (earliest deadline).
int64_t timeToNextEvent = 0;
+ std::string nameOfNextEvent;
for (std::vector<Timer*>::iterator i = spTimers->begin();
i != spTimers->end(); i++)
@@ -240,6 +269,7 @@ void Timers::Reschedule()
Timer& rTimer = **i;
int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow;
+ ASSERT(timeToExpiry > 0)
if (timeToExpiry <= 0)
{
timeToExpiry = 1;
@@ -248,23 +278,36 @@ void Timers::Reschedule()
if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry)
{
timeToNextEvent = timeToExpiry;
+ nameOfNextEvent = rTimer.GetName();
}
}
ASSERT(timeToNextEvent >= 0);
-
+
+ if (timeToNextEvent == 0)
+ {
+ BOX_TRACE("timer: no more events, going to sleep.");
+ }
+ else
+ {
+ BOX_TRACE("timer: next event: " << nameOfNextEvent <<
+ " expires in " << FORMAT_MICROSECONDS(timeToNextEvent));
+ }
+
struct itimerval timeout;
memset(&timeout, 0, sizeof(timeout));
timeout.it_value.tv_sec = BoxTimeToSeconds(timeToNextEvent);
timeout.it_value.tv_usec = (int)
- (BoxTimeToMicroSeconds(timeToNextEvent) % MICRO_SEC_IN_SEC);
+ (BoxTimeToMicroSeconds(timeToNextEvent)
+ % MICRO_SEC_IN_SEC);
if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0)
{
- BOX_ERROR("Failed to initialise timer\n");
+ BOX_ERROR("Failed to initialise system timer\n");
THROW_EXCEPTION(CommonException, Internal)
}
+#endif
}
// --------------------------------------------------------------------------
@@ -279,27 +322,42 @@ void Timers::Reschedule()
// Created: 5/11/2006
//
// --------------------------------------------------------------------------
-void Timers::SignalHandler(int iUnused)
+void Timers::SignalHandler(int unused)
{
// ASSERT(spTimers);
Timers::RequestReschedule();
}
-Timer::Timer(size_t timeoutSecs)
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::Timer(size_t timeoutSecs,
+// const std::string& rName)
+// Purpose: Standard timer constructor, takes a timeout in
+// seconds from now, and an optional name for
+// logging purposes.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
+Timer::Timer(size_t timeoutSecs, const std::string& rName)
: mExpires(GetCurrentBoxTime() + SecondsToBoxTime(timeoutSecs)),
- mExpired(false)
+ mExpired(false),
+ mName(rName)
+#ifdef WIN32
+, mTimerHandle(INVALID_HANDLE_VALUE)
+#endif
{
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
if (timeoutSecs == 0)
{
- BOX_TRACE("timer " << this << " initialised for " <<
- timeoutSecs << " secs, will not fire");
+ BOX_TRACE(TIMER_ID "initialised for " << timeoutSecs <<
+ " secs, will not fire");
}
else
{
- BOX_TRACE("timer " << this << " initialised for " <<
- timeoutSecs << " secs, to fire at " <<
- FORMAT_MICROSECONDS(mExpires));
+ BOX_TRACE(TIMER_ID "initialised for " << timeoutSecs <<
+ " secs, to fire at " << FormatTime(mExpires, false, true));
}
#endif
@@ -310,37 +368,157 @@ Timer::Timer(size_t timeoutSecs)
else
{
Timers::Add(*this);
+ Start(timeoutSecs * MICRO_SEC_IN_SEC_LL);
}
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::Start()
+// Purpose: This internal function initialises an OS TimerQueue
+// timer on Windows, while on Unixes there is only a
+// single global timer, managed by the Timers class,
+// so this method does nothing.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
+void Timer::Start()
+{
+#ifdef WIN32
+ box_time_t timeNow = GetCurrentBoxTime();
+ int64_t timeToExpiry = mExpires - timeNow;
+
+ if (timeToExpiry <= 0)
+ {
+ BOX_WARNING(TIMER_ID << "fudging expiry from -" <<
+ FORMAT_MICROSECONDS(-timeToExpiry))
+ timeToExpiry = 1;
+ }
+
+ Start(timeToExpiry);
+#endif
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::Start(int64_t delayInMicros)
+// Purpose: This internal function initialises an OS TimerQueue
+// timer on Windows, with a specified delay already
+// calculated to save us doing it again. Like
+// Timer::Start(), on Unixes it does nothing.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
+void Timer::Start(int64_t delayInMicros)
+{
+#ifdef WIN32
+ // only call me once!
+ ASSERT(mTimerHandle == INVALID_HANDLE_VALUE);
+
+ int64_t delayInMillis = delayInMicros / 1000;
+
+ // Windows XP always seems to fire timers up to 20 ms late,
+ // at least on my test laptop. Not critical in practice, but our
+ // tests are precise enough that they will fail if we don't
+ // correct for it.
+ delayInMillis -= 20;
+
+ // Set a system timer to call our timer routine
+ if (CreateTimerQueueTimer(&mTimerHandle, NULL, TimerRoutine,
+ (PVOID)this, delayInMillis, 0, WT_EXECUTEINTIMERTHREAD)
+ == FALSE)
+ {
+ BOX_ERROR(TIMER_ID "failed to create timer: " <<
+ GetErrorMessage(GetLastError()));
+ mTimerHandle = INVALID_HANDLE_VALUE;
+ }
+#endif
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::Stop()
+// Purpose: This internal function deletes the associated OS
+// TimerQueue timer on Windows, and on Unixes does
+// nothing.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
+void Timer::Stop()
+{
+#ifdef WIN32
+ if (mTimerHandle != INVALID_HANDLE_VALUE)
+ {
+ if (DeleteTimerQueueTimer(NULL, mTimerHandle,
+ INVALID_HANDLE_VALUE) == FALSE)
+ {
+ BOX_ERROR(TIMER_ID "failed to delete timer: " <<
+ GetErrorMessage(GetLastError()));
+ }
+ mTimerHandle = INVALID_HANDLE_VALUE;
+ }
+#endif
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::~Timer()
+// Purpose: Destructor for Timer objects.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
Timer::~Timer()
{
- #ifndef NDEBUG
- BOX_TRACE("timer " << this << " destroyed");
+ #ifndef BOX_RELEASE_BUILD
+ BOX_TRACE(TIMER_ID "destroyed");
#endif
Timers::Remove(*this);
+ Stop();
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::Timer(Timer& rToCopy)
+// Purpose: Copy constructor for Timer objects. Creates a new
+// timer that will trigger at the same time as the
+// original. The original will usually be discarded.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
Timer::Timer(const Timer& rToCopy)
: mExpires(rToCopy.mExpires),
- mExpired(rToCopy.mExpired)
+ mExpired(rToCopy.mExpired),
+ mName(rToCopy.mName)
+#ifdef WIN32
+, mTimerHandle(INVALID_HANDLE_VALUE)
+#endif
{
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
if (mExpired)
{
- BOX_TRACE("timer " << this << " initialised from timer " <<
- &rToCopy << ", already expired, will not fire");
+ BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", "
+ "already expired, will not fire");
}
else if (mExpires == 0)
{
- BOX_TRACE("timer " << this << " initialised from timer " <<
- &rToCopy << ", no expiry, will not fire");
+ BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", "
+ "no expiry, will not fire");
}
else
{
- BOX_TRACE("timer " << this << " initialised from timer " <<
- &rToCopy << " to fire at " <<
+ BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", "
+ "to fire at " <<
(int)(mExpires / 1000000) << "." <<
(int)(mExpires % 1000000));
}
@@ -349,46 +527,100 @@ Timer::Timer(const Timer& rToCopy)
if (!mExpired && mExpires != 0)
{
Timers::Add(*this);
+ Start();
}
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::operator=(const Timer& rToCopy)
+// Purpose: Assignment operator for Timer objects. Works
+// exactly the same as the copy constructor, except
+// that if the receiving timer is already running,
+// it is stopped first.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
Timer& Timer::operator=(const Timer& rToCopy)
{
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
if (rToCopy.mExpired)
{
- BOX_TRACE("timer " << this << " initialised from timer " <<
- &rToCopy << ", already expired, will not fire");
+ BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", "
+ "already expired, will not fire");
}
else if (rToCopy.mExpires == 0)
{
- BOX_TRACE("timer " << this << " initialised from timer " <<
- &rToCopy << ", no expiry, will not fire");
+ BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", "
+ "no expiry, will not fire");
}
else
{
- BOX_TRACE("timer " << this << " initialised from timer " <<
- &rToCopy << " to fire at " <<
+ BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", "
+ "to fire at " <<
(int)(rToCopy.mExpires / 1000000) << "." <<
(int)(rToCopy.mExpires % 1000000));
}
#endif
Timers::Remove(*this);
+ Stop();
+
mExpires = rToCopy.mExpires;
mExpired = rToCopy.mExpired;
+ mName = rToCopy.mName;
+
if (!mExpired && mExpires != 0)
{
Timers::Add(*this);
+ Start();
}
+
return *this;
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::OnExpire()
+// Purpose: Method called by Timers::Reschedule (on Unixes)
+// on next poll after timer expires, or from
+// Timer::TimerRoutine (on Windows) from a separate
+// thread managed by the OS. Marks the timer as
+// expired for future reference.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
void Timer::OnExpire()
{
- #ifndef NDEBUG
- BOX_TRACE("timer " << this << " fired");
+ #ifndef BOX_RELEASE_BUILD
+ BOX_TRACE(TIMER_ID "fired");
#endif
mExpired = true;
}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Timer::TimerRoutine(PVOID lpParam,
+// BOOLEAN TimerOrWaitFired)
+// Purpose: Static method called by the Windows OS when a
+// TimerQueue timer expires.
+// Created: 27/07/2008
+//
+// --------------------------------------------------------------------------
+
+#ifdef WIN32
+VOID CALLBACK Timer::TimerRoutine(PVOID lpParam,
+ BOOLEAN TimerOrWaitFired)
+{
+ Timer* pTimer = (Timer*)lpParam;
+ pTimer->OnExpire();
+ // is it safe to write to write debug output from a timer?
+ // e.g. to write to the Event Log?
+}
+#endif
diff --git a/lib/common/Timer.h b/lib/common/Timer.h
index ba6d71f4..42b2e00f 100644
--- a/lib/common/Timer.h
+++ b/lib/common/Timer.h
@@ -40,35 +40,24 @@ class Timers
static bool sRescheduleNeeded;
static void SignalHandler(int iUnused);
-
+
public:
static void Init();
static void Cleanup();
static void Add (Timer& rTimer);
static void Remove(Timer& rTimer);
- static void RequestReschedule()
- {
- sRescheduleNeeded = true;
- }
-
- static void RescheduleIfNeeded()
- {
- if (sRescheduleNeeded)
- {
- Reschedule();
- }
- }
+ static void RequestReschedule();
+ static void RescheduleIfNeeded();
};
class Timer
{
public:
- Timer(size_t timeoutSecs);
+ Timer(size_t timeoutSecs, const std::string& rName = "");
virtual ~Timer();
Timer(const Timer &);
Timer &operator=(const Timer &);
-public:
box_time_t GetExpiryTime() { return mExpires; }
virtual void OnExpire();
bool HasExpired()
@@ -76,10 +65,23 @@ public:
Timers::RescheduleIfNeeded();
return mExpired;
}
+
+ const std::string& GetName() const { return mName; }
private:
- box_time_t mExpires;
- bool mExpired;
+ box_time_t mExpires;
+ bool mExpired;
+ std::string mName;
+
+ void Start();
+ void Start(int64_t delayInMicros);
+ void Stop();
+
+ #ifdef WIN32
+ HANDLE mTimerHandle;
+ static VOID CALLBACK TimerRoutine(PVOID lpParam,
+ BOOLEAN TimerOrWaitFired);
+ #endif
};
#include "MemLeakFindOff.h"
diff --git a/lib/common/Utils.cpp b/lib/common/Utils.cpp
index 83a12ccf..f45ed26b 100644
--- a/lib/common/Utils.cpp
+++ b/lib/common/Utils.cpp
@@ -13,11 +13,17 @@
#include <sys/stat.h>
#include <errno.h>
+#include <cstdlib>
+
#ifdef SHOW_BACKTRACE_ON_EXCEPTION
#include <execinfo.h>
#include <stdlib.h>
#endif
+#ifdef HAVE_CXXABI_H
+ #include <cxxabi.h>
+#endif
+
#include "Utils.h"
#include "CommonException.h"
#include "Logging.h"
@@ -52,11 +58,11 @@ void SplitString(const std::string &String, char SplitOn, std::vector<std::strin
{
rOutput.push_back(String.substr(b));
}
-/*#ifndef NDEBUG
- TRACE2("Splitting string '%s' on %c\n", String.c_str(), SplitOn);
+/*#ifndef BOX_RELEASE_BUILD
+ BOX_TRACE("Splitting string '" << String << " on " << (char)SplitOn);
for(unsigned int l = 0; l < rOutput.size(); ++l)
{
- TRACE2("%d = '%s'\n", l, rOutput[l].c_str());
+ BOX_TRACE(l << " = '" << rOutput[l] << "'");
}
#endif*/
}
@@ -64,23 +70,83 @@ void SplitString(const std::string &String, char SplitOn, std::vector<std::strin
#ifdef SHOW_BACKTRACE_ON_EXCEPTION
void DumpStackBacktrace()
{
- void *array[10];
- size_t size;
- char **strings;
- size_t i;
-
- size = backtrace (array, 10);
- strings = backtrace_symbols (array, size);
+ void *array[10];
+ size_t size = backtrace (array, 10);
+ char **strings = backtrace_symbols (array, size);
BOX_TRACE("Obtained " << size << " stack frames.");
- for(i = 0; i < size; i++)
+ for(size_t i = 0; i < size; i++)
{
- BOX_TRACE(strings[i]);
+ // Demangling code copied from
+ // cctbx_sources/boost_adaptbx/meta_ext.cpp, BSD license
+
+ std::string mangled_frame = strings[i];
+ std::string output_frame = strings[i]; // default
+
+ #ifdef HAVE_CXXABI_H
+ int start = mangled_frame.find('(');
+ int end = mangled_frame.find('+', start);
+ std::string mangled_func = mangled_frame.substr(start + 1,
+ end - start - 1);
+
+ int status;
+
+#include "MemLeakFindOff.h"
+ char* result = abi::__cxa_demangle(mangled_func.c_str(),
+ NULL, NULL, &status);
+#include "MemLeakFindOn.h"
+
+ if (result == NULL)
+ {
+ if (status == 0)
+ {
+ BOX_WARNING("Demangle failed but no error: " <<
+ mangled_func);
+ }
+ else if (status == -1)
+ {
+ BOX_WARNING("Demangle failed with "
+ "memory allocation error: " <<
+ mangled_func);
+ }
+ else if (status == -2)
+ {
+ // Probably non-C++ name, don't demangle
+ /*
+ BOX_WARNING("Demangle failed with "
+ "with invalid name: " <<
+ mangled_func);
+ */
+ }
+ else if (status == -3)
+ {
+ BOX_WARNING("Demangle failed with "
+ "with invalid argument: " <<
+ mangled_func);
+ }
+ else
+ {
+ BOX_WARNING("Demangle failed with "
+ "with unknown error " << status <<
+ ": " << mangled_func);
+ }
+ }
+ else
+ {
+ output_frame = mangled_frame.substr(0, start + 1) +
+ result + mangled_frame.substr(end);
+#include "MemLeakFindOff.h"
+ std::free(result);
+#include "MemLeakFindOn.h"
+ }
+ #endif // HAVE_CXXABI_H
+
+ BOX_TRACE("Stack frame " << i << ": " << output_frame);
}
#include "MemLeakFindOff.h"
- free (strings);
+ std::free (strings);
#include "MemLeakFindOn.h"
}
#endif
@@ -97,8 +163,8 @@ void DumpStackBacktrace()
// --------------------------------------------------------------------------
bool FileExists(const char *Filename, int64_t *pFileSize, bool TreatLinksAsNotExisting)
{
- struct stat st;
- if(::lstat(Filename, &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(Filename, &st) != 0)
{
if(errno == ENOENT)
{
@@ -142,8 +208,8 @@ bool FileExists(const char *Filename, int64_t *pFileSize, bool TreatLinksAsNotEx
// --------------------------------------------------------------------------
int ObjectExists(const std::string& rFilename)
{
- struct stat st;
- if(::stat(rFilename.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(rFilename.c_str(), &st) != 0)
{
if(errno == ENOENT)
{
@@ -159,6 +225,85 @@ int ObjectExists(const std::string& rFilename)
return ((st.st_mode & S_IFDIR) == 0)?ObjectExists_File:ObjectExists_Dir;
}
+std::string HumanReadableSize(int64_t Bytes)
+{
+ double readableValue = Bytes;
+ std::string units = " B";
+
+ if (readableValue > 1024)
+ {
+ readableValue /= 1024;
+ units = "kB";
+ }
+
+ if (readableValue > 1024)
+ {
+ readableValue /= 1024;
+ units = "MB";
+ }
+
+ if (readableValue > 1024)
+ {
+ readableValue /= 1024;
+ units = "GB";
+ }
+
+ std::ostringstream result;
+ result << std::fixed << std::setprecision(2) << readableValue <<
+ " " << units;
+ return result.str();
+}
+
+std::string FormatUsageBar(int64_t Blocks, int64_t Bytes, int64_t Max,
+ bool MachineReadable)
+{
+ std::ostringstream result;
+
+
+ if (MachineReadable)
+ {
+ result << (Bytes >> 10) << " kB, " <<
+ std::setprecision(0) << ((Bytes*100)/Max) << "%";
+ }
+ else
+ {
+ // Bar graph
+ char bar[17];
+ unsigned int b = (int)((Bytes * (sizeof(bar)-1)) / Max);
+ if(b > sizeof(bar)-1) {b = sizeof(bar)-1;}
+ for(unsigned int l = 0; l < b; l++)
+ {
+ bar[l] = '*';
+ }
+ for(unsigned int l = b; l < sizeof(bar) - 1; l++)
+ {
+ bar[l] = ' ';
+ }
+ bar[sizeof(bar)-1] = '\0';
+
+ result << std::fixed <<
+ std::setw(10) << Blocks << " blocks, " <<
+ std::setw(10) << HumanReadableSize(Bytes) << ", " <<
+ std::setw(3) << std::setprecision(0) <<
+ ((Bytes*100)/Max) << "% |" << bar << "|";
+ }
+
+ return result.str();
+}
+std::string FormatUsageLineStart(const std::string& rName,
+ bool MachineReadable)
+{
+ std::ostringstream result;
+ if (MachineReadable)
+ {
+ result << rName << ": ";
+ }
+ else
+ {
+ result << std::setw(20) << std::right << rName << ": ";
+ }
+ return result.str();
+}
diff --git a/lib/common/Utils.h b/lib/common/Utils.h
index d0842b51..d6792077 100644
--- a/lib/common/Utils.h
+++ b/lib/common/Utils.h
@@ -30,6 +30,11 @@ enum
ObjectExists_Dir = 2
};
int ObjectExists(const std::string& rFilename);
+std::string HumanReadableSize(int64_t Bytes);
+std::string FormatUsageBar(int64_t Blocks, int64_t Bytes, int64_t Max,
+ bool MachineReadable);
+std::string FormatUsageLineStart(const std::string& rName,
+ bool MachineReadable);
#include "MemLeakFindOff.h"
diff --git a/lib/common/WaitForEvent.h b/lib/common/WaitForEvent.h
index 52a073e9..045d6d67 100644
--- a/lib/common/WaitForEvent.h
+++ b/lib/common/WaitForEvent.h
@@ -10,6 +10,8 @@
#ifndef WAITFOREVENT__H
#define WAITFOREVENT__H
+#include <cstdlib>
+
#ifdef HAVE_KQUEUE
#include <sys/event.h>
#include <sys/time.h>
diff --git a/lib/common/makeexception.pl.in b/lib/common/makeexception.pl.in
index 1564b75b..76b9b02b 100755
--- a/lib/common/makeexception.pl.in
+++ b/lib/common/makeexception.pl.in
@@ -71,13 +71,14 @@ print H <<__E;
class ${class}Exception : public BoxException
{
public:
- ${class}Exception(unsigned int SubType)
- : mSubType(SubType)
+ ${class}Exception(unsigned int SubType,
+ const std::string& rMessage = "")
+ : mSubType(SubType), mMessage(rMessage)
{
}
${class}Exception(const ${class}Exception &rToCopy)
- : mSubType(rToCopy.mSubType)
+ : mSubType(rToCopy.mSubType), mMessage(rToCopy.mMessage)
{
}
@@ -108,9 +109,14 @@ print H <<__E;
virtual unsigned int GetType() const throw();
virtual unsigned int GetSubType() const throw();
virtual const char *what() const throw();
+ virtual const std::string& GetMessage() const
+ {
+ return mMessage;
+ }
private:
unsigned int mSubType;
+ std::string mMessage;
};
#endif // $guardname