summaryrefslogtreecommitdiff
path: root/lib/common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common')
-rw-r--r--lib/common/Archive.h92
-rw-r--r--lib/common/BannerText.h2
-rw-r--r--lib/common/Box.h20
-rw-r--r--lib/common/BoxConfig-MSVC.h6
-rw-r--r--lib/common/BoxPlatform.h82
-rw-r--r--lib/common/BoxTime.cpp52
-rw-r--r--lib/common/BoxTime.h4
-rw-r--r--lib/common/BufferedStream.cpp6
-rw-r--r--lib/common/BufferedStream.h7
-rw-r--r--lib/common/BufferedWriteStream.cpp2
-rw-r--r--lib/common/BufferedWriteStream.h3
-rw-r--r--lib/common/CollectInBufferStream.cpp2
-rw-r--r--lib/common/CollectInBufferStream.h23
-rw-r--r--lib/common/CommonException.txt2
-rw-r--r--lib/common/Configuration.cpp16
-rw-r--r--lib/common/Configuration.h9
-rw-r--r--lib/common/DebugMemLeakFinder.cpp98
-rw-r--r--lib/common/EventWatchFilesystemObject.cpp112
-rw-r--r--lib/common/EventWatchFilesystemObject.h48
-rw-r--r--lib/common/ExcludeList.cpp15
-rw-r--r--lib/common/FileModificationTime.cpp14
-rw-r--r--lib/common/FileStream.cpp31
-rw-r--r--lib/common/FileStream.h10
-rw-r--r--lib/common/Guards.h29
-rw-r--r--lib/common/IOStream.cpp33
-rw-r--r--lib/common/IOStream.h18
-rw-r--r--lib/common/IOStreamGetLine.h14
-rw-r--r--lib/common/InvisibleTempFileStream.cpp5
-rw-r--r--lib/common/InvisibleTempFileStream.h2
-rw-r--r--lib/common/Logging.cpp294
-rw-r--r--lib/common/Logging.h388
-rw-r--r--lib/common/MainHelper.h19
-rw-r--r--lib/common/MemBlockStream.cpp22
-rw-r--r--lib/common/MemBlockStream.h9
-rw-r--r--lib/common/MemLeakFindOn.h1
-rw-r--r--lib/common/MemLeakFinder.h5
-rw-r--r--lib/common/NamedLock.cpp223
-rw-r--r--lib/common/NamedLock.h11
-rw-r--r--lib/common/PartialReadStream.cpp2
-rw-r--r--lib/common/PartialReadStream.h3
-rw-r--r--lib/common/RateLimitingStream.h5
-rw-r--r--lib/common/ReadGatherStream.cpp2
-rw-r--r--lib/common/ReadGatherStream.h3
-rw-r--r--lib/common/ReadLoggingStream.cpp2
-rw-r--r--lib/common/ReadLoggingStream.h5
-rw-r--r--lib/common/SelfFlushingStream.h11
-rw-r--r--lib/common/StreamableMemBlock.cpp12
-rw-r--r--lib/common/Test.cpp351
-rw-r--r--lib/common/Test.h101
-rw-r--r--lib/common/Timer.cpp99
-rw-r--r--lib/common/Timer.h2
-rw-r--r--lib/common/Utils.cpp190
-rw-r--r--lib/common/Utils.h10
-rw-r--r--lib/common/ZeroStream.cpp2
-rw-r--r--lib/common/ZeroStream.h3
-rwxr-xr-xlib/common/makeexception.pl.in77
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;