diff options
author | Reinhard Tartler <siretart@tauware.de> | 2009-04-02 13:58:11 +0200 |
---|---|---|
committer | Reinhard Tartler <siretart@tauware.de> | 2009-04-02 13:58:11 +0200 |
commit | a84d45498bd861c9225080232948a99c2e317bb8 (patch) | |
tree | 8f1f5fb7bf7ffbf6f24cf4a4fd6888a235dbcc08 /lib/common | |
parent | 25db897553a0db0f912602b375029e724f51556e (diff) |
Import upstream version 0.11~rc3~r2491
Diffstat (limited to 'lib/common')
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 |