diff options
Diffstat (limited to 'lib/common')
56 files changed, 1741 insertions, 868 deletions
diff --git a/lib/common/Archive.h b/lib/common/Archive.h index 6d5ce88b..2b27b303 100644 --- a/lib/common/Archive.h +++ b/lib/common/Archive.h @@ -26,7 +26,7 @@ class Archive { public: Archive(IOStream &Stream, int Timeout) - : mrStream(Stream) + : mrStream(Stream) { mTimeout = Timeout; } @@ -38,6 +38,7 @@ public: ~Archive() { } + // // // @@ -46,21 +47,29 @@ public: Write((int) Item); } void WriteExact(uint32_t Item) { Write((int)Item); } + // TODO FIXME: use of "int" here is dangerous and deprecated. It can lead to + // incompatible serialisation on non-32-bit machines. Passing anything other + // than one of the specifically supported fixed size types should be forbidden. void Write(int Item) { int32_t privItem = htonl(Item); - mrStream.Write(&privItem, sizeof(privItem)); + mrStream.Write(&privItem, sizeof(privItem), mTimeout); } void Write(int64_t Item) { int64_t privItem = box_hton64(Item); - mrStream.Write(&privItem, sizeof(privItem)); + mrStream.Write(&privItem, sizeof(privItem), mTimeout); + } + void WriteInt16(uint16_t Item) + { + uint16_t privItem = htons(Item); + mrStream.Write(&privItem, sizeof(privItem), mTimeout); } void WriteExact(uint64_t Item) { Write(Item); } void Write(uint64_t Item) { uint64_t privItem = box_hton64(Item); - mrStream.Write(&privItem, sizeof(privItem)); + mrStream.Write(&privItem, sizeof(privItem), mTimeout); } void Write(uint8_t Item) { @@ -71,7 +80,7 @@ public: { int size = Item.size(); Write(size); - mrStream.Write(Item.c_str(), size); + mrStream.Write(Item.c_str(), size, mTimeout); } // // @@ -100,17 +109,29 @@ public: void Read(int &rItemOut) { int32_t privItem; - if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */)) + if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), + 0 /* not interested in bytes read if this fails */, + mTimeout)) { - THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead); } rItemOut = ntohl(privItem); } + void ReadFullBuffer(void* Buffer, size_t Size) + { + if(!mrStream.ReadFullBuffer(Buffer, Size, + 0 /* not interested in bytes read if this fails */, + mTimeout)) + { + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead); + } + } void ReadIfPresent(int &rItemOut, int ValueIfNotPresent) { int32_t privItem; int bytesRead; - if(mrStream.ReadFullBuffer(&privItem, sizeof(privItem), &bytesRead)) + if(mrStream.ReadFullBuffer(&privItem, sizeof(privItem), + &bytesRead, mTimeout)) { rItemOut = ntohl(privItem); } @@ -122,48 +143,70 @@ public: else { // bad number of remaining bytes - THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead); } } void Read(int64_t &rItemOut) { int64_t privItem; - if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */)) - { - THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) - } + ReadFullBuffer(&privItem, sizeof(privItem)); rItemOut = box_ntoh64(privItem); } void ReadExact(uint64_t &rItemOut) { Read(rItemOut); } void Read(uint64_t &rItemOut) { uint64_t privItem; - if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */)) - { - THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) - } + ReadFullBuffer(&privItem, sizeof(privItem)); rItemOut = box_ntoh64(privItem); } + void ReadInt16(uint16_t &rItemOut) + { + uint16_t privItem; + ReadFullBuffer(&privItem, sizeof(privItem)); + rItemOut = ntohs(privItem); + } void Read(uint8_t &rItemOut) { int privItem; Read(privItem); rItemOut = privItem; } + void ReadIfPresent(std::string &rItemOut, const std::string& ValueIfNotPresent) + { + ReadString(rItemOut, &ValueIfNotPresent); + } void Read(std::string &rItemOut) { + ReadString(rItemOut, NULL); + } +private: + void ReadString(std::string &rItemOut, const std::string* pValueIfNotPresent) + { int size; - Read(size); + int bytesRead; + if(!mrStream.ReadFullBuffer(&size, sizeof(size), &bytesRead, mTimeout)) + { + if(bytesRead == 0 && pValueIfNotPresent != NULL) + { + // item is simply not present + rItemOut = *pValueIfNotPresent; + return; + } + else + { + // bad number of remaining bytes + THROW_EXCEPTION(CommonException, + ArchiveBlockIncompleteRead) + } + } + size = ntohl(size); // Assume most strings are relatively small char buf[256]; if(size < (int) sizeof(buf)) { // Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us - if(!mrStream.ReadFullBuffer(buf, size, 0 /* not interested in bytes read if this fails */, mTimeout)) - { - THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) - } + ReadFullBuffer(buf, size); // assign to this string, storing the header and the extra payload rItemOut.assign(buf, size); } @@ -174,10 +217,7 @@ public: char *ppayload = dataB; // Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us - if(!mrStream.ReadFullBuffer(ppayload, size, 0 /* not interested in bytes read if this fails */, mTimeout)) - { - THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) - } + ReadFullBuffer(ppayload, size); // assign to this string, storing the header and the extra pPayload rItemOut.assign(ppayload, size); } diff --git a/lib/common/BannerText.h b/lib/common/BannerText.h index f0772c9c..9ca0c11c 100644 --- a/lib/common/BannerText.h +++ b/lib/common/BannerText.h @@ -16,7 +16,7 @@ #define BANNER_TEXT(UtilityName) \ "Box " UtilityName " v" BOX_VERSION ", (c) Ben Summers and " \ - "contributors 2003-2011" + "contributors 2003-2014" #endif // BANNERTEXT__H diff --git a/lib/common/Box.h b/lib/common/Box.h index 316f4364..8ce2a625 100644 --- a/lib/common/Box.h +++ b/lib/common/Box.h @@ -116,16 +116,8 @@ { \ if((!HideExceptionMessageGuard::ExceptionsHidden() \ && !HideSpecificExceptionGuard::IsHidden( \ - type::ExceptionType, type::subtype)) \ - || Logging::Guard::IsGuardingFrom(Log::EVERYTHING)) \ + type::ExceptionType, type::subtype))) \ { \ - std::auto_ptr<Logging::Guard> guard; \ - \ - if(Logging::Guard::IsGuardingFrom(Log::EVERYTHING)) \ - { \ - guard.reset(new Logging::Guard(Log::EVERYTHING)); \ - } \ - \ OPTIONAL_DO_BACKTRACE \ BOX_WARNING("Exception thrown: " \ #type "(" #subtype ") " \ @@ -140,16 +132,8 @@ _box_throw_line << message; \ if((!HideExceptionMessageGuard::ExceptionsHidden() \ && !HideSpecificExceptionGuard::IsHidden( \ - type::ExceptionType, type::subtype)) \ - || Logging::Guard::IsGuardingFrom(Log::EVERYTHING)) \ + type::ExceptionType, type::subtype))) \ { \ - std::auto_ptr<Logging::Guard> guard; \ - \ - if(Logging::Guard::IsGuardingFrom(Log::EVERYTHING)) \ - { \ - guard.reset(new Logging::Guard(Log::EVERYTHING)); \ - } \ - \ OPTIONAL_DO_BACKTRACE \ BOX_WARNING("Exception thrown: " \ #type "(" #subtype ") (" << \ diff --git a/lib/common/BoxConfig-MSVC.h b/lib/common/BoxConfig-MSVC.h index eeb25d2e..2ec2edd7 100644 --- a/lib/common/BoxConfig-MSVC.h +++ b/lib/common/BoxConfig-MSVC.h @@ -386,9 +386,6 @@ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ -/* Define to `int' if <sys/types.h> doesn't define. */ -#define gid_t int - /* Define to `int' if <sys/types.h> does not define. */ /* #undef mode_t */ @@ -400,6 +397,3 @@ /* Define to `unsigned' if <sys/types.h> does not define. */ /* #undef size_t */ - -/* Define to `int' if <sys/types.h> doesn't define. */ -#define uid_t int diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h index 53a967e8..f7c74bfc 100644 --- a/lib/common/BoxPlatform.h +++ b/lib/common/BoxPlatform.h @@ -21,11 +21,13 @@ #define PLATFORM_DEV_NULL "/dev/null" -#ifdef _MSC_VER -#include "BoxConfig-MSVC.h" -#define NEED_BOX_VERSION_H +#if defined BOX_CMAKE +# include "BoxConfig.cmake.h" +#elif defined _MSC_VER +# include "BoxConfig-MSVC.h" +# define NEED_BOX_VERSION_H #else -#include "BoxConfig.h" +# include "BoxConfig.h" #endif #ifdef WIN32 @@ -40,9 +42,12 @@ #endif #endif +#include "emu.h" + #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif + #ifdef HAVE_INTTYPES_H #include <inttypes.h> #else @@ -93,66 +98,19 @@ #endif // Handle differing xattr APIs -#ifdef HAVE_SYS_XATTR_H - #if !defined(HAVE_LLISTXATTR) && defined(HAVE_LISTXATTR) && HAVE_DECL_XATTR_NOFOLLOW - #define llistxattr(a,b,c) listxattr(a,b,c,XATTR_NOFOLLOW) - #endif - #if !defined(HAVE_LGETXATTR) && defined(HAVE_GETXATTR) && HAVE_DECL_XATTR_NOFOLLOW - #define lgetxattr(a,b,c,d) getxattr(a,b,c,d,0,XATTR_NOFOLLOW) - #endif - #if !defined(HAVE_LSETXATTR) && defined(HAVE_SETXATTR) && HAVE_DECL_XATTR_NOFOLLOW - #define lsetxattr(a,b,c,d,e) setxattr(a,b,c,d,0,(e)|XATTR_NOFOLLOW) - #endif +#if !defined(HAVE_LLISTXATTR) && defined(HAVE_LISTXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define llistxattr(a,b,c) listxattr(a,b,c,XATTR_NOFOLLOW) + #define HAVE_LLISTXATTR #endif -#if defined WIN32 && !defined __MINGW32__ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - - typedef unsigned __int8 u_int8_t; - typedef unsigned __int16 u_int16_t; - typedef unsigned __int32 u_int32_t; - typedef unsigned __int64 u_int64_t; - - #define HAVE_U_INT8_T - #define HAVE_U_INT16_T - #define HAVE_U_INT32_T - #define HAVE_U_INT64_T -#endif // WIN32 && !__MINGW32__ - -// Define missing types -#ifndef HAVE_UINT8_T - typedef u_int8_t uint8_t; +#if !defined(HAVE_LGETXATTR) && defined(HAVE_GETXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define lgetxattr(a,b,c,d) getxattr(a,b,c,d,0,XATTR_NOFOLLOW) + #define HAVE_LGETXATTR #endif -#ifndef HAVE_UINT16_T - typedef u_int16_t uint16_t; -#endif - -#ifndef HAVE_UINT32_T - typedef u_int32_t uint32_t; -#endif - -#ifndef HAVE_UINT64_T - typedef u_int64_t uint64_t; -#endif - -#ifndef HAVE_U_INT8_T - typedef uint8_t u_int8_t; -#endif - -#ifndef HAVE_U_INT16_T - typedef uint16_t u_int16_t; -#endif - -#ifndef HAVE_U_INT32_T - typedef uint32_t u_int32_t; -#endif - -#ifndef HAVE_U_INT64_T - typedef uint64_t u_int64_t; +#if !defined(HAVE_LSETXATTR) && defined(HAVE_SETXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define lsetxattr(a,b,c,d,e) setxattr(a,b,c,d,0,(e)|XATTR_NOFOLLOW) + #define HAVE_LSETXATTR #endif #if !HAVE_DECL_INFTIM @@ -173,7 +131,7 @@ #endif #ifdef WIN32 - typedef u_int64_t InodeRefType; + typedef uint64_t InodeRefType; #else typedef ino_t InodeRefType; #endif @@ -182,8 +140,6 @@ #define WIN32_LEAN_AND_MEAN #endif -#include "emu.h" - #ifdef WIN32 #define INVALID_FILE INVALID_HANDLE_VALUE typedef HANDLE tOSFileHandle; diff --git a/lib/common/BoxTime.cpp b/lib/common/BoxTime.cpp index f62b1c35..77daae6d 100644 --- a/lib/common/BoxTime.cpp +++ b/lib/common/BoxTime.cpp @@ -35,21 +35,30 @@ // -------------------------------------------------------------------------- box_time_t GetCurrentBoxTime() { - #ifdef HAVE_GETTIMEOFDAY - struct timeval tv; - if (gettimeofday(&tv, NULL) != 0) - { - BOX_LOG_SYS_ERROR("Failed to gettimeofday(), " - "dropping precision"); - } - else - { - box_time_t timeNow = (tv.tv_sec * MICRO_SEC_IN_SEC_LL) - + tv.tv_usec; - return timeNow; - } - #endif - +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + if (gettimeofday(&tv, NULL) != 0) + { + BOX_LOG_SYS_ERROR("Failed to gettimeofday(), " + "dropping precision"); + } + else + { + box_time_t time_now = (tv.tv_sec * MICRO_SEC_IN_SEC_LL) + tv.tv_usec; + return time_now; + } +#elif WIN32 + // There's no Win32 API function that returns the current time as a UNIX timestamp with + // sub-second precision. So we use time(0) and add the fractional part from + // GetSystemTime() in the hope that the difference between these two (if any) is a whole + // number of seconds. + box_time_t time_now = SecondsToBoxTime(time(0)); + SYSTEMTIME system_time; + GetSystemTime(&system_time); + time_now += MilliSecondsToBoxTime(system_time.wMilliseconds); + return time_now; +#endif + return SecondsToBoxTime(time(0)); } @@ -83,7 +92,7 @@ std::string FormatTime(box_time_t time, bool includeDate, bool showMicros) if (showMicros) { - buf << "." << std::setw(6) << micros; + buf << "." << std::setw(3) << (int)(micros / 1000); } } else @@ -108,8 +117,7 @@ void ShortSleep(box_time_t duration, bool logDuration) { if(logDuration) { - BOX_TRACE("Sleeping for " << BoxTimeToMicroSeconds(duration) << - " microseconds"); + BOX_TRACE("Sleeping for " << BOX_FORMAT_MICROSECONDS(duration)); } #ifdef WIN32 @@ -118,7 +126,9 @@ void ShortSleep(box_time_t duration, bool logDuration) struct timespec ts; memset(&ts, 0, sizeof(ts)); ts.tv_sec = duration / MICRO_SEC_IN_SEC; - ts.tv_nsec = duration % MICRO_SEC_IN_SEC; + ts.tv_nsec = (duration % MICRO_SEC_IN_SEC) * 1000; + + box_time_t start_time = GetCurrentBoxTime(); while (nanosleep(&ts, &ts) == -1 && errno == EINTR) { @@ -140,6 +150,10 @@ void ShortSleep(box_time_t duration, bool logDuration) BOX_TRACE("nanosleep interrupted with " << remain_ns << " nanosecs remaining, sleeping again"); } + + box_time_t sleep_time = GetCurrentBoxTime() - start_time; + BOX_TRACE("Actually slept for " << BOX_FORMAT_MICROSECONDS(sleep_time) << + ", was aiming for " << BOX_FORMAT_MICROSECONDS(duration)); #endif } diff --git a/lib/common/BoxTime.h b/lib/common/BoxTime.h index 3108d809..6afaada3 100644 --- a/lib/common/BoxTime.h +++ b/lib/common/BoxTime.h @@ -10,8 +10,8 @@ #ifndef BOXTIME__H #define BOXTIME__H -// Time is presented as an unsigned 64 bit integer, in microseconds -typedef int64_t box_time_t; +// Time is presented as a signed 64 bit integer, in microseconds +typedef int64_t box_time_t; #define NANO_SEC_IN_SEC (1000000000LL) #define NANO_SEC_IN_USEC (1000) diff --git a/lib/common/BufferedStream.cpp b/lib/common/BufferedStream.cpp index b58253f3..847cf66c 100644 --- a/lib/common/BufferedStream.cpp +++ b/lib/common/BufferedStream.cpp @@ -96,7 +96,7 @@ IOStream::pos_type BufferedStream::BytesLeftToRead() // Created: 2003/07/31 // // -------------------------------------------------------------------------- -void BufferedStream::Write(const void *pBuffer, int NBytes) +void BufferedStream::Write(const void *pBuffer, int NBytes, int Timeout) { THROW_EXCEPTION(CommonException, NotSupported); } @@ -189,7 +189,9 @@ void BufferedStream::Close() // -------------------------------------------------------------------------- bool BufferedStream::StreamDataLeft() { - return mrSource.StreamDataLeft(); + // Return true if either the source has data left to read, or we have + // buffered data still to be read. + return mrSource.StreamDataLeft() || (mBufferPosition < mBufferSize); } // -------------------------------------------------------------------------- diff --git a/lib/common/BufferedStream.h b/lib/common/BufferedStream.h index 079c482a..3984aceb 100644 --- a/lib/common/BufferedStream.h +++ b/lib/common/BufferedStream.h @@ -25,7 +25,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(IOStream::pos_type Offset, int SeekType); virtual void Close(); @@ -33,6 +34,10 @@ public: virtual bool StreamDataLeft(); virtual bool StreamClosed(); + virtual std::string ToString() const + { + return std::string("Buffered ") + mrSource.ToString(); + } private: BufferedStream(const BufferedStream &rToCopy) : mrSource(rToCopy.mrSource) { /* do not call */ } diff --git a/lib/common/BufferedWriteStream.cpp b/lib/common/BufferedWriteStream.cpp index 797be00d..8fbabe9b 100644 --- a/lib/common/BufferedWriteStream.cpp +++ b/lib/common/BufferedWriteStream.cpp @@ -64,7 +64,7 @@ IOStream::pos_type BufferedWriteStream::BytesLeftToRead() // Created: 2003/07/31 // // -------------------------------------------------------------------------- -void BufferedWriteStream::Write(const void *pBuffer, int NBytes) +void BufferedWriteStream::Write(const void *pBuffer, int NBytes, int Timeout) { int numBytesRemain = NBytes; diff --git a/lib/common/BufferedWriteStream.h b/lib/common/BufferedWriteStream.h index 7a1c8c17..5f6d5f19 100644 --- a/lib/common/BufferedWriteStream.h +++ b/lib/common/BufferedWriteStream.h @@ -25,7 +25,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(IOStream::pos_type Offset, int SeekType); virtual void Flush(int Timeout = IOStream::TimeOutInfinite); diff --git a/lib/common/CollectInBufferStream.cpp b/lib/common/CollectInBufferStream.cpp index 90e2e7bc..47b271f0 100644 --- a/lib/common/CollectInBufferStream.cpp +++ b/lib/common/CollectInBufferStream.cpp @@ -98,7 +98,7 @@ IOStream::pos_type CollectInBufferStream::BytesLeftToRead() // Created: 2003/08/26 // // -------------------------------------------------------------------------- -void CollectInBufferStream::Write(const void *pBuffer, int NBytes) +void CollectInBufferStream::Write(const void *pBuffer, int NBytes, int Timeout) { if(mInWritePhase != true) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } diff --git a/lib/common/CollectInBufferStream.h b/lib/common/CollectInBufferStream.h index d73af8db..297d2851 100644 --- a/lib/common/CollectInBufferStream.h +++ b/lib/common/CollectInBufferStream.h @@ -26,24 +26,31 @@ class CollectInBufferStream : public IOStream public: CollectInBufferStream(); ~CollectInBufferStream(); -private: - // No copying - CollectInBufferStream(const CollectInBufferStream &); - CollectInBufferStream(const IOStream &); -public: + + // Move constructor: + CollectInBufferStream(CollectInBufferStream& rOther) + : mBuffer(rOther.mBuffer.Release()), + mBufferSize(rOther.mBufferSize), + mBytesInBuffer(rOther.mBytesInBuffer), + mReadPosition(rOther.mReadPosition), + mInWritePhase(rOther.mInWritePhase) + { + rOther.Reset(); + } virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(pos_type Offset, int SeekType); virtual bool StreamDataLeft(); virtual bool StreamClosed(); void SetForReading(); - + void Reset(); - + void *GetBuffer() const; int GetSize() const; bool IsSetForReading() const {return !mInWritePhase;} diff --git a/lib/common/CommonException.txt b/lib/common/CommonException.txt index 05da2709..610ba1a8 100644 --- a/lib/common/CommonException.txt +++ b/lib/common/CommonException.txt @@ -55,3 +55,5 @@ DatabaseRecordAlreadyExists 47 The database already contains a record with this DatabaseRecordBadSize 48 The database contains a record with an invalid size DatabaseIterateFailed 49 Failed to iterate over the database keys ReferenceNotFound 50 The database does not contain an expected reference +TimersNotInitialised 51 The timer framework should have been ready at this point +InvalidConfiguration 52 Some required values are missing or incorrect in the configuration file. diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp index f49f3c6e..8ce8d389 100644 --- a/lib/common/Configuration.cpp +++ b/lib/common/Configuration.cpp @@ -34,6 +34,8 @@ inline bool iw(int c) static const char *sValueBooleanStrings[] = {"yes", "true", "no", "false", 0}; static const bool sValueBooleanValue[] = {true, true, false, false}; +const ConfigurationCategory ConfigurationVerify::VERIFY_ERROR("VerifyError"); + ConfigurationVerifyKey::ConfigurationVerifyKey ( std::string name, @@ -212,8 +214,8 @@ std::auto_ptr<Configuration> Configuration::LoadAndVerify( if(!rErrorMsg.empty()) { // An error occured, return now - BOX_ERROR("Error in Configuration::LoadInto: " << - rErrorMsg); + BOX_LOG_CATEGORY(Log::ERROR, ConfigurationVerify::VERIFY_ERROR, + "Error in Configuration::LoadInto: " << rErrorMsg); return std::auto_ptr<Configuration>(0); } @@ -222,8 +224,11 @@ std::auto_ptr<Configuration> Configuration::LoadAndVerify( { if(!apConfig->Verify(*pVerify, std::string(), rErrorMsg)) { - BOX_ERROR("Error verifying configuration: " << - rErrorMsg); + BOX_LOG_CATEGORY(Log::ERROR, + ConfigurationVerify::VERIFY_ERROR, + "Error verifying configuration: " << + rErrorMsg.substr(0, rErrorMsg.size() > 0 + ? rErrorMsg.size() - 1 : 0)); return std::auto_ptr<Configuration>(0); } } @@ -425,7 +430,8 @@ const std::string &Configuration::GetKeyValue(const std::string& rKeyName) const if(i == mKeys.end()) { - BOX_ERROR("Missing configuration key: " << rKeyName); + BOX_LOG_CATEGORY(Log::ERROR, ConfigurationVerify::VERIFY_ERROR, + "Missing configuration key: " << rKeyName); THROW_EXCEPTION(CommonException, ConfigNoKey) } else diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h index 4828b315..e6498e80 100644 --- a/lib/common/Configuration.h +++ b/lib/common/Configuration.h @@ -27,6 +27,14 @@ enum ConfigTest_IsBool = 32 }; +class ConfigurationCategory : public Log::Category +{ + public: + ConfigurationCategory(const std::string& name) + : Log::Category(std::string("Configuration/") + name) + { } +}; + class ConfigurationVerifyKey { public: @@ -75,6 +83,7 @@ public: const ConfigurationVerifyKey *mpKeys; int Tests; void *TestFunction; // set to zero for now, will implement later + static const ConfigurationCategory VERIFY_ERROR; }; class FdGetLine; diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp index 0b123675..58a82c0e 100644 --- a/lib/common/DebugMemLeakFinder.cpp +++ b/lib/common/DebugMemLeakFinder.cpp @@ -15,14 +15,19 @@ #undef realloc #undef free -#ifdef HAVE_UNISTD_H - #include <unistd.h> -#endif - +#include <limits.h> #include <signal.h> #include <stdio.h> #include <string.h> +#ifdef HAVE_PROCESS_H +# include <process.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + #include <cstdlib> // for std::atexit #include <map> #include <set> @@ -155,7 +160,17 @@ void *memleakfinder_malloc(size_t size, const char *file, int line) InternalAllocGuard guard; void *b = std::malloc(size); - if(!memleakfinder_global_enable) return b; + + if(!memleakfinder_global_enable) + { + // We may not be tracking this allocation, but if + // someone realloc()s the buffer later then it will + // trigger an untracked buffer warning, which we don't + // want to see either. + memleakfinder_notaleak(b); + return b; + } + if(!memleakfinder_initialised) return b; memleakfinder_malloc_add_block(b, size, file, line); @@ -176,25 +191,58 @@ void *memleakfinder_calloc(size_t blocks, size_t size, const char *file, int lin void *memleakfinder_realloc(void *ptr, size_t size) { + if(!ptr) + { + return memleakfinder_malloc(size, "realloc", 0); + } + + if(!size) + { + memleakfinder_free(ptr); + return NULL; + } + InternalAllocGuard guard; + ASSERT(ptr != NULL); + if(!ptr) return NULL; // defensive + if(!memleakfinder_global_enable || !memleakfinder_initialised) { - return std::realloc(ptr, size); + ptr = std::realloc(ptr, size); + if(!memleakfinder_global_enable) + { + // We may not be tracking this allocation, but if + // someone realloc()s the buffer later then it will + // trigger an untracked buffer warning, which we don't + // want to see either. + memleakfinder_notaleak(ptr); + } + return ptr; } // Check it's been allocated std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); - if(ptr && i == sMallocBlocks.end()) + std::set<void *>::iterator j(sNotLeaks.find(ptr)); + + if(i == sMallocBlocks.end() && j == sNotLeaks.end()) { BOX_WARNING("Block " << ptr << " realloc()ated, but not " "in list. Error? Or allocated in startup static " "objects?"); } + if(j != sNotLeaks.end()) + { + // It's in the list of not-leaks, so don't warn about it, + // but it's being reallocated, so remove it from the list too, + // in case it's reassigned, and add the new block below. + sNotLeaks.erase(j); + } + void *b = std::realloc(ptr, size); - if(ptr && i!=sMallocBlocks.end()) + if(i != sMallocBlocks.end()) { // Worked? if(b != 0) @@ -230,10 +278,19 @@ void memleakfinder_free(void *ptr) { // Check it's been allocated std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); + std::set<void *>::iterator j(sNotLeaks.find(ptr)); + if(i != sMallocBlocks.end()) { sMallocBlocks.erase(i); } + else if(j != sNotLeaks.end()) + { + // It's in the list of not-leaks, so don't warn + // about it, but it's being freed, so remove it + // from the list too, in case it's reassigned. + sNotLeaks.erase(j); + } else { BOX_WARNING("Block " << ptr << " freed, but not " @@ -284,7 +341,8 @@ void memleakfinder_notaleak(void *ptr) ASSERT(!sTrackingDataDestroyed); memleakfinder_notaleak_insert_pre(); - if(memleakfinder_global_enable && memleakfinder_initialised) + + if(memleakfinder_initialised) { sNotLeaks.insert(ptr); } @@ -294,7 +352,9 @@ void memleakfinder_notaleak(void *ptr) sizeof(sNotLeaksPre)/sizeof(*sNotLeaksPre) ) sNotLeaksPre[sNotLeaksPreNum++] = ptr; } -/* { + + /* + { std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); if(i != sMallocBlocks.end()) sMallocBlocks.erase(i); } @@ -531,9 +591,14 @@ extern "C" void memleakfinder_atexit() memleakfinder_reportleaks_appendfile(atexit_filename, atexit_markertext); } -void memleakfinder_setup_exit_report(const char *filename, const char *markertext) +void memleakfinder_setup_exit_report(const std::string& filename, + const char *markertext) { - ::strncpy(atexit_filename, filename, sizeof(atexit_filename)-1); + char buffer[PATH_MAX]; + std::string abs_filename = std::string(getcwd(buffer, sizeof(buffer))) + + DIRECTORY_SEPARATOR + filename; + ::strncpy(atexit_filename, abs_filename.c_str(), + 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; @@ -544,9 +609,6 @@ void memleakfinder_setup_exit_report(const char *filename, const char *markertex } } - - - void add_object_block(void *block, size_t size, const char *file, int line, bool array) { InternalAllocGuard guard; @@ -557,6 +619,10 @@ void add_object_block(void *block, size_t size, const char *file, int line, bool if(block != 0) { + std::map<void *, ObjectInfo>::iterator j(sObjectBlocks.find(block)); + // The same block should not already be tracked! + ASSERT(j == sObjectBlocks.end()); + ObjectInfo i; i.size = size; i.file = file; @@ -637,7 +703,7 @@ void *operator new(size_t size) } */ -void *operator new[](size_t size) +void *operator new[](size_t size) throw (std::bad_alloc) { return internal_new(size, "standard libraries", 0); } diff --git a/lib/common/EventWatchFilesystemObject.cpp b/lib/common/EventWatchFilesystemObject.cpp deleted file mode 100644 index 43533fc8..00000000 --- a/lib/common/EventWatchFilesystemObject.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -------------------------------------------------------------------------- -// -// File -// Name: EventWatchFilesystemObject.cpp -// Purpose: WaitForEvent compatible object for watching directories -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- - -#include "Box.h" - -#include <errno.h> -#include <fcntl.h> - -#ifdef HAVE_UNISTD_H - #include <unistd.h> -#endif - -#include "EventWatchFilesystemObject.h" -#include "autogen_CommonException.h" -#include "Logging.h" - -#include "MemLeakFindOn.h" - - -// -------------------------------------------------------------------------- -// -// Function -// Name: EventWatchFilesystemObject::EventWatchFilesystemObject -// (const char *) -// Purpose: Constructor -- opens the file object -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- -EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename) -#ifdef HAVE_KQUEUE - : mDescriptor(::open(Filename, O_RDONLY /*O_EVTONLY*/, 0)) -#endif -{ -#ifdef HAVE_KQUEUE - if(mDescriptor == -1) - { - BOX_LOG_SYS_ERROR("EventWatchFilesystemObject: " - "Failed to open file '" << Filename << "'"); - THROW_EXCEPTION(CommonException, OSFileOpenError) - } -#else - THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform) -#endif -} - - -// -------------------------------------------------------------------------- -// -// Function -// Name: EventWatchFilesystemObject::~EventWatchFilesystemObject() -// Purpose: Destructor -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- -EventWatchFilesystemObject::~EventWatchFilesystemObject() -{ - if(mDescriptor != -1) - { - ::close(mDescriptor); - } -} - - -// -------------------------------------------------------------------------- -// -// Function -// Name: EventWatchFilesystemObject::EventWatchFilesystemObject -// (const EventWatchFilesystemObject &) -// Purpose: Copy constructor -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- -EventWatchFilesystemObject::EventWatchFilesystemObject( - const EventWatchFilesystemObject &rToCopy) - : mDescriptor(::dup(rToCopy.mDescriptor)) -{ - if(mDescriptor == -1) - { - THROW_EXCEPTION(CommonException, OSFileError) - } -} - - -#ifdef HAVE_KQUEUE -// -------------------------------------------------------------------------- -// -// Function -// Name: EventWatchFilesystemObject::FillInKEvent(struct kevent &, int) -// Purpose: For WaitForEvent -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- -void EventWatchFilesystemObject::FillInKEvent(struct kevent &rEvent, - int Flags) const -{ - 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 -{ - THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform) -} -#endif - diff --git a/lib/common/EventWatchFilesystemObject.h b/lib/common/EventWatchFilesystemObject.h deleted file mode 100644 index f9175a49..00000000 --- a/lib/common/EventWatchFilesystemObject.h +++ /dev/null @@ -1,48 +0,0 @@ -// -------------------------------------------------------------------------- -// -// File -// Name: EventWatchFilesystemObject.h -// Purpose: WaitForEvent compatible object for watching directories -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- - -#ifndef EVENTWATCHFILESYSTEMOBJECT__H -#define EVENTWATCHFILESYSTEMOBJECT__H - -#ifdef HAVE_KQUEUE - #include <sys/event.h> -#endif - - -// -------------------------------------------------------------------------- -// -// Class -// Name: EventWatchFilesystemObject -// Purpose: WaitForEvent compatible object for watching files and directories -// Created: 12/3/04 -// -// -------------------------------------------------------------------------- -class EventWatchFilesystemObject -{ -public: - EventWatchFilesystemObject(const char *Filename); - ~EventWatchFilesystemObject(); - EventWatchFilesystemObject(const EventWatchFilesystemObject &rToCopy); -private: - // Assignment not allowed - EventWatchFilesystemObject &operator=(const EventWatchFilesystemObject &); -public: - -#ifdef HAVE_KQUEUE - void FillInKEvent(struct kevent &rEvent, int Flags = 0) const; -#else - void FillInPoll(int &fd, short &events, int Flags = 0) const; -#endif - -private: - int mDescriptor; -}; - -#endif // EventWatchFilesystemObject__H - diff --git a/lib/common/ExcludeList.cpp b/lib/common/ExcludeList.cpp index 213c4f8e..3f9f69ee 100644 --- a/lib/common/ExcludeList.cpp +++ b/lib/common/ExcludeList.cpp @@ -10,9 +10,9 @@ #include "Box.h" #ifdef HAVE_REGEX_SUPPORT - #ifdef HAVE_PCREPOSIX_H + #if defined HAVE_PCREPOSIX_H #include <pcreposix.h> - #else + #elif defined HAVE_REGEX_H #include <regex.h> #endif #define EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED @@ -139,9 +139,10 @@ void ExcludeList::AddDefiniteEntries(const std::string &rEntries) if (entry.size() > 0 && entry[entry.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR) { - BOX_WARNING("Exclude entry ends in path " - "separator, will never match: " - << entry); + BOX_LOG_CATEGORY(Log::WARNING, + ConfigurationVerify::VERIFY_ERROR, + "Exclude entry ends in path separator, " + "will never match: " << entry); } mDefinite.insert(entry); @@ -198,9 +199,9 @@ void ExcludeList::AddRegexEntries(const std::string &rEntries) { char buf[1024]; regerror(errcode, pregex, buf, sizeof(buf)); - BOX_ERROR("Invalid regular expression: " << + THROW_EXCEPTION_MESSAGE(CommonException, BadRegularExpression, + "Invalid regular expression: " << entry << ": " << buf); - THROW_EXCEPTION(CommonException, BadRegularExpression) } // Store in list of regular expressions diff --git a/lib/common/FileModificationTime.cpp b/lib/common/FileModificationTime.cpp index 06fc7887..50f1fb62 100644 --- a/lib/common/FileModificationTime.cpp +++ b/lib/common/FileModificationTime.cpp @@ -18,11 +18,14 @@ box_time_t FileModificationTime(const EMU_STRUCT_STAT &st) { -#ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC - box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL); -#else +#if defined HAVE_STRUCT_STAT_ST_ATIM + box_time_t datamodified = (((int64_t)st.st_mtim.tv_nsec) / NANO_SEC_IN_USEC_LL) + + (((int64_t)st.st_mtim.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC box_time_t datamodified = (((int64_t)st.st_mtimespec.tv_nsec) / NANO_SEC_IN_USEC_LL) + (((int64_t)st.st_mtimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#else + box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL); #endif return datamodified; @@ -31,7 +34,10 @@ box_time_t FileModificationTime(const EMU_STRUCT_STAT &st) box_time_t FileAttrModificationTime(const EMU_STRUCT_STAT &st) { box_time_t statusmodified = -#ifdef HAVE_STRUCT_STAT_ST_MTIMESPEC +#if defined HAVE_STRUCT_STAT_ST_ATIM + (((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_ATIMESPEC (((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 diff --git a/lib/common/FileStream.cpp b/lib/common/FileStream.cpp index fc0319da..51752f85 100644 --- a/lib/common/FileStream.cpp +++ b/lib/common/FileStream.cpp @@ -67,22 +67,29 @@ void FileStream::AfterOpen() { MEMLEAKFINDER_NOT_A_LEAK(this); - #ifdef WIN32 - BOX_LOG_WIN_WARNING_NUMBER("Failed to open file: " << - mFileName, winerrno); - #else - BOX_LOG_SYS_WARNING("Failed to open file: " << - mFileName); - #endif - +#ifdef WIN32 + if(errno == EACCES) + { + THROW_WIN_FILE_ERRNO("Failed to open file", mFileName, + winerrno, CommonException, AccessDenied); + } + else + { + THROW_WIN_FILE_ERRNO("Failed to open file", mFileName, + winerrno, CommonException, OSFileOpenError); + } +#else if(errno == EACCES) { - THROW_EXCEPTION(CommonException, AccessDenied) + THROW_SYS_FILE_ERROR("Failed to open file", mFileName, + CommonException, AccessDenied); } else { - THROW_EXCEPTION(CommonException, OSFileOpenError) + THROW_SYS_FILE_ERROR("Failed to open file", mFileName, + CommonException, OSFileOpenError); } +#endif } } @@ -244,9 +251,9 @@ IOStream::pos_type FileStream::BytesLeftToRead() // Created: 2003/07/31 // // -------------------------------------------------------------------------- -void FileStream::Write(const void *pBuffer, int NBytes) +void FileStream::Write(const void *pBuffer, int NBytes, int Timeout) { - if(mOSFileHandle == INVALID_FILE) + if(mOSFileHandle == INVALID_FILE) { THROW_EXCEPTION(CommonException, FileClosed) } diff --git a/lib/common/FileStream.h b/lib/common/FileStream.h index 9101a968..1426d8a2 100644 --- a/lib/common/FileStream.h +++ b/lib/common/FileStream.h @@ -23,7 +23,7 @@ class FileStream : public IOStream { public: - FileStream(const std::string& rFilename, + FileStream(const std::string& rFilename, int flags = (O_RDONLY | O_BINARY), int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); @@ -40,7 +40,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(IOStream::pos_type Offset, int SeekType); virtual void Close(); @@ -49,6 +50,11 @@ public: virtual bool StreamClosed(); bool CompareWith(IOStream& rOther, int Timeout = IOStream::TimeOutInfinite); + std::string ToString() const + { + return std::string("local file ") + mFileName; + } + const std::string GetFileName() const { return mFileName; } private: tOSFileHandle mOSFileHandle; diff --git a/lib/common/Guards.h b/lib/common/Guards.h index cd2e4628..46b6d2bd 100644 --- a/lib/common/Guards.h +++ b/lib/common/Guards.h @@ -37,9 +37,8 @@ public: { if(mOSFileHandle < 0) { - BOX_LOG_SYS_ERROR("FileHandleGuard: failed to open " - "file '" << rFilename << "'"); - THROW_EXCEPTION(CommonException, OSFileOpenError) + THROW_SYS_FILE_ERROR("Failed to open file", rFilename, + CommonException, OSFileOpenError); } } @@ -78,7 +77,17 @@ class MemoryBlockGuard { public: MemoryBlockGuard(int BlockSize) - : mpBlock(::malloc(BlockSize)) + : mpBlock(::malloc(BlockSize)), + mBlockSize(BlockSize) + { + if(mpBlock == 0) + { + throw std::bad_alloc(); + } + } + + MemoryBlockGuard(void *pBlock) + : mpBlock(pBlock) { if(mpBlock == 0) { @@ -110,9 +119,21 @@ public: } mpBlock = ptrn; } + + void* Release() + { + void* pBlock = mpBlock; + mpBlock = ::malloc(mBlockSize); + if(mpBlock == 0) + { + throw std::bad_alloc(); + } + return pBlock; + } private: void *mpBlock; + int mBlockSize; }; #include "MemLeakFindOff.h" diff --git a/lib/common/IOStream.cpp b/lib/common/IOStream.cpp index fc9d0bc3..3e126d3f 100644 --- a/lib/common/IOStream.cpp +++ b/lib/common/IOStream.cpp @@ -127,8 +127,8 @@ int IOStream::ConvertSeekTypeToOSWhence(int SeekType) // Function // Name: IOStream::ReadFullBuffer(void *, int, int) // Purpose: Reads bytes into buffer, returning whether or not it managed to -// get all the bytes required. Exception and abort use of stream -// if this returns false. +// get all the bytes required. Exception and abort use of stream +// if this returns false. // Created: 2003/08/26 // // -------------------------------------------------------------------------- @@ -165,7 +165,7 @@ bool IOStream::ReadFullBuffer(void *pBuffer, int NBytes, int *pNBytesRead, int T // Created: 2003/08/26 // // -------------------------------------------------------------------------- -void IOStream::WriteAllBuffered() +void IOStream::WriteAllBuffered(int Timeout) { } @@ -245,7 +245,30 @@ void IOStream::Flush(int Timeout) } } -void IOStream::Write(const char *pBuffer) +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::Write +// Purpose: Convenience method for writing a C++ string to a +// protocol buffer. +// +// -------------------------------------------------------------------------- +void IOStream::Write(const std::string& rBuffer, int Timeout) +{ + Write(rBuffer.c_str(), rBuffer.size(), Timeout); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::ToString() +// Purpose: Returns a string which describes this stream. Useful +// when reporting exceptions about a stream of unknown +// origin, for example in BackupStoreDirectory(). +// Created: 2014/04/28 +// +// -------------------------------------------------------------------------- +std::string IOStream::ToString() const { - Write(pBuffer, strlen(pBuffer)); + return "unknown IOStream"; } diff --git a/lib/common/IOStream.h b/lib/common/IOStream.h index 0b1cedd3..df7216c3 100644 --- a/lib/common/IOStream.h +++ b/lib/common/IOStream.h @@ -47,27 +47,27 @@ public: typedef int64_t pos_type; 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 void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) = 0; + virtual void Write(const std::string& rBuffer, + int Timeout = IOStream::TimeOutInfinite); + virtual void WriteAllBuffered(int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(pos_type Offset, int SeekType); virtual void Close(); - + // Has all data that can be read been read? virtual bool StreamDataLeft() = 0; // Has the stream been closed (writing not possible) virtual bool StreamClosed() = 0; - + // 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); + virtual std::string ToString() const; }; - #endif // IOSTREAM__H - - diff --git a/lib/common/IOStreamGetLine.h b/lib/common/IOStreamGetLine.h index c4289073..1b537031 100644 --- a/lib/common/IOStreamGetLine.h +++ b/lib/common/IOStreamGetLine.h @@ -33,16 +33,22 @@ private: public: bool GetLine(std::string &rOutput, bool Preprocess = false, int Timeout = IOStream::TimeOutInfinite); - + std::string GetLine() + { + std::string output; + GetLine(output); + return output; + } + // Call to detach, setting file pointer correctly to last bit read. // Only works for lseek-able file descriptors. void DetachFile(); - + virtual bool IsStreamDataLeft() { return mrStream.StreamDataLeft(); } - + // For doing interesting stuff with the remaining data... // Be careful with this! const void *GetBufferedData() const {return mBuffer + mBufferBegin;} @@ -52,7 +58,7 @@ public: protected: int ReadMore(int Timeout = IOStream::TimeOutInfinite); - + private: IOStream &mrStream; }; diff --git a/lib/common/InvisibleTempFileStream.cpp b/lib/common/InvisibleTempFileStream.cpp index abfcb5f6..1a9d6d5a 100644 --- a/lib/common/InvisibleTempFileStream.cpp +++ b/lib/common/InvisibleTempFileStream.cpp @@ -22,7 +22,8 @@ // Created: 2006/10/13 // // -------------------------------------------------------------------------- -InvisibleTempFileStream::InvisibleTempFileStream(const char *Filename, int flags, int mode) +InvisibleTempFileStream::InvisibleTempFileStream(const std::string& Filename, + int flags, int mode) #ifdef WIN32 : FileStream(Filename, flags | O_TEMPORARY, mode) #else @@ -30,7 +31,7 @@ InvisibleTempFileStream::InvisibleTempFileStream(const char *Filename, int flags #endif { #ifndef WIN32 - if(unlink(Filename) != 0) + if(unlink(Filename.c_str()) != 0) { MEMLEAKFINDER_NOT_A_LEAK(this); THROW_EXCEPTION(CommonException, OSFileOpenError) diff --git a/lib/common/InvisibleTempFileStream.h b/lib/common/InvisibleTempFileStream.h index a77d05e2..bb6c3954 100644 --- a/lib/common/InvisibleTempFileStream.h +++ b/lib/common/InvisibleTempFileStream.h @@ -16,7 +16,7 @@ class InvisibleTempFileStream : public FileStream { public: - InvisibleTempFileStream(const char *Filename, + InvisibleTempFileStream(const std::string& Filename, #ifdef WIN32 int flags = (O_RDONLY | O_BINARY), #else diff --git a/lib/common/Logging.cpp b/lib/common/Logging.cpp index 7ce0dd3f..0928a4d4 100644 --- a/lib/common/Logging.cpp +++ b/lib/common/Logging.cpp @@ -13,19 +13,19 @@ #include <time.h> #include <string.h> // for stderror -// c.f. http://bugs.debian.org/512510 -#include <cstdio> +#ifdef HAVE_PROCESS_H +# include <process.h> +#endif #ifdef HAVE_SYSLOG_H - #include <syslog.h> +# include <syslog.h> #endif + #ifdef HAVE_UNISTD_H - #include <unistd.h> -#endif -#ifdef WIN32 - #include <process.h> +# include <unistd.h> #endif +#include <cstdio> #include <cstring> #include <iomanip> @@ -42,16 +42,14 @@ std::vector<Logger*> Logging::sLoggers; std::string Logging::sContext; Console* Logging::spConsole = NULL; Syslog* Logging::spSyslog = NULL; -Log::Level Logging::sGlobalLevel = Log::EVERYTHING; -Logging Logging::sGlobalLogging; //automatic initialisation +Logging Logging::sGlobalLogging; // automatic initialisation std::string Logging::sProgramName; +const Log::Category Logging::UNCATEGORISED("Uncategorised"); +std::auto_ptr<HideFileGuard> Logging::sapHideFileGuard; HideSpecificExceptionGuard::SuppressedExceptions_t HideSpecificExceptionGuard::sSuppressedExceptions; -int Logging::Guard::sGuardCount = 0; -Log::Level Logging::Guard::sOriginalLevel = Log::INVALID; - Logging::Logging() { ASSERT(!spConsole); @@ -139,14 +137,10 @@ void Logging::Remove(Logger* pOldLogger) } } -void Logging::Log(Log::Level level, const std::string& rFile, - int line, const std::string& rMessage) +void Logging::Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) { - if (level > sGlobalLevel) - { - return; - } - std::string newMessage; if (sContextSet) @@ -154,12 +148,13 @@ void Logging::Log(Log::Level level, const std::string& rFile, newMessage += "[" + sContext + "] "; } - newMessage += rMessage; + newMessage += message; for (std::vector<Logger*>::iterator i = sLoggers.begin(); i != sLoggers.end(); i++) { - bool result = (*i)->Log(level, rFile, line, newMessage); + bool result = (*i)->Log(level, file, line, function, category, + newMessage); if (!result) { return; @@ -167,19 +162,15 @@ 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) +void Logging::LogToSyslog(Log::Level level, const std::string& rFile, int line, + const std::string& function, const Log::Category& category, + const std::string& message) { if (!sLogToSyslog) { return; } - if (level > sGlobalLevel) - { - return; - } - std::string newMessage; if (sContextSet) @@ -187,9 +178,9 @@ void Logging::LogToSyslog(Log::Level level, const std::string& rFile, newMessage += "[" + sContext + "] "; } - newMessage += rMessage; + newMessage += message; - spSyslog->Log(level, rFile, line, newMessage); + spSyslog->Log(level, rFile, line, function, category, newMessage); } void Logging::SetContext(std::string context) @@ -255,8 +246,7 @@ Logger::~Logger() bool Logger::IsEnabled(Log::Level level) { - return Logging::IsEnabled(level) && - (int)mCurrentLevel >= (int)level; + return (int)mCurrentLevel >= (int)level; } bool Console::sShowTime = false; @@ -290,8 +280,9 @@ void Console::SetShowPID(bool enabled) sShowPID = enabled; } -bool Console::Log(Log::Level level, const std::string& rFile, - int line, std::string& rMessage) +bool Console::Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) { if (level > GetLevel()) { @@ -299,12 +290,6 @@ bool Console::Log(Log::Level level, const std::string& rFile, } FILE* target = stdout; - - if (level <= Log::WARNING) - { - target = stderr; - } - std::ostringstream buf; if (sShowTime) @@ -354,7 +339,7 @@ bool Console::Log(Log::Level level, const std::string& rFile, buf << "TRACE: "; } - buf << rMessage; + buf << message; #ifdef WIN32 std::string output = buf.str(); @@ -376,8 +361,9 @@ bool Console::Log(Log::Level level, const std::string& rFile, return true; } -bool Syslog::Log(Log::Level level, const std::string& rFile, - int line, std::string& rMessage) +bool Syslog::Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) { if (level > GetLevel()) { @@ -418,7 +404,7 @@ bool Syslog::Log(Log::Level level, const std::string& rFile, msg = "NOTICE: "; } - msg += rMessage; + msg += message; syslog(syslogLevel, "%s", msg.c_str()); @@ -432,6 +418,11 @@ Syslog::Syslog() : mFacility(LOG_LOCAL6) Syslog::~Syslog() { + Shutdown(); +} + +void Syslog::Shutdown() +{ ::closelog(); } @@ -467,8 +458,9 @@ int Syslog::GetNamedFacility(const std::string& rFacility) return LOG_LOCAL6; } -bool FileLogger::Log(Log::Level Level, const std::string& rFile, - int line, std::string& rMessage) +bool FileLogger::Log(Log::Level Level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) { if (mLogFile.StreamClosed()) { @@ -515,7 +507,7 @@ bool FileLogger::Log(Log::Level Level, const std::string& rFile, buf << "[TRACE] "; } - buf << rMessage << "\n"; + buf << message << "\n"; std::string output = buf.str(); #ifdef WIN32 @@ -564,3 +556,211 @@ bool HideSpecificExceptionGuard::IsHidden(int type, int subtype) return false; } +// -------------------------------------------------------------------------- +// +// Function +// Name: Logging::OptionParser::GetOptionString() +// Purpose: Returns the valid Getopt command-line options +// that Logging::OptionParser::ProcessOption will handle. +// Created: 2014/04/09 +// +// -------------------------------------------------------------------------- +std::string Logging::OptionParser::GetOptionString() +{ + return "L:NPqQt:TUvVW:"; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Logging::OptionParser::ProcessOption(signed int option) +// Purpose: Processes the supplied option (equivalent to the +// return code from getopt()). Return zero if the +// option was handled successfully, or nonzero to +// abort the program with that return value. +// Created: 2007/09/18 +// +// -------------------------------------------------------------------------- +int Logging::OptionParser::ProcessOption(signed int option) +{ + switch(option) + { + case 'L': + { + if(sapHideFileGuard.get()) + { + sapHideFileGuard->Add(optarg); + } + else + { + sapHideFileGuard.reset(new HideFileGuard( + optarg, true)); // HideAllButSelected + } + } + break; + + case 'N': + { + mTruncateLogFile = true; + } + break; + + case 'P': + { + Console::SetShowPID(true); + } + break; + + case 'q': + { + if(mCurrentLevel == Log::NOTHING) + { + BOX_FATAL("Too many '-q': " + "Cannot reduce logging " + "level any more"); + return 2; + } + mCurrentLevel--; + } + break; + + case 'Q': + { + mCurrentLevel = Log::NOTHING; + } + break; + + case 't': + { + Logging::SetProgramName(optarg); + Console::SetShowTag(true); + } + break; + + case 'T': + { + Console::SetShowTime(true); + } + break; + + case 'U': + { + Console::SetShowTime(true); + Console::SetShowTimeMicros(true); + } + break; + + case 'v': + { + if(mCurrentLevel == Log::EVERYTHING) + { + BOX_FATAL("Too many '-v': " + "Cannot increase logging " + "level any more"); + return 2; + } + mCurrentLevel++; + } + break; + + case 'V': + { + mCurrentLevel = Log::EVERYTHING; + } + break; + + case 'W': + { + mCurrentLevel = Logging::GetNamedLevel(optarg); + if (mCurrentLevel == Log::INVALID) + { + BOX_FATAL("Invalid logging level: " << optarg); + return 2; + } + } + break; + + case '?': + { + BOX_FATAL("Unknown option on command line: " + << "'" << (char)optopt << "'"); + return 2; + } + break; + + default: + { + BOX_FATAL("Unknown error in getopt: returned " + << "'" << option << "'"); + return 1; + } + } + + // If we didn't explicitly return an error code above, then the option + // was fine, so return 0 to continue processing. + return 0; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Logging::OptionParser::GetUsageString() +// Purpose: Returns a string suitable for displaying as part +// of a program's command-line usage help message, +// describing the logging options. +// Created: 2014/04/09 +// +// -------------------------------------------------------------------------- +std::string Logging::OptionParser::GetUsageString() +{ + return + " -L <file> Filter out log messages except from specified file, can repeat\n" + " (for example, -L " __FILE__ ")\n" + " -N Truncate log file at startup and on backup start\n" + " -P Show process ID (PID) in console output\n" + " -q Run more quietly, reduce verbosity level by one, can repeat\n" + " -Q Run at minimum verbosity, log nothing to console and system\n" + " -t <tag> Tag console output with specified marker\n" + " -T Timestamp console output\n" + " -U Timestamp console output with microseconds\n" + " -v Run more verbosely, increase verbosity level by one, can repeat\n" + " -V Run at maximum verbosity, log everything to console and system\n" + " -W <level> Set verbosity to error/warning/notice/info/trace/everything\n"; +} + +bool HideCategoryGuard::Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) +{ + std::list<Log::Category>::iterator i = std::find(mCategories.begin(), + mCategories.end(), category); + // Return false if category is in our list, to suppress further + // logging (thus, return true if it's not in our list, i.e. we + // found nothing, to allow it). + return (i == mCategories.end()); +} + +bool HideFileGuard::Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) +{ + std::list<std::string>::iterator i = std::find(mFileNames.begin(), + mFileNames.end(), file); + bool allow_log_message; + if(mHideAllButSelected) + { + // Return true if filename is in our list, to allow further + // logging (thus, return false if it's not in our list, i.e. we + // found nothing, to suppress it). + allow_log_message = (i != mFileNames.end()); + } + else + { + // Return false if filename is in our list, to suppress further + // logging (thus, return true if it's not in our list, i.e. we + // found nothing, to allow it). + allow_log_message = (i == mFileNames.end()); + } + return allow_log_message; +} + diff --git a/lib/common/Logging.h b/lib/common/Logging.h index 1074b7c3..3dc3e69c 100644 --- a/lib/common/Logging.h +++ b/lib/common/Logging.h @@ -12,9 +12,11 @@ #include <assert.h> +#include <algorithm> #include <cerrno> #include <cstring> #include <iomanip> +#include <list> #include <sstream> #include <vector> @@ -24,14 +26,24 @@ { \ std::ostringstream _box_log_line; \ _box_log_line << stuff; \ - Logging::Log(level, __FILE__, __LINE__, _box_log_line.str()); \ + Logging::Log(level, __FILE__, __LINE__, __FUNCTION__, \ + Logging::UNCATEGORISED, _box_log_line.str()); \ +} + +#define BOX_LOG_CATEGORY(level, category, stuff) \ +{ \ + std::ostringstream _box_log_line; \ + _box_log_line << stuff; \ + Logging::Log(level, __FILE__, __LINE__, __FUNCTION__, \ + category, _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()); \ + Logging::LogToSyslog(level, __FILE__, __LINE__, __FUNCTION__, \ + Logging::UNCATEGORISED, _box_log_line.str()); \ } #define BOX_FATAL(stuff) BOX_LOG(Log::FATAL, stuff) @@ -39,9 +51,7 @@ #define BOX_WARNING(stuff) BOX_LOG(Log::WARNING, stuff) #define BOX_NOTICE(stuff) BOX_LOG(Log::NOTICE, stuff) #define BOX_INFO(stuff) BOX_LOG(Log::INFO, stuff) -#define BOX_TRACE(stuff) \ - if (Logging::IsEnabled(Log::TRACE)) \ - { BOX_LOG(Log::TRACE, stuff) } +#define BOX_TRACE(stuff) BOX_LOG(Log::TRACE, stuff) #define BOX_SYS_ERRNO_MESSAGE(error_number, stuff) \ stuff << ": " << std::strerror(error_number) << \ @@ -85,18 +95,20 @@ BOX_FILE_MESSAGE(filename, message)) #ifdef WIN32 + #define BOX_WIN_ERRNO_MESSAGE(error_number, stuff) \ + stuff << ": " << GetErrorMessage(error_number) + #define BOX_NATIVE_ERRNO_MESSAGE(error_number, stuff) \ + BOX_WIN_ERRNO_MESSAGE(error_number, stuff) #define BOX_LOG_WIN_ERROR(stuff) \ - BOX_ERROR(stuff << ": " << GetErrorMessage(GetLastError())) + BOX_ERROR(BOX_WIN_ERRNO_MESSAGE(GetLastError(), stuff)) #define BOX_LOG_WIN_WARNING(stuff) \ - BOX_WARNING(stuff << ": " << GetErrorMessage(GetLastError())) + BOX_WARNING(BOX_WIN_ERRNO_MESSAGE(GetLastError(), stuff)) #define BOX_LOG_WIN_ERROR_NUMBER(stuff, number) \ - BOX_ERROR(stuff << ": " << GetErrorMessage(number)) + BOX_ERROR(BOX_WIN_ERRNO_MESSAGE(number, stuff)) #define BOX_LOG_WIN_WARNING_NUMBER(stuff, number) \ - BOX_WARNING(stuff << ": " << GetErrorMessage(number)) + BOX_WARNING(BOX_WIN_ERRNO_MESSAGE(number, stuff)) #define BOX_LOG_NATIVE_ERROR(stuff) BOX_LOG_WIN_ERROR(stuff) #define BOX_LOG_NATIVE_WARNING(stuff) BOX_LOG_WIN_WARNING(stuff) - #define BOX_WIN_ERRNO_MESSAGE(error_number, stuff) \ - stuff << ": " << GetErrorMessage(error_number) #define THROW_WIN_ERROR_NUMBER(message, error_number, exception, subtype) \ THROW_EXCEPTION_MESSAGE(exception, subtype, \ BOX_WIN_ERRNO_MESSAGE(error_number, message)) @@ -106,19 +118,33 @@ #define THROW_WIN_FILE_ERROR(message, filename, exception, subtype) \ THROW_WIN_FILE_ERRNO(message, filename, GetLastError(), \ exception, subtype) + #define EMU_ERRNO winerrno + #define THROW_EMU_ERROR(message, exception, subtype) \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + BOX_NATIVE_ERRNO_MESSAGE(EMU_ERRNO, message)) #else + #define BOX_NATIVE_ERRNO_MESSAGE(error_number, stuff) \ + BOX_SYS_ERRNO_MESSAGE(error_number, stuff) #define BOX_LOG_NATIVE_ERROR(stuff) BOX_LOG_SYS_ERROR(stuff) #define BOX_LOG_NATIVE_WARNING(stuff) BOX_LOG_SYS_WARNING(stuff) + #define EMU_ERRNO errno + #define THROW_EMU_ERROR(message, exception, subtype) \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + BOX_SYS_ERRNO_MESSAGE(EMU_ERRNO, message)) #endif +#define THROW_EMU_FILE_ERROR(message, filename, exception, subtype) \ + THROW_EMU_ERROR(BOX_FILE_MESSAGE(filename, message), \ + exception, subtype) + #ifdef WIN32 -# define BOX_LOG_SOCKET_ERROR(_type, _name, _port, stuff) \ - BOX_LOG_WIN_ERROR_NUMBER(stuff << " (type " << _type << ", name " << \ - _name << ", port " << _port << ")", WSAGetLastError()) +# define BOX_SOCKET_ERROR_MESSAGE(_type, _name, _port, stuff) \ + BOX_WIN_ERRNO_MESSAGE(WSAGetLastError(), stuff << " (type " << _type << \ + ", name " << _name << ", port " << _port << ")") #else -# define BOX_LOG_SOCKET_ERROR(_type, _name, _port, stuff) \ - BOX_LOG_NATIVE_ERROR(stuff << " (type " << _type << ", name " << \ - _name << ", port " << _port << ")") +# define BOX_SOCKET_ERROR_MESSAGE(_type, _name, _port, stuff) \ + BOX_SYS_ERROR_MESSAGE(stuff << " (type " << _type << ", name " << _name << \ + ", port " << _port << ")") #endif #define BOX_FORMAT_HEX32(number) \ @@ -141,14 +167,16 @@ #define BOX_FORMAT_TIMESPEC(timespec) \ timespec.tv_sec << \ + "." << \ std::setw(6) << \ + std::setfill('0') << \ timespec.tv_usec #define BOX_FORMAT_MICROSECONDS(t) \ (int)((t) / 1000000) << "." << \ - std::setw(6) << \ + std::setw(3) << \ std::setfill('0') << \ - (int)((t) % 1000000) << " seconds" + (int)((t % 1000000) / 1000) << " seconds" #undef ERROR @@ -166,6 +194,18 @@ namespace Log EVERYTHING, INVALID = -1 }; + + class Category { + private: + std::string mName; + + public: + Category(const std::string& name) + : mName(name) + { } + const std::string& ToString() { return mName; } + bool operator==(const Category& other) { return mName == other.mName; } + }; } // -------------------------------------------------------------------------- @@ -187,8 +227,9 @@ class Logger Logger(Log::Level level); virtual ~Logger(); - virtual bool Log(Log::Level level, const std::string& rFile, - int line, std::string& rMessage) = 0; + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) = 0; void Filter(Log::Level level) { @@ -201,19 +242,23 @@ class Logger virtual void SetProgramName(const std::string& rProgramName) = 0; - class Guard + class LevelGuard { private: Logger& mLogger; Log::Level mOldLevel; public: - Guard(Logger& Logger) + LevelGuard(Logger& Logger, Log::Level newLevel = Log::INVALID) : mLogger(Logger) { mOldLevel = Logger.GetLevel(); + if (newLevel != Log::INVALID) + { + Logger.Filter(newLevel); + } } - ~Guard() + ~LevelGuard() { mLogger.Filter(mOldLevel); } @@ -239,8 +284,9 @@ class Console : public Logger static std::string sTag; public: - virtual bool Log(Log::Level level, const std::string& rFile, - int line, std::string& rMessage); + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message); virtual const char* GetType() { return "Console"; } virtual void SetProgramName(const std::string& rProgramName); @@ -248,6 +294,33 @@ class Console : public Logger static void SetShowTime(bool enabled); static void SetShowTimeMicros(bool enabled); static void SetShowPID(bool enabled); + static bool GetShowTag() { return sShowTag; } + + class SettingsGuard + { + private: + bool mShowTag; + bool mShowTime; + bool mShowTimeMicros; + bool mShowPID; + std::string mTag; + public: + SettingsGuard() + : mShowTag(Console::sShowTag), + mShowTime(Console::sShowTime), + mShowTimeMicros(Console::sShowTimeMicros), + mShowPID(Console::sShowPID), + mTag(Console::sTag) + { } + ~SettingsGuard() + { + Console::SetShowTag(mShowTag); + Console::SetShowTime(mShowTime); + Console::SetShowTimeMicros(mShowTimeMicros); + Console::SetShowPID(mShowPID); + Console::sTag = mTag; + } + }; }; // -------------------------------------------------------------------------- @@ -269,17 +342,80 @@ class Syslog : public Logger Syslog(); virtual ~Syslog(); - virtual bool Log(Log::Level level, const std::string& rFile, - int line, std::string& rMessage); + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message); virtual const char* GetType() { return "Syslog"; } virtual void SetProgramName(const std::string& rProgramName); virtual void SetFacility(int facility); + virtual void Shutdown(); static int GetNamedFacility(const std::string& rFacility); }; // -------------------------------------------------------------------------- // // Class +// Name: Capture +// Purpose: Keeps log messages for analysis in tests. +// Created: 2014/03/08 +// +// -------------------------------------------------------------------------- + +class Capture : public Logger +{ + public: + struct Message + { + Message(const Log::Category& category) + : mCategory(category) { } + Log::Level level; + std::string file; + int line; + std::string function; + Log::Category mCategory; + std::string message; + }; + + private: + std::vector<Message> mMessages; + + public: + virtual ~Capture() { } + + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) + { + Message msg(category); + msg.level = level; + msg.file = file; + msg.line = line; + msg.function = function; + msg.message = message; + mMessages.push_back(msg); + return true; + } + virtual const char* GetType() { return "Capture"; } + virtual void SetProgramName(const std::string& rProgramName) { } + const std::vector<Message>& GetMessages() const { return mMessages; } + std::string GetString() const + { + std::ostringstream oss; + for (std::vector<Message>::const_iterator i = mMessages.begin(); + i != mMessages.end(); i++) + { + oss << i->message << "\n"; + } + return oss.str(); + } +}; + +// Forward declaration +class HideFileGuard; + +// -------------------------------------------------------------------------- +// +// Class // Name: Logging // Purpose: Static logging helper, keeps track of enabled loggers // and distributes log messages to them. @@ -296,10 +432,10 @@ class Logging static bool sContextSet; static Console* spConsole; static Syslog* spSyslog; - static Log::Level sGlobalLevel; static Logging sGlobalLogging; static std::string sProgramName; - + static std::auto_ptr<HideFileGuard> sapHideFileGuard; + public: Logging (); ~Logging(); @@ -309,55 +445,35 @@ class Logging static void FilterConsole (Log::Level level); static void Add (Logger* pNewLogger); 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 Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message); + static void LogToSyslog(Log::Level level, const std::string& rFile, int line, + const std::string& function, const Log::Category& category, + const std::string& message); 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); static Console& GetConsole() { return *spConsole; } static Syslog& GetSyslog() { return *spSyslog; } - class Guard + class ShowTagOnConsole { private: - Log::Level mOldLevel; - static int sGuardCount; - static Log::Level sOriginalLevel; - + bool mOldShowTag; + public: - Guard(Log::Level newLevel) - { - mOldLevel = Logging::GetGlobalLevel(); - if(sGuardCount == 0) - { - sOriginalLevel = mOldLevel; - } - sGuardCount++; - Logging::SetGlobalLevel(newLevel); - } - ~Guard() + ShowTagOnConsole() + : mOldShowTag(Console::GetShowTag()) { - sGuardCount--; - Logging::SetGlobalLevel(mOldLevel); + Console::SetShowTag(true); } - - static bool IsActive() { return (sGuardCount > 0); } - static Log::Level GetOriginalLevel() { return sOriginalLevel; } - static bool IsGuardingFrom(Log::Level originalLevel) + ~ShowTagOnConsole() { - return IsActive() && - (int)sOriginalLevel >= (int)originalLevel; + Console::SetShowTag(mOldShowTag); } }; @@ -365,16 +481,19 @@ class Logging { private: std::string mOldTag; + bool mReplace; public: Tagger() - : mOldTag(Logging::GetProgramName()) + : mOldTag(Logging::GetProgramName()), + mReplace(false) { } - Tagger(const std::string& rTempTag) - : mOldTag(Logging::GetProgramName()) + Tagger(const std::string& rTempTag, bool replace = false) + : mOldTag(Logging::GetProgramName()), + mReplace(replace) { - Logging::SetProgramName(mOldTag + " " + rTempTag); + Change(rTempTag); } ~Tagger() { @@ -383,9 +502,73 @@ class Logging void Change(const std::string& newTempTag) { - Logging::SetProgramName(mOldTag + " " + newTempTag); + if(mReplace || mOldTag.empty()) + { + Logging::SetProgramName(newTempTag); + } + else + { + Logging::SetProgramName(mOldTag + " " + newTempTag); + } + } + }; + + class TempLoggerGuard + { + private: + Logger* mpLogger; + + public: + TempLoggerGuard(Logger* pLogger) + : mpLogger(pLogger) + { + Logging::Add(mpLogger); + } + ~TempLoggerGuard() + { + Logging::Remove(mpLogger); + } + }; + + // Process global options + static std::string GetOptionString(); + static int ProcessOption(signed int option); + static std::string GetUsageString(); + + // -------------------------------------------------------------------------- + // + // Class + // Name: Logging::OptionParser + // Purpose: Process command-line options, some global, some local + // Created: 2014/04/09 + // + // -------------------------------------------------------------------------- + class OptionParser + { + public: + OptionParser(Log::Level InitialLevel = + #ifdef BOX_RELEASE_BUILD + Log::NOTICE + #else + Log::INFO + #endif + ) + : mCurrentLevel(InitialLevel), + mTruncateLogFile(false) + { } + + static std::string GetOptionString(); + int ProcessOption(signed int option); + static std::string GetUsageString(); + int mCurrentLevel; // need an int to do math with + bool mTruncateLogFile; + Log::Level GetCurrentLevel() + { + return (Log::Level) mCurrentLevel; } }; + + static const Log::Category UNCATEGORISED; }; class FileLogger : public Logger @@ -396,13 +579,14 @@ class FileLogger : public Logger : mLogFile("") { /* do not call */ } public: - FileLogger(const std::string& rFileName, Log::Level Level) + FileLogger(const std::string& rFileName, Log::Level Level, bool append) : Logger(Level), - mLogFile(rFileName, O_WRONLY | O_CREAT | O_APPEND) + mLogFile(rFileName, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC)) { } - virtual bool Log(Log::Level Level, const std::string& rFile, - int Line, std::string& rMessage); + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message); virtual const char* GetType() { return "FileLogger"; } virtual void SetProgramName(const std::string& rProgramName) { } @@ -451,6 +635,64 @@ class HideSpecificExceptionGuard static bool IsHidden(int type, int subtype); }; +class HideCategoryGuard : public Logger +{ + private: + std::list<Log::Category> mCategories; + HideCategoryGuard(const HideCategoryGuard& other); // no copying + HideCategoryGuard& operator=(const HideCategoryGuard& other); // no assignment + + public: + HideCategoryGuard(const Log::Category& category) + { + mCategories.push_back(category); + Logging::Add(this); + } + ~HideCategoryGuard() + { + Logging::Remove(this); + } + void Add(const Log::Category& category) + { + mCategories.push_back(category); + } + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message); + virtual const char* GetType() { return "HideCategoryGuard"; } + virtual void SetProgramName(const std::string& rProgramName) { } +}; + +class HideFileGuard : public Logger +{ + private: + std::list<std::string> mFileNames; + HideFileGuard(const HideFileGuard& other); // no copying + HideFileGuard& operator=(const HideFileGuard& other); // no assignment + bool mHideAllButSelected; + + public: + HideFileGuard(const std::string& rFileName, bool HideAllButSelected = false) + : mHideAllButSelected(HideAllButSelected) + { + mFileNames.push_back(rFileName); + Logging::Add(this); + } + ~HideFileGuard() + { + Logging::Remove(this); + } + void Add(const std::string& rFileName) + { + mFileNames.push_back(rFileName); + } + virtual bool Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message); + virtual const char* GetType() { return "HideFileGuard"; } + virtual void SetProgramName(const std::string& rProgramName) { } +}; + std::string PrintEscapedBinaryData(const std::string& rInput); #endif // LOGGING__H diff --git a/lib/common/MainHelper.h b/lib/common/MainHelper.h index 3c6e9ff0..0303090e 100644 --- a/lib/common/MainHelper.h +++ b/lib/common/MainHelper.h @@ -19,18 +19,21 @@ #include "BoxException.h" #include "Logging.h" -#define MAINHELPER_START \ - if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ - { printf(BOX_VERSION "\n"); return 0; } \ +#define MAINHELPER_START \ + if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ + { printf(BOX_VERSION "\n"); return 0; } \ MEMLEAKFINDER_INIT \ - MEMLEAKFINDER_START \ + MEMLEAKFINDER_START \ try { -#define MAINHELPER_END \ - } catch(std::exception &e) { \ +#define MAINHELPER_END \ + } catch(BoxException &e) { \ + BOX_FATAL(e.what() << ": " << e.GetMessage()); \ + return 1; \ + } catch(std::exception &e) { \ BOX_FATAL(e.what()); \ - return 1; \ - } catch(...) { \ + return 1; \ + } catch(...) { \ BOX_FATAL("UNKNOWN"); \ return 1; \ } diff --git a/lib/common/MemBlockStream.cpp b/lib/common/MemBlockStream.cpp index 3a43a304..f49ac96f 100644 --- a/lib/common/MemBlockStream.cpp +++ b/lib/common/MemBlockStream.cpp @@ -52,6 +52,26 @@ MemBlockStream::MemBlockStream(const void *pBuffer, int Size) // -------------------------------------------------------------------------- // // Function +// Name: MemBlockStream::MemBlockStream(const std::string& rMessage) +// Purpose: Convenience constructor for sending a simple string. +// Copies the string, so you can pass a temporary in. +// Created: 2014/01/20 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const std::string& rMessage) +: mReadPosition(0) +{ + mTempBuffer.Write(rMessage.c_str(), rMessage.size()); + mTempBuffer.SetForReading(); + mpBuffer = (const char *)(mTempBuffer.GetBuffer()); + mBytesInBuffer = rMessage.size(); + ASSERT(mpBuffer != 0); + ASSERT(mBytesInBuffer >= 0); +} + +// -------------------------------------------------------------------------- +// +// Function // Name: MemBlockStream::MemBlockStream(const StreamableMemBlock &) // Purpose: Constructor (doesn't copy block, careful with lifetimes) // Created: 2003/09/05 @@ -159,7 +179,7 @@ IOStream::pos_type MemBlockStream::BytesLeftToRead() // Created: 2003/09/05 // // -------------------------------------------------------------------------- -void MemBlockStream::Write(const void *pBuffer, int NBytes) +void MemBlockStream::Write(const void *pBuffer, int NBytes, int Timeout) { THROW_EXCEPTION(CommonException, MemBlockStreamNotSupported) } diff --git a/lib/common/MemBlockStream.h b/lib/common/MemBlockStream.h index 5234525b..1ba4b0a6 100644 --- a/lib/common/MemBlockStream.h +++ b/lib/common/MemBlockStream.h @@ -10,10 +10,10 @@ #ifndef MEMBLOCKSTREAM__H #define MEMBLOCKSTREAM__H +#include "CollectInBufferStream.h" #include "IOStream.h" class StreamableMemBlock; -class CollectInBufferStream; // -------------------------------------------------------------------------- // @@ -29,6 +29,7 @@ class MemBlockStream : public IOStream public: MemBlockStream(); MemBlockStream(const void *pBuffer, int Size); + MemBlockStream(const std::string& rMessage); MemBlockStream(const StreamableMemBlock &rBlock); MemBlockStream(const CollectInBufferStream &rBuffer); MemBlockStream(const MemBlockStream &rToCopy); @@ -37,7 +38,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(pos_type Offset, int SeekType); virtual bool StreamDataLeft(); @@ -46,6 +48,9 @@ public: virtual int GetSize() const { return mBytesInBuffer; } private: + // Use mTempBuffer when we need to hold a copy of the memory block, + // and free it ourselves when done. + CollectInBufferStream mTempBuffer; const char *mpBuffer; int mBytesInBuffer; int mReadPosition; diff --git a/lib/common/MemLeakFindOn.h b/lib/common/MemLeakFindOn.h index c20fe25a..f1113184 100644 --- a/lib/common/MemLeakFindOn.h +++ b/lib/common/MemLeakFindOn.h @@ -15,6 +15,7 @@ #ifndef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED #define malloc(X) memleakfinder_malloc(X, __FILE__, __LINE__) + #define calloc(X, Y) memleakfinder_calloc(X, Y, __FILE__, __LINE__) #define realloc memleakfinder_realloc #define free memleakfinder_free #define MEMLEAKFINDER_MALLOC_MONITORING_DEFINED diff --git a/lib/common/MemLeakFinder.h b/lib/common/MemLeakFinder.h index 1a2cf90c..07b52e26 100644 --- a/lib/common/MemLeakFinder.h +++ b/lib/common/MemLeakFinder.h @@ -43,7 +43,7 @@ void memleakfinder_reportleaks(); void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext); -void memleakfinder_setup_exit_report(const char *filename, const char *markertext); +void memleakfinder_setup_exit_report(const std::string& filename, const char *markertext); void memleakfinder_startsectionmonitor(); @@ -54,7 +54,8 @@ void memleakfinder_notaleak(void *ptr); void *operator new (size_t size, const char *file, int line); void *operator new[](size_t size, const char *file, int line); -// define the malloc functions now, if required +// Define the malloc functions now, if required. These should match the definitions +// in MemLeakFindOn.h. #ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING #define malloc(X) memleakfinder_malloc(X, __FILE__, __LINE__) #define calloc(X, Y) memleakfinder_calloc(X, Y, __FILE__, __LINE__) diff --git a/lib/common/NamedLock.cpp b/lib/common/NamedLock.cpp index f96f80b5..8e672ff5 100644 --- a/lib/common/NamedLock.cpp +++ b/lib/common/NamedLock.cpp @@ -21,8 +21,9 @@ #include <sys/file.h> #endif -#include "NamedLock.h" #include "CommonException.h" +#include "NamedLock.h" +#include "Utils.h" #include "MemLeakFindOn.h" @@ -35,7 +36,11 @@ // // -------------------------------------------------------------------------- NamedLock::NamedLock() - : mFileDescriptor(-1) +#ifdef WIN32 +: mFileDescriptor(INVALID_HANDLE_VALUE) +#else +: mFileDescriptor(-1) +#endif { } @@ -49,7 +54,11 @@ NamedLock::NamedLock() // -------------------------------------------------------------------------- NamedLock::~NamedLock() { +#ifdef WIN32 + if(mFileDescriptor != INVALID_HANDLE_VALUE) +#else if(mFileDescriptor != -1) +#endif { ReleaseLock(); } @@ -68,76 +77,151 @@ NamedLock::~NamedLock() bool NamedLock::TryAndGetLock(const std::string& rFilename, int mode) { // Check +#ifdef WIN32 + if(mFileDescriptor != INVALID_HANDLE_VALUE) +#else if(mFileDescriptor != -1) +#endif { THROW_EXCEPTION(CommonException, NamedLockAlreadyLockingSomething) } + mFileName = rFilename; + // See if the lock can be got + int flags = O_WRONLY | O_CREAT | O_TRUNC; + #if HAVE_DECL_O_EXLOCK - int fd = ::open(rFilename.c_str(), - O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC | O_EXLOCK, mode); - if(fd != -1) - { - // Got a lock, lovely - mFileDescriptor = fd; - return true; - } - - // Failed. Why? - if(errno != EWOULDBLOCK) - { - // Not the expected error - THROW_EXCEPTION(CommonException, OSFileError) - } + flags |= O_NONBLOCK | O_EXLOCK; + BOX_TRACE("Trying to create lockfile " << rFilename << " using O_EXLOCK"); +#elif defined BOX_OPEN_LOCK + flags |= BOX_OPEN_LOCK; + BOX_TRACE("Trying to create lockfile " << rFilename << " using BOX_OPEN_LOCK"); +#elif !HAVE_DECL_F_SETLK && !defined HAVE_FLOCK + // We have no other way to get a lock, so all we can do is fail if + // the file already exists, and take the risk of stale locks. + flags |= O_EXCL; + BOX_TRACE("Trying to create lockfile " << rFilename << " using O_EXCL"); +#else + BOX_TRACE("Trying to create lockfile " << rFilename << " without special flags"); +#endif - return false; +#ifdef WIN32 + HANDLE fd = openfile(rFilename.c_str(), flags, mode); + if(fd == INVALID_HANDLE_VALUE) #else - int fd = ::open(rFilename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); + int fd = ::open(rFilename.c_str(), flags, mode); if(fd == -1) - { - BOX_WARNING("Failed to open lockfile: " << rFilename); - THROW_EXCEPTION(CommonException, OSFileError) - } - -#ifdef HAVE_FLOCK - if(::flock(fd, LOCK_EX | LOCK_NB) != 0) - { - ::close(fd); +#endif +#if HAVE_DECL_O_EXLOCK + { // if() if(errno == EWOULDBLOCK) { + // Lockfile already exists, and we tried to open it + // exclusively, which means we failed to lock it. + BOX_NOTICE("Failed to lock lockfile with O_EXLOCK: " << rFilename + << ": already locked by another process?"); return false; } else { - THROW_EXCEPTION(CommonException, OSFileError) + THROW_SYS_FILE_ERROR("Failed to open lockfile with O_EXLOCK", + rFilename, CommonException, OSFileError); } } -#elif HAVE_DECL_F_SETLK - struct flock desc; - desc.l_type = F_WRLCK; - desc.l_whence = SEEK_SET; - desc.l_start = 0; - desc.l_len = 0; - if(::fcntl(fd, F_SETLK, &desc) != 0) - { - ::close(fd); - if(errno == EAGAIN) +#else // !HAVE_DECL_O_EXLOCK + { // if() +# if defined BOX_OPEN_LOCK + if(errno == EBUSY) +# else // !BOX_OPEN_LOCK + if(errno == EEXIST && (flags & O_EXCL)) +# endif { + // Lockfile already exists, and we tried to open it + // exclusively, which means we failed to lock it. + BOX_NOTICE("Failed to lock lockfile with O_EXCL: " << rFilename + << ": already locked by another process?"); return false; } else { - THROW_EXCEPTION(CommonException, OSFileError) + THROW_SYS_FILE_ERROR("Failed to open lockfile with O_EXCL", + rFilename, CommonException, OSFileError); } } -#endif + + try + { +# ifdef HAVE_FLOCK + BOX_TRACE("Trying to lock lockfile " << rFilename << " using flock()"); + if(::flock(fd, LOCK_EX | LOCK_NB) != 0) + { + if(errno == EWOULDBLOCK) + { + ::close(fd); + BOX_NOTICE("Failed to lock lockfile with flock(): " << rFilename + << ": already locked by another process"); + return false; + } + else + { + THROW_SYS_FILE_ERROR("Failed to lock lockfile with flock()", + rFilename, CommonException, OSFileError); + } + } +# elif HAVE_DECL_F_SETLK + struct flock desc; + desc.l_type = F_WRLCK; + desc.l_whence = SEEK_SET; + desc.l_start = 0; + desc.l_len = 0; + BOX_TRACE("Trying to lock lockfile " << rFilename << " using fcntl()"); + if(::fcntl(fd, F_SETLK, &desc) != 0) + { + if(errno == EAGAIN) + { + ::close(fd); + BOX_NOTICE("Failed to lock lockfile with fcntl(): " << rFilename + << ": already locked by another process"); + return false; + } + else + { + THROW_SYS_FILE_ERROR("Failed to lock lockfile with fcntl()", + rFilename, CommonException, OSFileError); + } + } +# endif + } + catch(BoxException &e) + { +# ifdef WIN32 + CloseHandle(fd); +# else + ::close(fd); +# endif + BOX_NOTICE("Failed to lock lockfile " << rFilename << ": " << e.what()); + throw; + } +#endif // HAVE_DECL_O_EXLOCK + + if(!FileExists(rFilename)) + { + BOX_ERROR("Locked lockfile " << rFilename << ", but lockfile no longer " + "exists, bailing out"); +# ifdef WIN32 + CloseHandle(fd); +# else + ::close(fd); +# endif + return false; + } // Success mFileDescriptor = fd; + BOX_TRACE("Successfully locked lockfile " << rFilename); return true; -#endif } // -------------------------------------------------------------------------- @@ -151,20 +235,65 @@ bool NamedLock::TryAndGetLock(const std::string& rFilename, int mode) void NamedLock::ReleaseLock() { // Got a lock? +#ifdef WIN32 + if(mFileDescriptor == INVALID_HANDLE_VALUE) +#else if(mFileDescriptor == -1) +#endif { THROW_EXCEPTION(CommonException, NamedLockNotHeld) } - + +#ifndef WIN32 + // Delete the file. We need to do this before closing the filehandle, + // if we used flock() or fcntl() to lock it, otherwise someone could + // acquire the lock, release and delete it between us closing (and + // hence releasing) and deleting it, and we'd fail when it came to + // deleting the file. This happens in tests much more often than + // you'd expect! + // + // This doesn't apply on systems using plain lockfile locking, such as + // Windows, and there we need to close the file before deleting it, + // otherwise the system won't let us delete it. + + if(::unlink(mFileName.c_str()) != 0) + { + THROW_EMU_ERROR( + BOX_FILE_MESSAGE(mFileName, "Failed to delete lockfile"), + CommonException, OSFileError); + } +#endif // !WIN32 + // Close the file +# ifdef WIN32 + if(!CloseHandle(mFileDescriptor)) +# else if(::close(mFileDescriptor) != 0) +# endif { - THROW_EXCEPTION(CommonException, OSFileError) + THROW_EMU_ERROR( + BOX_FILE_MESSAGE(mFileName, "Failed to close lockfile"), + CommonException, OSFileError); } - // Mark as unlocked - mFileDescriptor = -1; -} + // Mark as unlocked, so we don't try to close it again if the unlink() fails. +#ifdef WIN32 + mFileDescriptor = INVALID_HANDLE_VALUE; +#else + mFileDescriptor = -1; +#endif +#ifdef WIN32 + // On Windows we need to close the file before deleting it, otherwise + // the system won't let us delete it. + if(::unlink(mFileName.c_str()) != 0) + { + THROW_EMU_ERROR( + BOX_FILE_MESSAGE(mFileName, "Failed to delete lockfile"), + CommonException, OSFileError); + } +#endif // WIN32 + BOX_TRACE("Released lock and deleted lockfile " << mFileName); +} diff --git a/lib/common/NamedLock.h b/lib/common/NamedLock.h index 534115db..a7d0d778 100644 --- a/lib/common/NamedLock.h +++ b/lib/common/NamedLock.h @@ -29,12 +29,21 @@ private: public: bool TryAndGetLock(const std::string& rFilename, int mode = 0755); +# ifdef WIN32 + bool GotLock() {return mFileDescriptor != INVALID_HANDLE_VALUE;} +# else bool GotLock() {return mFileDescriptor != -1;} +# endif void ReleaseLock(); - private: +# ifdef WIN32 + HANDLE mFileDescriptor; +# else int mFileDescriptor; +# endif + + std::string mFileName; }; #endif // NAMEDLOCK__H diff --git a/lib/common/PartialReadStream.cpp b/lib/common/PartialReadStream.cpp index f2f79715..b5f99bb5 100644 --- a/lib/common/PartialReadStream.cpp +++ b/lib/common/PartialReadStream.cpp @@ -104,7 +104,7 @@ IOStream::pos_type PartialReadStream::BytesLeftToRead() // Created: 2003/08/26 // // -------------------------------------------------------------------------- -void PartialReadStream::Write(const void *pBuffer, int NBytes) +void PartialReadStream::Write(const void *pBuffer, int NBytes, int Timeout) { THROW_EXCEPTION(CommonException, CantWriteToPartialReadStream) } diff --git a/lib/common/PartialReadStream.h b/lib/common/PartialReadStream.h index 1b46b0bd..61bdd7d1 100644 --- a/lib/common/PartialReadStream.h +++ b/lib/common/PartialReadStream.h @@ -33,7 +33,8 @@ private: public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual bool StreamDataLeft(); virtual bool StreamClosed(); diff --git a/lib/common/RateLimitingStream.h b/lib/common/RateLimitingStream.h index a322b99b..818c90af 100644 --- a/lib/common/RateLimitingStream.h +++ b/lib/common/RateLimitingStream.h @@ -30,9 +30,10 @@ public: int Timeout = IOStream::TimeOutInfinite); // Everything else is delegated to the sink - virtual void Write(const void *pBuffer, int NBytes) + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) { - Write(pBuffer, NBytes); + mrSink.Write(pBuffer, NBytes, Timeout); } virtual pos_type BytesLeftToRead() { diff --git a/lib/common/ReadGatherStream.cpp b/lib/common/ReadGatherStream.cpp index f50e6664..ae252832 100644 --- a/lib/common/ReadGatherStream.cpp +++ b/lib/common/ReadGatherStream.cpp @@ -213,7 +213,7 @@ IOStream::pos_type ReadGatherStream::BytesLeftToRead() // Created: 10/12/03 // // -------------------------------------------------------------------------- -void ReadGatherStream::Write(const void *pBuffer, int NBytes) +void ReadGatherStream::Write(const void *pBuffer, int NBytes, int Timeout) { THROW_EXCEPTION(CommonException, CannotWriteToReadGatherStream); } diff --git a/lib/common/ReadGatherStream.h b/lib/common/ReadGatherStream.h index 613ede3e..9a44480b 100644 --- a/lib/common/ReadGatherStream.h +++ b/lib/common/ReadGatherStream.h @@ -37,7 +37,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual bool StreamDataLeft(); virtual bool StreamClosed(); virtual pos_type GetPosition() const; diff --git a/lib/common/ReadLoggingStream.cpp b/lib/common/ReadLoggingStream.cpp index 54c99c95..df493344 100644 --- a/lib/common/ReadLoggingStream.cpp +++ b/lib/common/ReadLoggingStream.cpp @@ -96,7 +96,7 @@ IOStream::pos_type ReadLoggingStream::BytesLeftToRead() // Created: 2003/07/31 // // -------------------------------------------------------------------------- -void ReadLoggingStream::Write(const void *pBuffer, int NBytes) +void ReadLoggingStream::Write(const void *pBuffer, int NBytes, int Timeout) { THROW_EXCEPTION(CommonException, NotSupported); } diff --git a/lib/common/ReadLoggingStream.h b/lib/common/ReadLoggingStream.h index b23b542c..bee7e1d6 100644 --- a/lib/common/ReadLoggingStream.h +++ b/lib/common/ReadLoggingStream.h @@ -39,7 +39,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(IOStream::pos_type Offset, int SeekType); virtual void Close(); @@ -48,7 +49,7 @@ public: virtual bool StreamClosed(); private: - ReadLoggingStream(const ReadLoggingStream &rToCopy) + ReadLoggingStream(const ReadLoggingStream &rToCopy) : mrSource(rToCopy.mrSource), mrLogger(rToCopy.mrLogger) { /* do not call */ } }; diff --git a/lib/common/SelfFlushingStream.h b/lib/common/SelfFlushingStream.h index 36e9a4d3..b4efa294 100644 --- a/lib/common/SelfFlushingStream.h +++ b/lib/common/SelfFlushingStream.h @@ -33,6 +33,12 @@ public: ~SelfFlushingStream() { + if(StreamDataLeft()) + { + BOX_WARNING("Not all data was read from stream, " + "discarding the rest"); + } + Flush(); } @@ -50,9 +56,10 @@ public: { return mrSource.BytesLeftToRead(); } - virtual void Write(const void *pBuffer, int NBytes) + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) { - mrSource.Write(pBuffer, NBytes); + mrSource.Write(pBuffer, NBytes, Timeout); } virtual bool StreamDataLeft() { diff --git a/lib/common/StreamableMemBlock.cpp b/lib/common/StreamableMemBlock.cpp index b376f037..9abf78d3 100644 --- a/lib/common/StreamableMemBlock.cpp +++ b/lib/common/StreamableMemBlock.cpp @@ -125,7 +125,9 @@ void StreamableMemBlock::Set(IOStream &rStream, int Timeout) try { // Read in - if(!rStream.ReadFullBuffer(pblock, size, 0 /* not interested in bytes read if this fails */)) + if(!rStream.ReadFullBuffer(pblock, size, + 0 /* not interested in bytes read if this fails */, + Timeout)) { THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) } @@ -252,7 +254,9 @@ void StreamableMemBlock::ReadFromStream(IOStream &rStream, int Timeout) { // Get the size of the block int32_t size_s; - if(!rStream.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */)) + if(!rStream.ReadFullBuffer(&size_s, sizeof(size_s), + 0, /* not interested in bytes read if this fails */ + Timeout)) { THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) } @@ -270,7 +274,9 @@ void StreamableMemBlock::ReadFromStream(IOStream &rStream, int Timeout) try { // Read in - if(!rStream.ReadFullBuffer(pblock, size, 0 /* not interested in bytes read if this fails */)) + if(!rStream.ReadFullBuffer(pblock, size, + 0, /* not interested in bytes read if this fails */ + Timeout)) { THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) } diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp index de87c465..2c51cd61 100644 --- a/lib/common/Test.cpp +++ b/lib/common/Test.cpp @@ -22,7 +22,236 @@ #endif #include "BoxTime.h" +#include "FileStream.h" #include "Test.h" +#include "Utils.h" + +int num_tests_selected = 0; +int num_failures = 0; +int old_failure_count = 0; +int first_fail_line; +std::string original_working_dir; +std::string first_fail_file; +std::string current_test_name; +std::list<std::string> run_only_named_tests; +std::map<std::string, std::string> s_test_status; + +bool setUp(const char* function_name) +{ + current_test_name = function_name; + + if (!run_only_named_tests.empty()) + { + bool run_this_test = false; + + for (std::list<std::string>::iterator + i = run_only_named_tests.begin(); + i != run_only_named_tests.end(); i++) + { + if (*i == current_test_name) + { + run_this_test = true; + break; + } + } + + if (!run_this_test) + { + // not in the list, so don't run it. + return false; + } + } + + printf("\n\n== %s ==\n", function_name); + num_tests_selected++; + old_failure_count = num_failures; + + if (original_working_dir == "") + { + char buf[1024]; + if (getcwd(buf, sizeof(buf)) == NULL) + { + BOX_LOG_SYS_ERROR("getcwd"); + } + original_working_dir = buf; + } + else + { + if (chdir(original_working_dir.c_str()) != 0) + { + BOX_LOG_SYS_ERROR("chdir"); + } + } + +#ifdef _MSC_VER + DIR* pDir = opendir("testfiles"); + if(!pDir) + { + THROW_SYS_FILE_ERROR("Failed to open test temporary directory", + "testfiles", CommonException, Internal); + } + struct dirent* pEntry; + for(pEntry = readdir(pDir); pEntry; pEntry = readdir(pDir)) + { + std::string filename = pEntry->d_name; + if(StartsWith("TestDir", filename) || + StartsWith("0_", filename) || + filename == "accounts.txt" || + StartsWith("file", filename) || + StartsWith("notifyran", filename) || + StartsWith("notifyscript.tag", filename) || + StartsWith("restore", filename) || + filename == "bbackupd-data" || + filename == "syncallowscript.control" || + StartsWith("syncallowscript.notifyran.", filename) || + filename == "test2.downloaded" || + EndsWith("testfile", filename)) + { + std::string filepath = std::string("testfiles\\") + filename; + + int filetype = ObjectExists(filepath); + if(filetype == ObjectExists_File) + { + if(::unlink(filepath.c_str()) != 0) + { + TEST_FAIL_WITH_MESSAGE(BOX_SYS_ERROR_MESSAGE("Failed to delete " + "test fixture file: unlink(\"" << filepath << "\")")); + } + } + else if(filetype == ObjectExists_Dir) + { + std::string cmd = "cmd /c rd /s /q " + filepath; + WCHAR* wide_cmd = ConvertUtf8ToWideString(cmd.c_str()); + if(wide_cmd == NULL) + { + TEST_FAIL_WITH_MESSAGE("Failed to convert string " + "to wide string: " << cmd); + continue; + } + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + BOOL result = CreateProcessW( + NULL, // lpApplicationName + wide_cmd, // lpCommandLine + NULL, // lpProcessAttributes + NULL, // lpThreadAttributes + TRUE, // bInheritHandles + 0, // dwCreationFlags + NULL, // lpEnvironment + NULL, // lpCurrentDirectory + &si, // lpStartupInfo + &pi // lpProcessInformation + ); + delete [] wide_cmd; + + if(result == FALSE) + { + TEST_FAIL_WITH_MESSAGE("Failed to delete test " + "fixture file: failed to execute command " + "'" << cmd << "': " << + GetErrorMessage(GetLastError())); + continue; + } + + // Wait until child process exits. + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD exit_code; + result = GetExitCodeProcess(pi.hProcess, &exit_code); + + if(result == FALSE) + { + TEST_FAIL_WITH_MESSAGE("Failed to delete " + "test fixture file: failed to get " + "command exit status: '" << + cmd << "': " << + GetErrorMessage(GetLastError())); + } + else if(exit_code != 0) + { + TEST_FAIL_WITH_MESSAGE("Failed to delete test " + "fixture file: command '" << cmd << "' " + "exited with status " << exit_code); + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + else + { + TEST_FAIL_WITH_MESSAGE("Don't know how to delete file " << filepath << + " of type " << filetype); + } + } + } + closedir(pDir); + FileStream touch("testfiles/accounts.txt", O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); +#else + TEST_THAT_THROWONFAIL(system( + "rm -rf testfiles/TestDir* testfiles/0_0 testfiles/0_1 " + "testfiles/0_2 testfiles/accounts.txt " // testfiles/test* .tgz! + "testfiles/file* testfiles/notifyran testfiles/notifyran.* " + "testfiles/notifyscript.tag* " + "testfiles/restore* testfiles/bbackupd-data " + "testfiles/syncallowscript.control " + "testfiles/syncallowscript.notifyran.* " + "testfiles/test2.downloaded" + ) == 0); + TEST_THAT_THROWONFAIL(system("touch testfiles/accounts.txt") == 0); +#endif + TEST_THAT_THROWONFAIL(mkdir("testfiles/0_0", 0755) == 0); + TEST_THAT_THROWONFAIL(mkdir("testfiles/0_1", 0755) == 0); + TEST_THAT_THROWONFAIL(mkdir("testfiles/0_2", 0755) == 0); + TEST_THAT_THROWONFAIL(mkdir("testfiles/bbackupd-data", 0755) == 0); + + return true; +} + +bool tearDown() +{ + if (num_failures == old_failure_count) + { + BOX_NOTICE(current_test_name << " passed"); + s_test_status[current_test_name] = "passed"; + return true; + } + else + { + BOX_NOTICE(current_test_name << " failed"); \ + s_test_status[current_test_name] = "FAILED"; + return false; + } +} + +bool fail() +{ + num_failures++; + return tearDown(); +} + +int finish_test_suite() +{ + printf("\n"); + printf("Test results:\n"); + + typedef std::map<std::string, std::string>::iterator s_test_status_iterator; + for(s_test_status_iterator i = s_test_status.begin(); + i != s_test_status.end(); i++) + { + BOX_NOTICE("test result: " << i->second << ": " << i->first); + } + + TEST_LINE(num_tests_selected > 0, "No tests matched the patterns " + "specified on the command line"); + + return (num_failures == 0 && num_tests_selected > 0) ? 0 : 1; +} bool TestFileExists(const char *Filename) { @@ -136,7 +365,7 @@ int ReadPidFile(const char *pidFile) if(!TestFileNotEmpty(pidFile)) { TEST_FAIL_WITH_MESSAGE("Server didn't save PID file " - "(perhaps one was already running?)"); + "(perhaps one was already running?)"); return -1; } @@ -145,7 +374,7 @@ int ReadPidFile(const char *pidFile) FILE *f = fopen(pidFile, "r"); if(f == NULL || fscanf(f, "%d", &pid) != 1) { - TEST_FAIL_WITH_MESSAGE("Couldn't read PID file"); + TEST_FAIL_WITH_MESSAGE("Couldn't read PID file"); return -1; } fclose(f); @@ -155,7 +384,7 @@ int ReadPidFile(const char *pidFile) int LaunchServer(const std::string& rCommandLine, const char *pidFile) { - ::fprintf(stdout, "Starting server: %s\n", rCommandLine.c_str()); + BOX_INFO("Starting server: " << rCommandLine); #ifdef WIN32 @@ -189,14 +418,10 @@ int LaunchServer(const std::string& rCommandLine, const char *pidFile) 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"); + TEST_THAT_OR(result != 0, + BOX_LOG_WIN_ERROR("Launch failed: " << rCommandLine); return -1; - } + ); CloseHandle(procInfo.hProcess); CloseHandle(procInfo.hThread); @@ -205,11 +430,10 @@ int LaunchServer(const std::string& rCommandLine, const char *pidFile) #else // !WIN32 - if(RunCommand(rCommandLine) != 0) - { - TEST_FAIL_WITH_MESSAGE("Couldn't start server"); + TEST_THAT_OR(RunCommand(rCommandLine) == 0, + TEST_FAIL_WITH_MESSAGE("Failed to start server: " << rCommandLine); return -1; - } + ) return WaitForServerStartup(pidFile, 0); @@ -230,18 +454,11 @@ int WaitForServerStartup(const char *pidFile, int pidIfKnown) #endif // time for it to start up - if (Logging::GetGlobalLevel() >= Log::TRACE) - { - BOX_TRACE("Waiting for server to start"); - } - else - { - ::fprintf(stdout, "Waiting for server to start: "); - } + BOX_TRACE("Waiting for server to start"); for (int i = 0; i < 15; i++) { - if (TestFileNotEmpty(pidFile)) + if (TestFileNotEmpty(pidFile)) { break; } @@ -251,12 +468,6 @@ int WaitForServerStartup(const char *pidFile, int pidIfKnown) break; } - if (Logging::GetGlobalLevel() < Log::TRACE) - { - ::fprintf(stdout, "."); - ::fflush(stdout); - } - ::sleep(1); } @@ -265,42 +476,17 @@ int WaitForServerStartup(const char *pidFile, int pidIfKnown) if (pidIfKnown && !ServerIsAlive(pidIfKnown)) { - if (Logging::GetGlobalLevel() >= Log::TRACE) - { - BOX_ERROR("server died!"); - } - else - { - ::fprintf(stdout, " server died!\n"); - } - - TEST_FAIL_WITH_MESSAGE("Server died!"); + TEST_FAIL_WITH_MESSAGE("Server died!"); return -1; } if (!TestFileNotEmpty(pidFile)) { - if (Logging::GetGlobalLevel() >= Log::TRACE) - { - BOX_ERROR("timed out!"); - } - else - { - ::fprintf(stdout, " timed out!\n"); - } - - TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); + TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); return -1; } - if (Logging::GetGlobalLevel() >= Log::TRACE) - { - BOX_TRACE("Server started"); - } - else - { - ::fprintf(stdout, " done.\n"); - } + BOX_TRACE("Server started"); // wait a second for the pid to be written to the file ::sleep(1); @@ -330,12 +516,12 @@ void TestRemoteProcessMemLeaksFunc(const char *filename, // Does the file exist? if(!TestFileExists(filename)) { - if (failures == 0) + if (num_failures == 0) { first_fail_file = file; first_fail_line = line; } - ++failures; + ++num_failures; printf("FAILURE: MemLeak report not available (file %s) " "at %s:%d\n", filename, file, line); } @@ -344,12 +530,12 @@ void TestRemoteProcessMemLeaksFunc(const char *filename, // Is it empty? if(TestGetFileSize(filename) > 0) { - if (failures == 0) + if (num_failures == 0) { first_fail_file = file; first_fail_line = line; } - ++failures; + ++num_failures; printf("FAILURE: Memory leaks found in other process " "(file %s) at %s:%d\n==========\n", filename, file, line); @@ -378,23 +564,29 @@ void force_sync() void wait_for_sync_start() { + BOX_TRACE("Waiting for sync to start..."); TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " "wait-for-sync") == 0); TestRemoteProcessMemLeaks("bbackupctl.memleaks"); + BOX_TRACE("Backup daemon reported that sync has started."); } void wait_for_sync_end() { + BOX_TRACE("Waiting for sync to finish..."); TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " "wait-for-end") == 0); TestRemoteProcessMemLeaks("bbackupctl.memleaks"); + BOX_TRACE("Backup daemon reported that sync has finished."); } void sync_and_wait() { + BOX_TRACE("Starting a sync and waiting for it to finish..."); TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " "sync-and-wait") == 0); TestRemoteProcessMemLeaks("bbackupctl.memleaks"); + BOX_TRACE("Backup daemon reported that sync has finished."); } void terminate_bbackupd(int pid) @@ -419,35 +611,14 @@ void terminate_bbackupd(int pid) // Wait a given number of seconds for something to complete void wait_for_operation(int seconds, const char* message) { - if (Logging::GetGlobalLevel() >= Log::TRACE) - { - BOX_TRACE("Waiting " << seconds << " seconds for " << message); - } - else - { - printf("Waiting for %s: ", message); - fflush(stdout); - } + BOX_INFO("Waiting " << seconds << " seconds for " << message); for(int l = 0; l < seconds; ++l) { sleep(1); - if (Logging::GetGlobalLevel() < Log::TRACE) - { - printf("."); - fflush(stdout); - } } - if (Logging::GetGlobalLevel() >= Log::TRACE) - { - BOX_TRACE("Finished waiting for " << message); - } - else - { - printf(" done.\n"); - fflush(stdout); - } + BOX_TRACE("Finished waiting for " << message); } void safe_sleep(int seconds) @@ -455,3 +626,15 @@ void safe_sleep(int seconds) ShortSleep(SecondsToBoxTime(seconds), true); } +std::auto_ptr<Configuration> load_config_file(const std::string& config_file, + const ConfigurationVerify& verify) +{ + std::string errs; + std::auto_ptr<Configuration> config( + Configuration::LoadAndVerify(config_file, &verify, errs)); + TEST_EQUAL_LINE(0, errs.size(), "Failed to load configuration file: " + config_file + + ": " + errs); + TEST_EQUAL_OR(0, errs.size(), config.reset()); + return config; +} + diff --git a/lib/common/Test.h b/lib/common/Test.h index f318c811..4b5cef61 100644 --- a/lib/common/Test.h +++ b/lib/common/Test.h @@ -11,6 +11,10 @@ #define TEST__H #include <cstring> +#include <list> +#include <map> + +#include "Configuration.h" #ifdef WIN32 #define BBACKUPCTL "..\\..\\bin\\bbackupctl\\bbackupctl.exe" @@ -28,30 +32,70 @@ #define TEST_RETURN(actual, expected) TEST_EQUAL((expected << 8), actual); #endif -extern int failures; +extern int num_failures; extern int first_fail_line; +extern int num_tests_selected; +extern int old_failure_count; extern std::string first_fail_file; extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; +extern std::list<std::string> run_only_named_tests; +extern std::string current_test_name; +extern std::map<std::string, std::string> s_test_status; + +//! Simplifies calling setUp() with the current function name in each test. +#define SETUP() \ + if (!setUp(__FUNCTION__)) return true; \ + try \ + { // left open for TEARDOWN() + +#define TEARDOWN() \ + return tearDown(); \ + } \ + catch (BoxException &e) \ + { \ + BOX_NOTICE(__FUNCTION__ << " errored: " << e.what()); \ + num_failures++; \ + tearDown(); \ + s_test_status[__FUNCTION__] = "ERRORED"; \ + return false; \ + } + +//! End the current test. Only use within a test function, because it just returns false! +#define FAIL { \ + std::ostringstream os; \ + os << "failed at " << __FUNCTION__ << ":" << __LINE__; \ + s_test_status[current_test_name] = os.str(); \ + return fail(); \ +} #define TEST_FAIL_WITH_MESSAGE(msg) \ { \ - if (failures == 0) \ + if (num_failures == 0) \ { \ first_fail_file = __FILE__; \ first_fail_line = __LINE__; \ } \ - failures++; \ + num_failures++; \ BOX_ERROR("**** TEST FAILURE: " << msg << " at " << __FILE__ << \ ":" << __LINE__); \ } #define TEST_ABORT_WITH_MESSAGE(msg) {TEST_FAIL_WITH_MESSAGE(msg); return 1;} -#define TEST_THAT(condition) {if(!(condition)) TEST_FAIL_WITH_MESSAGE("Condition [" #condition "] failed")} +#define TEST_THAT_OR(condition, or_command) \ + if(!(condition)) \ + { \ + TEST_FAIL_WITH_MESSAGE("Condition [" #condition "] failed"); \ + or_command; \ + } +#define TEST_THAT(condition) TEST_THAT_OR(condition,) #define TEST_THAT_ABORTONFAIL(condition) {if(!(condition)) TEST_ABORT_WITH_MESSAGE("Condition [" #condition "] failed")} +#define TEST_THAT_THROWONFAIL(condition) \ + TEST_THAT_OR(condition, THROW_EXCEPTION_MESSAGE(CommonException, \ + AssertFailed, "Condition [" #condition "] failed")); // NOTE: The 0- bit is to allow this to work with stuff which has negative constants for flags (eg ConnectionException) -#define TEST_CHECK_THROWS(statement, excepttype, subtype) \ +#define TEST_CHECK_THROWS_AND_OR(statement, excepttype, subtype, and_command, or_command) \ { \ bool didthrow = false; \ HideExceptionMessageGuard hide; \ @@ -69,6 +113,7 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; throw; \ } \ didthrow = true; \ + and_command; \ } \ catch(...) \ { \ @@ -76,12 +121,15 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; } \ if(!didthrow) \ { \ - TEST_FAIL_WITH_MESSAGE("Didn't throw exception " #excepttype "(" #subtype ")") \ + TEST_FAIL_WITH_MESSAGE("Didn't throw exception " #excepttype "(" #subtype ")"); \ + or_command; \ } \ } +#define TEST_CHECK_THROWS(statement, excepttype, subtype) \ + TEST_CHECK_THROWS_AND_OR(statement, excepttype, subtype,,) // utility macro for comparing two strings in a line -#define TEST_EQUAL(_expected, _found) \ +#define TEST_EQUAL_OR(_expected, _found, or_command) \ { \ std::ostringstream _oss1; \ _oss1 << _expected; \ @@ -100,8 +148,11 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; _oss3 << #_found << " != " << #_expected; \ \ TEST_FAIL_WITH_MESSAGE(_oss3.str().c_str()); \ + or_command; \ } \ } +#define TEST_EQUAL(_expected, _found) \ + TEST_EQUAL_OR(_expected, _found,) // utility macro for comparing two strings in a line #define TEST_EQUAL_LINE(_expected, _found, _line) \ @@ -129,7 +180,7 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; } \ } -// utility macro for testing a line +// utility macros for testing a string/output line #define TEST_LINE(_condition, _line) \ TEST_THAT(_condition); \ if (!(_condition)) \ @@ -140,6 +191,28 @@ extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; printf("Test failed on <%s>\n", _line_str.c_str()); \ } +#define TEST_LINE_OR(_condition, _line, _or_command) \ + TEST_LINE(_condition, _line); \ + if(!(_condition)) \ + { \ + _or_command; \ + } + +#define TEST_STARTSWITH(expected, actual) \ + TEST_EQUAL_LINE(expected, actual.substr(0, std::string(expected).size()), actual); + +//! Sets up (cleans up) test environment at the start of every test. +bool setUp(const char* function_name); + +//! Checks account for errors and shuts down daemons at end of every test. +bool tearDown(); + +//! Like tearDown() but returns false, because a test failure was detected. +bool fail(); + +//! Report final status of all tests, and return the correct value to test main(). +int finish_test_suite(); + bool TestFileExists(const char *Filename); bool TestDirExists(const char *Filename); @@ -167,5 +240,17 @@ void terminate_bbackupd(int pid); // Wait a given number of seconds for something to complete void wait_for_operation(int seconds, const char* message); void safe_sleep(int seconds); +std::auto_ptr<Configuration> load_config_file(const std::string& config_file, + const ConfigurationVerify& verify); + +#ifndef TEST_EXECUTABLE +# ifdef _MSC_VER + // Our CMakeFiles compile tests to different executable filenames, + // e.g. test_common.exe instead of _test.exe. + #define TEST_EXECUTABLE BOX_MODULE ".exe" +# else + #define TEST_EXECUTABLE "./_test" +# endif +#endif // TEST_EXECUTABLE #endif // TEST__H diff --git a/lib/common/Timer.cpp b/lib/common/Timer.cpp index ad6b5e8d..4f8c989e 100644 --- a/lib/common/Timer.cpp +++ b/lib/common/Timer.cpp @@ -55,8 +55,8 @@ void Timers::Init() sigemptyset(&newact.sa_mask); if (::sigaction(SIGALRM, &newact, &oldact) != 0) { - BOX_ERROR("Failed to install signal handler"); - THROW_EXCEPTION(CommonException, Internal); + THROW_SYS_ERROR("Failed to install signal handler", + CommonException, Internal); } ASSERT(oldact.sa_handler == 0); #endif // WIN32 && !PLATFORM_CYGWIN @@ -72,13 +72,23 @@ void Timers::Init() // Created: 6/11/2006 // // -------------------------------------------------------------------------- -void Timers::Cleanup() +void Timers::Cleanup(bool throw_exception_if_not_initialised) { - ASSERT(spTimers); - if (!spTimers) + if (throw_exception_if_not_initialised) { - BOX_ERROR("Tried to clean up timers when not initialised!"); - return; + ASSERT(spTimers); + if (!spTimers) + { + BOX_ERROR("Tried to clean up timers when not initialised!"); + return; + } + } + else + { + if (!spTimers) + { + return; + } } #if defined WIN32 && ! defined PLATFORM_CYGWIN @@ -87,8 +97,11 @@ void Timers::Cleanup() struct itimerval timeout; memset(&timeout, 0, sizeof(timeout)); - int result = ::setitimer(ITIMER_REAL, &timeout, NULL); - ASSERT(result == 0); + if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0) + { + THROW_SYS_ERROR("Failed to set interval timer", + CommonException, Internal); + } struct sigaction newact, oldact; newact.sa_handler = SIG_DFL; @@ -96,8 +109,8 @@ void Timers::Cleanup() sigemptyset(&(newact.sa_mask)); if (::sigaction(SIGALRM, &newact, &oldact) != 0) { - BOX_ERROR("Failed to remove signal handler"); - THROW_EXCEPTION(CommonException, Internal); + THROW_SYS_ERROR("Failed to remove signal handler", + CommonException, Internal); } ASSERT(oldact.sa_handler == Timers::SignalHandler); #endif // WIN32 && !PLATFORM_CYGWIN @@ -118,7 +131,6 @@ void Timers::Cleanup() void Timers::Add(Timer& rTimer) { ASSERT(spTimers); - ASSERT(&rTimer); BOX_TRACE(TIMER_ID_OF(rTimer) " added to global queue, rescheduling"); spTimers->push_back(&rTimer); Reschedule(); @@ -135,8 +147,14 @@ void Timers::Add(Timer& rTimer) // -------------------------------------------------------------------------- void Timers::Remove(Timer& rTimer) { + if(!spTimers) + { + BOX_WARNING(TIMER_ID_OF(rTimer) " was still active after " + "timer subsystem was cleaned up, already removed."); + return; + } + ASSERT(spTimers); - ASSERT(&rTimer); BOX_TRACE(TIMER_ID_OF(rTimer) " removed from global queue, rescheduling"); bool restart = true; @@ -155,7 +173,7 @@ void Timers::Remove(Timer& rTimer) } } } - + Reschedule(); } @@ -185,26 +203,24 @@ void Timers::Reschedule() ASSERT(spTimers); if (spTimers == NULL) { - THROW_EXCEPTION(CommonException, Internal) + THROW_EXCEPTION(CommonException, TimersNotInitialised); } #ifndef WIN32 struct sigaction oldact; if (::sigaction(SIGALRM, NULL, &oldact) != 0) { - BOX_ERROR("Failed to check signal handler"); - THROW_EXCEPTION(CommonException, Internal) + THROW_SYS_ERROR("Failed to check signal handler", + CommonException, Internal); } ASSERT(oldact.sa_handler == Timers::SignalHandler); if (oldact.sa_handler != Timers::SignalHandler) { - BOX_ERROR("Signal handler was " << - (void *)oldact.sa_handler << - ", expected " << - (void *)Timers::SignalHandler); - THROW_EXCEPTION(CommonException, Internal) + THROW_EXCEPTION_MESSAGE(CommonException, Internal, + "Signal handler was " << (void *)oldact.sa_handler << + ", expected " << (void *)Timers::SignalHandler); } #endif @@ -218,6 +234,8 @@ void Timers::Reschedule() // win32 timers need no management #else box_time_t timeNow = GetCurrentBoxTime(); + int64_t timeToNextEvent; + std::string nameOfNextEvent; // scan for, trigger and remove expired timers. Removal requires // us to restart the scan each time, due to std::vector semantics. @@ -225,6 +243,7 @@ void Timers::Reschedule() while (restart) { restart = false; + timeToNextEvent = 0; for (std::vector<Timer*>::iterator i = spTimers->begin(); i != spTimers->end(); i++) @@ -252,35 +271,14 @@ void Timers::Reschedule() " seconds"); */ } - } - } - - // Now the only remaining timers should all be in the future. - // 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++) - { - Timer& rTimer = **i; - int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow; - ASSERT(timeToExpiry > 0) - if (timeToExpiry <= 0) - { - timeToExpiry = 1; - } - - if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry) - { - timeToNextEvent = timeToExpiry; - nameOfNextEvent = rTimer.GetName(); + if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry) + { + timeToNextEvent = timeToExpiry; + nameOfNextEvent = rTimer.GetName(); + } } } - - ASSERT(timeToNextEvent >= 0); if (timeToNextEvent == 0) { @@ -302,8 +300,8 @@ void Timers::Reschedule() if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0) { - BOX_ERROR("Failed to initialise system timer\n"); - THROW_EXCEPTION(CommonException, Internal) + THROW_SYS_ERROR("Failed to initialise system timer", + CommonException, Internal); } #endif } @@ -322,7 +320,6 @@ void Timers::Reschedule() // -------------------------------------------------------------------------- void Timers::SignalHandler(int unused) { - // ASSERT(spTimers); Timers::RequestReschedule(); } diff --git a/lib/common/Timer.h b/lib/common/Timer.h index 09be58fa..17233203 100644 --- a/lib/common/Timer.h +++ b/lib/common/Timer.h @@ -43,7 +43,7 @@ class Timers public: static void Init(); - static void Cleanup(); + static void Cleanup(bool throw_exception_if_not_initialised = true); static void Add (Timer& rTimer); static void Remove(Timer& rTimer); static void RequestReschedule(); diff --git a/lib/common/Utils.cpp b/lib/common/Utils.cpp index decc80e8..0915f29a 100644 --- a/lib/common/Utils.cpp +++ b/lib/common/Utils.cpp @@ -15,7 +15,7 @@ #include <cstdlib> -#ifdef SHOW_BACKTRACE_ON_EXCEPTION +#ifdef HAVE_EXECINFO_H #include <execinfo.h> #include <stdlib.h> #endif @@ -51,25 +51,40 @@ std::string GetBoxBackupVersion() // Created: 2003/07/31 // // -------------------------------------------------------------------------- -void SplitString(const std::string &String, char SplitOn, std::vector<std::string> &rOutput) +void SplitString(std::string String, char SplitOn, std::vector<std::string> &rOutput) { // Split it up. - std::string::size_type b = 0; - std::string::size_type e = 0; - while(e = String.find_first_of(SplitOn, b), e != String.npos) + std::string::size_type begin = 0, end = 0, pos = 0; + + while(end = String.find_first_of(SplitOn, pos), end != String.npos) { - // Get this string - unsigned int len = e - b; - if(len >= 1) + // Is it preceded by the escape character? + if(end > 0 && String[end - 1] == '\\') + { + // Ignore this one, don't change begin, let the next + // match/fallback consume it instead. But remove the + // backslash from the string, and set pos to the + // current position, which no longer contains a + // separator character. + String.erase(end - 1, 1); + pos = end; + } + else { - rOutput.push_back(String.substr(b, len)); + // Extract the substring and move past it. + unsigned int len = end - begin; + if(len >= 1) + { + rOutput.push_back(String.substr(begin, len)); + } + begin = end + 1; + pos = begin; } - b = e + 1; } // Last string - if(b < String.size()) + if(begin < String.size()) { - rOutput.push_back(String.substr(b)); + rOutput.push_back(String.substr(begin)); } /*#ifndef BOX_RELEASE_BUILD BOX_TRACE("Splitting string '" << String << " on " << (char)SplitOn); @@ -80,71 +95,95 @@ void SplitString(const std::string &String, char SplitOn, std::vector<std::strin #endif*/ } -#ifdef SHOW_BACKTRACE_ON_EXCEPTION +bool StartsWith(const std::string& prefix, const std::string& haystack) +{ + return haystack.size() >= prefix.size() && + haystack.substr(0, prefix.size()) == prefix; +} + +bool EndsWith(const std::string& suffix, const std::string& haystack) +{ + return haystack.size() >= suffix.size() && + haystack.substr(haystack.size() - suffix.size()) == suffix; +} + +std::string RemovePrefix(const std::string& prefix, const std::string& haystack) +{ + if(StartsWith(prefix, haystack)) + { + return haystack.substr(prefix.size()); + } + else + { + return ""; + } +} + +std::string RemoveSuffix(const std::string& suffix, const std::string& haystack) +{ + if(EndsWith(suffix, haystack)) + { + return haystack.substr(0, haystack.size() - suffix.size()); + } + else + { + return ""; + } +} + static std::string demangle(const std::string& mangled_name) { + std::string demangled_name = mangled_name; + #ifdef HAVE_CXXABI_H + char buffer[1024]; int status; + size_t length = sizeof(buffer); -#include "MemLeakFindOff.h" char* result = abi::__cxa_demangle(mangled_name.c_str(), - NULL, NULL, &status); -#include "MemLeakFindOn.h" + buffer, &length, &status); - if (result == NULL) + if (status == 0) { - if (status == 0) - { - BOX_WARNING("Demangle failed but no error: " << - mangled_name); - } - else if (status == -1) - { - BOX_WARNING("Demangle failed with " - "memory allocation error: " << - mangled_name); - } - else if (status == -2) - { - // Probably non-C++ name, don't demangle - /* - BOX_WARNING("Demangle failed with " - "with invalid name: " << - mangled_name); - */ - } - else if (status == -3) - { - BOX_WARNING("Demangle failed with " - "with invalid argument: " << - mangled_name); - } - else - { - BOX_WARNING("Demangle failed with " - "with unknown error " << status << - ": " << mangled_name); - } - - return std::string(mangled_name); + demangled_name = result; + } + else if (status == -1) + { + BOX_WARNING("Demangle failed with " + "memory allocation error: " << + mangled_name); + } + else if (status == -2) + { + // Probably non-C++ name, don't demangle + /* + BOX_WARNING("Demangle failed with " + "with invalid name: " << + mangled_name); + */ + } + else if (status == -3) + { + BOX_WARNING("Demangle failed with " + "with invalid argument: " << + mangled_name); } else { - std::string output = result; -#include "MemLeakFindOff.h" - free(result); -#include "MemLeakFindOn.h" - return output; + BOX_WARNING("Demangle failed with " + "with unknown error " << status << + ": " << mangled_name); } - #else // !HAVE_CXXABI_H - return mangled_name; #endif // HAVE_CXXABI_H + + return demangled_name; } void DumpStackBacktrace() { - void *array[10]; - size_t size = backtrace(array, 10); +#ifdef HAVE_EXECINFO_H + void *array[20]; + size_t size = backtrace(array, 20); BOX_TRACE("Obtained " << size << " stack frames."); for(size_t i = 0; i < size; i++) @@ -179,8 +218,10 @@ void DumpStackBacktrace() BOX_TRACE(output.str()); } +#else // !HAVE_EXECINFO_H + BOX_TRACE("Backtrace support was not compiled in"); +#endif // HAVE_EXECINFO_H } -#endif // SHOW_BACKTRACE_ON_EXCEPTION @@ -340,30 +381,3 @@ std::string FormatUsageLineStart(const std::string& rName, return result.str(); } -std::string BoxGetTemporaryDirectoryName() -{ -#ifdef WIN32 - // http://msdn.microsoft.com/library/default.asp? - // url=/library/en-us/fileio/fs/creating_and_using_a_temporary_file.asp - - DWORD dwRetVal; - char lpPathBuffer[1024]; - DWORD dwBufSize = sizeof(lpPathBuffer); - - // Get the temp path. - dwRetVal = GetTempPath(dwBufSize, // length of the buffer - lpPathBuffer); // buffer for path - if (dwRetVal > dwBufSize) - { - THROW_EXCEPTION(CommonException, TempDirPathTooLong) - } - - return std::string(lpPathBuffer); -#elif defined TEMP_DIRECTORY_NAME - return std::string(TEMP_DIRECTORY_NAME); -#else - #error non-static temporary directory names not supported yet -#endif -} - - diff --git a/lib/common/Utils.h b/lib/common/Utils.h index 3134245a..d306ce1c 100644 --- a/lib/common/Utils.h +++ b/lib/common/Utils.h @@ -17,11 +17,13 @@ std::string GetBoxBackupVersion(); -void SplitString(const std::string &String, char SplitOn, std::vector<std::string> &rOutput); +void SplitString(std::string String, char SplitOn, std::vector<std::string> &rOutput); +bool StartsWith(const std::string& prefix, const std::string& haystack); +bool EndsWith(const std::string& prefix, const std::string& haystack); +std::string RemovePrefix(const std::string& prefix, const std::string& haystack); +std::string RemoveSuffix(const std::string& suffix, const std::string& haystack); -#ifdef SHOW_BACKTRACE_ON_EXCEPTION - void DumpStackBacktrace(); -#endif +void DumpStackBacktrace(); bool FileExists(const std::string& rFilename, int64_t *pFileSize = 0, bool TreatLinksAsNotExisting = false); diff --git a/lib/common/ZeroStream.cpp b/lib/common/ZeroStream.cpp index d11ed80c..e1342e6f 100644 --- a/lib/common/ZeroStream.cpp +++ b/lib/common/ZeroStream.cpp @@ -76,7 +76,7 @@ IOStream::pos_type ZeroStream::BytesLeftToRead() // Created: 2003/07/31 // // -------------------------------------------------------------------------- -void ZeroStream::Write(const void *pBuffer, int NBytes) +void ZeroStream::Write(const void *pBuffer, int NBytes, int Timeout) { THROW_EXCEPTION(CommonException, NotSupported); } diff --git a/lib/common/ZeroStream.h b/lib/common/ZeroStream.h index 0119045b..f91221b0 100644 --- a/lib/common/ZeroStream.h +++ b/lib/common/ZeroStream.h @@ -22,7 +22,8 @@ public: virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); virtual pos_type BytesLeftToRead(); - virtual void Write(const void *pBuffer, int NBytes); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); virtual pos_type GetPosition() const; virtual void Seek(IOStream::pos_type Offset, int SeekType); virtual void Close(); diff --git a/lib/common/makeexception.pl.in b/lib/common/makeexception.pl.in index b1b3a8ac..bddaa94a 100755 --- a/lib/common/makeexception.pl.in +++ b/lib/common/makeexception.pl.in @@ -73,12 +73,13 @@ class ${class}Exception : public BoxException public: ${class}Exception(unsigned int SubType, const std::string& rMessage = "") - : mSubType(SubType), mMessage(rMessage) + : mSubType(SubType), mMessage(rMessage), + mWhat(GetMessage(SubType) + std::string(rMessage.empty() ? "" : ": ") + rMessage) { } ${class}Exception(const ${class}Exception &rToCopy) - : mSubType(rToCopy.mSubType), mMessage(rToCopy.mMessage) + : mSubType(rToCopy.mSubType), mMessage(rToCopy.mMessage), mWhat(rToCopy.mWhat) { } @@ -113,10 +114,11 @@ print H <<__E; { return mMessage; } - + static const char* GetMessage(int SubType); private: unsigned int mSubType; std::string mMessage; + std::string mWhat; }; #endif // $guardname @@ -133,74 +135,39 @@ print CPP <<__E; #include "MemLeakFindOn.h" -#ifdef EXCEPTION_CODENAMES_EXTENDED - #ifdef EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION -static const char *whats[] = { -__E - -my $last_seen = -1; -for(my $e = 0; $e <= $#exception; $e++) +unsigned int ${class}Exception::GetType() const throw() { - if($exception[$e] ne '') - { - for(my $s = $last_seen + 1; $s < $e; $s++) - { - print CPP "\t\"UNUSED\",\n" - } - my $ext = ($exception_desc[$e] ne '')?" ($exception_desc[$e])":''; - print CPP "\t\"${class} ".$exception[$e].$ext.'"'.(($e==$#exception)?'':',')."\n"; - $last_seen = $e; - } + return ${class}Exception::ExceptionType; } -print CPP <<__E; -}; - #else -static const char *whats[] = { -__E - -$last_seen = -1; -for(my $e = 0; $e <= $#exception; $e++) +unsigned int ${class}Exception::GetSubType() const throw() { - if($exception[$e] ne '') - { - for(my $s = $last_seen + 1; $s < $e; $s++) - { - print CPP "\t\"UNUSED\",\n" - } - print CPP "\t\"${class} ".$exception[$e].'"'.(($e==$#exception)?'':',')."\n"; - $last_seen = $e; - } + return mSubType; } -print CPP <<__E; -}; - #endif -#endif - -unsigned int ${class}Exception::GetType() const throw() +const char * ${class}Exception::what() const throw() { - return ${class}Exception::ExceptionType; + return mWhat.c_str(); } -unsigned int ${class}Exception::GetSubType() const throw() +const char * ${class}Exception::GetMessage(int SubType) { - return mSubType; -} + switch(SubType) + { +__E -const char *${class}Exception::what() const throw() +for(my $e = 0; $e <= $#exception; $e++) { -#ifdef EXCEPTION_CODENAMES_EXTENDED - if(mSubType > (sizeof(whats) / sizeof(whats[0]))) + if($exception[$e] ne '') { - return "${class}"; + print CPP "\t\tcase ".$exception[$e].': return "'.$exception[$e].'";'."\n"; } - return whats[mSubType]; -#else - return "${class}"; -#endif } +print CPP <<__E; + default: return "Unknown"; + } +} __E close H; |