From 8a3db62b58d33f95ba5ca55d998876be8dce79a4 Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Sun, 10 May 2020 18:55:55 -0400 Subject: Import boxbackup_0.13~~git20200326.g8e8b63c.orig.tar.xz [dgit import orig boxbackup_0.13~~git20200326.g8e8b63c.orig.tar.xz] --- lib/common/Archive.h | 230 ++++++++ lib/common/BannerText.cpp | 14 + lib/common/BannerText.h | 61 +++ lib/common/BeginStructPackForWire.h | 23 + lib/common/Box.h | 201 +++++++ lib/common/BoxConfig-MSVC.h | 399 ++++++++++++++ lib/common/BoxException.cpp | 21 + lib/common/BoxException.h | 42 ++ lib/common/BoxPlatform.h | 169 ++++++ lib/common/BoxPortsAndFiles.h.in | 51 ++ lib/common/BoxTime.cpp | 159 ++++++ lib/common/BoxTime.h | 53 ++ lib/common/BoxTimeToText.cpp | 76 +++ lib/common/BoxTimeToText.h | 19 + lib/common/BoxTimeToUnix.h | 34 ++ lib/common/BufferedStream.cpp | 209 ++++++++ lib/common/BufferedStream.h | 48 ++ lib/common/BufferedWriteStream.cpp | 181 +++++++ lib/common/BufferedWriteStream.h | 45 ++ lib/common/CollectInBufferStream.cpp | 274 ++++++++++ lib/common/CollectInBufferStream.h | 67 +++ lib/common/CommonException.h | 17 + lib/common/CommonException.txt | 59 +++ lib/common/Configuration.cpp | 933 +++++++++++++++++++++++++++++++++ lib/common/Configuration.h | 157 ++++++ lib/common/Conversion.h | 98 ++++ lib/common/ConversionException.txt | 8 + lib/common/ConversionString.cpp | 129 +++++ lib/common/Database.h | 31 ++ lib/common/DebugAssertFailed.cpp | 37 ++ lib/common/DebugMemLeakFinder.cpp | 730 ++++++++++++++++++++++++++ lib/common/DebugPrintf.cpp | 83 +++ lib/common/EndStructPackForWire.h | 23 + lib/common/ExcludeList.cpp | 480 +++++++++++++++++ lib/common/ExcludeList.h | 76 +++ lib/common/FdGetLine.cpp | 142 +++++ lib/common/FdGetLine.h | 50 ++ lib/common/FileModificationTime.cpp | 70 +++ lib/common/FileModificationTime.h | 22 + lib/common/FileStream.cpp | 465 ++++++++++++++++ lib/common/FileStream.h | 73 +++ lib/common/GetLine.cpp | 176 +++++++ lib/common/GetLine.h | 67 +++ lib/common/Guards.h | 147 ++++++ lib/common/IOStream.cpp | 274 ++++++++++ lib/common/IOStream.h | 73 +++ lib/common/IOStreamGetLine.cpp | 127 +++++ lib/common/IOStreamGetLine.h | 67 +++ lib/common/InvisibleTempFileStream.cpp | 40 ++ lib/common/InvisibleTempFileStream.h | 35 ++ lib/common/Logging.cpp | 766 +++++++++++++++++++++++++++ lib/common/Logging.h | 698 ++++++++++++++++++++++++ lib/common/MainHelper.h | 56 ++ lib/common/Makefile.extra | 11 + lib/common/MemBlockStream.cpp | 268 ++++++++++ lib/common/MemBlockStream.h | 60 +++ lib/common/MemLeakFindOff.h | 27 + lib/common/MemLeakFindOn.h | 26 + lib/common/MemLeakFinder.h | 68 +++ lib/common/NamedLock.cpp | 299 +++++++++++ lib/common/NamedLock.h | 50 ++ lib/common/PartialReadStream.cpp | 138 +++++ lib/common/PartialReadStream.h | 47 ++ lib/common/PathUtils.cpp | 34 ++ lib/common/PathUtils.h | 26 + lib/common/RateLimitingStream.cpp | 95 ++++ lib/common/RateLimitingStream.h | 72 +++ lib/common/ReadGatherStream.cpp | 263 ++++++++++ lib/common/ReadGatherStream.h | 68 +++ lib/common/ReadLoggingStream.cpp | 203 +++++++ lib/common/ReadLoggingStream.h | 59 +++ lib/common/SelfFlushingStream.h | 78 +++ lib/common/StreamableMemBlock.cpp | 371 +++++++++++++ lib/common/StreamableMemBlock.h | 72 +++ lib/common/Test.cpp | 518 ++++++++++++++++++ lib/common/Test.h | 260 +++++++++ lib/common/Timer.cpp | 648 +++++++++++++++++++++++ lib/common/Timer.h | 92 ++++ lib/common/UnixUser.cpp | 123 +++++ lib/common/UnixUser.h | 37 ++ lib/common/Utils.cpp | 383 ++++++++++++++ lib/common/Utils.h | 48 ++ lib/common/WaitForEvent.cpp | 197 +++++++ lib/common/WaitForEvent.h | 152 ++++++ lib/common/ZeroStream.cpp | 170 ++++++ lib/common/ZeroStream.h | 40 ++ lib/common/makeexception.pl.in | 250 +++++++++ 87 files changed, 13838 insertions(+) create mode 100644 lib/common/Archive.h create mode 100644 lib/common/BannerText.cpp create mode 100644 lib/common/BannerText.h create mode 100644 lib/common/BeginStructPackForWire.h create mode 100644 lib/common/Box.h create mode 100644 lib/common/BoxConfig-MSVC.h create mode 100644 lib/common/BoxException.cpp create mode 100644 lib/common/BoxException.h create mode 100644 lib/common/BoxPlatform.h create mode 100644 lib/common/BoxPortsAndFiles.h.in create mode 100644 lib/common/BoxTime.cpp create mode 100644 lib/common/BoxTime.h create mode 100644 lib/common/BoxTimeToText.cpp create mode 100644 lib/common/BoxTimeToText.h create mode 100644 lib/common/BoxTimeToUnix.h create mode 100644 lib/common/BufferedStream.cpp create mode 100644 lib/common/BufferedStream.h create mode 100644 lib/common/BufferedWriteStream.cpp create mode 100644 lib/common/BufferedWriteStream.h create mode 100644 lib/common/CollectInBufferStream.cpp create mode 100644 lib/common/CollectInBufferStream.h create mode 100644 lib/common/CommonException.h create mode 100644 lib/common/CommonException.txt create mode 100644 lib/common/Configuration.cpp create mode 100644 lib/common/Configuration.h create mode 100644 lib/common/Conversion.h create mode 100644 lib/common/ConversionException.txt create mode 100644 lib/common/ConversionString.cpp create mode 100644 lib/common/Database.h create mode 100644 lib/common/DebugAssertFailed.cpp create mode 100644 lib/common/DebugMemLeakFinder.cpp create mode 100644 lib/common/DebugPrintf.cpp create mode 100644 lib/common/EndStructPackForWire.h create mode 100644 lib/common/ExcludeList.cpp create mode 100644 lib/common/ExcludeList.h create mode 100644 lib/common/FdGetLine.cpp create mode 100644 lib/common/FdGetLine.h create mode 100644 lib/common/FileModificationTime.cpp create mode 100644 lib/common/FileModificationTime.h create mode 100644 lib/common/FileStream.cpp create mode 100644 lib/common/FileStream.h create mode 100644 lib/common/GetLine.cpp create mode 100644 lib/common/GetLine.h create mode 100644 lib/common/Guards.h create mode 100644 lib/common/IOStream.cpp create mode 100644 lib/common/IOStream.h create mode 100644 lib/common/IOStreamGetLine.cpp create mode 100644 lib/common/IOStreamGetLine.h create mode 100644 lib/common/InvisibleTempFileStream.cpp create mode 100644 lib/common/InvisibleTempFileStream.h create mode 100644 lib/common/Logging.cpp create mode 100644 lib/common/Logging.h create mode 100644 lib/common/MainHelper.h create mode 100644 lib/common/Makefile.extra create mode 100644 lib/common/MemBlockStream.cpp create mode 100644 lib/common/MemBlockStream.h create mode 100644 lib/common/MemLeakFindOff.h create mode 100644 lib/common/MemLeakFindOn.h create mode 100644 lib/common/MemLeakFinder.h create mode 100644 lib/common/NamedLock.cpp create mode 100644 lib/common/NamedLock.h create mode 100644 lib/common/PartialReadStream.cpp create mode 100644 lib/common/PartialReadStream.h create mode 100644 lib/common/PathUtils.cpp create mode 100644 lib/common/PathUtils.h create mode 100644 lib/common/RateLimitingStream.cpp create mode 100644 lib/common/RateLimitingStream.h create mode 100644 lib/common/ReadGatherStream.cpp create mode 100644 lib/common/ReadGatherStream.h create mode 100644 lib/common/ReadLoggingStream.cpp create mode 100644 lib/common/ReadLoggingStream.h create mode 100644 lib/common/SelfFlushingStream.h create mode 100644 lib/common/StreamableMemBlock.cpp create mode 100644 lib/common/StreamableMemBlock.h create mode 100644 lib/common/Test.cpp create mode 100644 lib/common/Test.h create mode 100644 lib/common/Timer.cpp create mode 100644 lib/common/Timer.h create mode 100644 lib/common/UnixUser.cpp create mode 100644 lib/common/UnixUser.h create mode 100644 lib/common/Utils.cpp create mode 100644 lib/common/Utils.h create mode 100644 lib/common/WaitForEvent.cpp create mode 100644 lib/common/WaitForEvent.h create mode 100644 lib/common/ZeroStream.cpp create mode 100644 lib/common/ZeroStream.h create mode 100755 lib/common/makeexception.pl.in (limited to 'lib/common') diff --git a/lib/common/Archive.h b/lib/common/Archive.h new file mode 100644 index 00000000..2b27b303 --- /dev/null +++ b/lib/common/Archive.h @@ -0,0 +1,230 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Archive.h +// Purpose: Backup daemon state archive +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- + +#ifndef ARCHIVE__H +#define ARCHIVE__H + +#include +#include +#include + +#include "IOStream.h" +#include "Guards.h" + +#define ARCHIVE_GET_SIZE(hdr) (( ((uint8_t)((hdr)[0])) | ( ((uint8_t)((hdr)[1])) << 8)) >> 2) + +#define ARCHIVE_MAGIC_VALUE_RECURSE 0x4449525F +#define ARCHIVE_MAGIC_VALUE_NOOP 0x5449525F + +class Archive +{ +public: + Archive(IOStream &Stream, int Timeout) + : mrStream(Stream) + { + mTimeout = Timeout; + } +private: + // no copying + Archive(const Archive &); + Archive & operator=(const Archive &); +public: + ~Archive() + { + } + + // + // + // + void Write(bool Item) + { + 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), mTimeout); + } + void Write(int64_t Item) + { + int64_t privItem = box_hton64(Item); + 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), mTimeout); + } + void Write(uint8_t Item) + { + int privItem = Item; + Write(privItem); + } + void Write(const std::string &Item) + { + int size = Item.size(); + Write(size); + mrStream.Write(Item.c_str(), size, mTimeout); + } + // + // + // + void Read(bool &rItemOut) + { + int privItem; + Read(privItem); + + if(privItem) + { + rItemOut = true; + } + else + { + rItemOut = false; + } + } + void ReadIfPresent(bool &rItemOut, bool ValueIfNotPresent) + { + int privItem; + ReadIfPresent(privItem, ValueIfNotPresent ? 1 : 0); + rItemOut = privItem ? true : false; + } + void ReadExact(uint32_t &rItemOut) { Read((int&)rItemOut); } + void Read(int &rItemOut) + { + int32_t privItem; + if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), + 0 /* not interested in bytes read if this fails */, + mTimeout)) + { + 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, mTimeout)) + { + rItemOut = ntohl(privItem); + } + else if(bytesRead == 0) + { + // item is simply not present + rItemOut = ValueIfNotPresent; + } + else + { + // bad number of remaining bytes + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead); + } + } + void Read(int64_t &rItemOut) + { + int64_t privItem; + ReadFullBuffer(&privItem, sizeof(privItem)); + rItemOut = box_ntoh64(privItem); + } + void ReadExact(uint64_t &rItemOut) { Read(rItemOut); } + void Read(uint64_t &rItemOut) + { + uint64_t privItem; + 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; + 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 + ReadFullBuffer(buf, size); + // assign to this string, storing the header and the extra payload + rItemOut.assign(buf, size); + } + else + { + // Block of memory to hold it + MemoryBlockGuard dataB(size); + char *ppayload = dataB; + + // Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us + ReadFullBuffer(ppayload, size); + // assign to this string, storing the header and the extra pPayload + rItemOut.assign(ppayload, size); + } + } +private: + IOStream &mrStream; + int mTimeout; +}; + +#endif // ARCHIVE__H diff --git a/lib/common/BannerText.cpp b/lib/common/BannerText.cpp new file mode 100644 index 00000000..9ec2c0d7 --- /dev/null +++ b/lib/common/BannerText.cpp @@ -0,0 +1,14 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BannerText.cpp +// Purpose: Banner text for daemons and utilities +// Created: 2018-08-16 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include "BannerText.h" + +#pragma message("Build signature: " BOX_BUILD_SIGNATURE) diff --git a/lib/common/BannerText.h b/lib/common/BannerText.h new file mode 100644 index 00000000..a2e412a8 --- /dev/null +++ b/lib/common/BannerText.h @@ -0,0 +1,61 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BannerText.h +// Purpose: Banner text for daemons and utilities +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef BANNERTEXT__H +#define BANNERTEXT__H + +#ifdef NEED_BOX_VERSION_H +# include "BoxVersion.h" +#endif + +// Already included by BoxPlatform.h: +#include + +// Yes, you need two function macros to stringify an expanded macro value. +// https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor +#define BOX_STRINGIFY_HELPER(x) #x +#define BOX_STRINGIFY(x) BOX_STRINGIFY_HELPER(x) + +// How to identify a 64-bit build: https://stackoverflow.com/a/687902/648162 +#if UINTPTR_MAX == (4294967295U) +# define BOX_BUILD_BITS 32 +#elif UINTPTR_MAX == (18446744073709551615UL) +# define BOX_BUILD_BITS 64 +#else +# pragma message ("UINTPTR_MAX = " BOX_STRINGIFY(UINTPTR_MAX)) +# error Unknown architecture pointer size +#endif + +#ifdef BOX_RELEASE_BUILD +# define BOX_BUILD_TYPE Release +#else +# define BOX_BUILD_TYPE Debug +#endif + +#define STRINGIFY1(x) #x +#define STRINGIFY2(x) STRINGIFY1(x) +#ifdef _MSC_VER +# define BOX_COMPILER "MSVC " STRINGIFY2(_MSC_VER) +#elif defined __GNUC__ +# define BOX_COMPILER "GCC " __VERSION__ +#elif defined __VERSION__ +// It might be an integer, not a string! +# define BOX_COMPILER "Unknown " BOX_STRINGIFY(__VERSION__) +#else +# define BOX_COMPILER "Unknown" +#endif + +#define BOX_BUILD_SIGNATURE BOX_COMPILER " " BOX_STRINGIFY(BOX_BUILD_BITS) "bit " BOX_STRINGIFY(BOX_BUILD_TYPE) + +#define BANNER_TEXT(UtilityName) \ + "Box Backup " UtilityName " v" BOX_VERSION "\n" \ + "(c) Ben Summers and contributors 2003-2020. Build type: " BOX_BUILD_SIGNATURE + +#endif // BANNERTEXT__H + diff --git a/lib/common/BeginStructPackForWire.h b/lib/common/BeginStructPackForWire.h new file mode 100644 index 00000000..e73bb886 --- /dev/null +++ b/lib/common/BeginStructPackForWire.h @@ -0,0 +1,23 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BeginStructPackForWire.h +// Purpose: Begin structure packing for wire +// Created: 25/11/03 +// +// -------------------------------------------------------------------------- + +// No header guard -- this is intentional + +#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS + +#pragma pack(1) + +#else + + logical error -- check BoxPlatform.h and including file + +#endif + + + diff --git a/lib/common/Box.h b/lib/common/Box.h new file mode 100644 index 00000000..5f629790 --- /dev/null +++ b/lib/common/Box.h @@ -0,0 +1,201 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Box.h +// Purpose: Main header file for the Box project +// Created: 2003/07/08 +// +// -------------------------------------------------------------------------- + +#ifndef BOX__H +#define BOX__H + +// Use the same changes as gcc3 for gcc4 +#ifdef PLATFORM_GCC4 + #define PLATFORM_GCC3 +#endif + +#include "BoxPlatform.h" + +#include + +// uncomment this line to enable full memory leak finding on all +// malloc-ed blocks (at least, ones used by the STL) +//#define MEMLEAKFINDER_FULL_MALLOC_MONITORING + +// Show backtraces on exceptions in release builds until further notice +// (they are only logged at TRACE level anyway) +#ifdef HAVE_EXECINFO_H + #define SHOW_BACKTRACE_ON_EXCEPTION +#endif + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION + #include "Utils.h" + #define OPTIONAL_DO_BACKTRACE DumpStackBacktrace(); +#else + #define OPTIONAL_DO_BACKTRACE +#endif + +#include "CommonException.h" +#include "Logging.h" + +#ifndef BOX_RELEASE_BUILD // this is a debug build: + extern bool AssertFailuresToSyslog; + #define ASSERT_FAILS_TO_SYSLOG_ON {AssertFailuresToSyslog = true;} + void BoxDebugAssertFailed(const char *cond, const char *file, int line); + #define ASSERT(cond) \ + { \ + if(!(cond)) \ + { \ + BoxDebugAssertFailed(#cond, __FILE__, __LINE__); \ + THROW_EXCEPTION_MESSAGE(CommonException, \ + AssertFailed, #cond); \ + } \ + } + + // Note that syslog tracing is independent of BoxDebugTraceOn, + // but stdout tracing is not + extern bool BoxDebugTraceToSyslog; + #define TRACE_TO_SYSLOG(x) {BoxDebugTraceToSyslog = x;} + extern bool BoxDebugTraceToStdout; + #define TRACE_TO_STDOUT(x) {BoxDebugTraceToStdout = x;} + + extern bool BoxDebugTraceOn; + int BoxDebug_printf(const char *format, ...); + int BoxDebugTrace(const char *format, ...); + + #ifndef PLATFORM_DISABLE_MEM_LEAK_TESTING + #define BOX_MEMORY_LEAK_TESTING + #endif + + // Exception names + #define EXCEPTION_CODENAMES_EXTENDED +#else // this is a release build: + #define ASSERT_FAILS_TO_SYSLOG_ON + #define ASSERT(cond) + + #define TRACE_TO_SYSLOG(x) {} + #define TRACE_TO_STDOUT(x) {} + + // Box Backup builds release get extra information for exception logging + #define EXCEPTION_CODENAMES_EXTENDED + #define EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION + + #ifdef BOX_MEMORY_LEAK_TESTING + #error BOX_MEMORY_LEAK_TESTING should not already be defined in release builds! + #endif +#endif + +#if defined DEBUG_LEAKS // optionally enable memory leak debugging even in release builds + #ifdef PLATFORM_DISABLE_MEM_LEAK_TESTING + #error Compiling with DEBUG_LEAKS enabled, but not supported on this platform + #else + #define BOX_MEMORY_LEAK_TESTING + #endif +#endif // DEBUG_LEAKS + +#ifdef BOX_MEMORY_LEAK_TESTING + // Memory leak testing + #include "MemLeakFinder.h" + #define DEBUG_NEW new(__FILE__,__LINE__) + #define MEMLEAKFINDER_NOT_A_LEAK(x) memleakfinder_notaleak(x); + #define MEMLEAKFINDER_NO_LEAKS MemLeakSuppressionGuard _guard; + #define MEMLEAKFINDER_INIT memleakfinder_init(); + #define MEMLEAKFINDER_START {memleakfinder_global_enable = true;} + #define MEMLEAKFINDER_STOP {memleakfinder_global_enable = false;} +#else + #define DEBUG_NEW new + #define MEMLEAKFINDER_NOT_A_LEAK(x) + #define MEMLEAKFINDER_NO_LEAKS + #define MEMLEAKFINDER_INIT + #define MEMLEAKFINDER_START + #define MEMLEAKFINDER_STOP +#endif + +#define THROW_EXCEPTION(type, subtype) \ + { \ + if((!HideExceptionMessageGuard::ExceptionsHidden() \ + && !HideSpecificExceptionGuard::IsHidden( \ + type::ExceptionType, type::subtype))) \ + { \ + OPTIONAL_DO_BACKTRACE \ + BOX_WARNING("Exception thrown: " \ + #type "(" #subtype ") " \ + "at " __FILE__ "(" << __LINE__ << ")") \ + } \ + throw type(type::subtype); \ + } + +#define THROW_EXCEPTION_MESSAGE(type, subtype, message) \ + { \ + std::ostringstream _box_throw_line; \ + _box_throw_line << message; \ + if((!HideExceptionMessageGuard::ExceptionsHidden() \ + && !HideSpecificExceptionGuard::IsHidden( \ + type::ExceptionType, type::subtype))) \ + { \ + OPTIONAL_DO_BACKTRACE \ + BOX_WARNING("Exception thrown: " \ + #type "(" #subtype ") (" << \ + _box_throw_line.str() << \ + ") at " __FILE__ ":" << __LINE__) \ + } \ + throw type(type::subtype, _box_throw_line.str()); \ + } + +// extra macros for converting to network byte order +#ifdef HAVE_NETINET_IN_H + #include +#endif + +// Always define a swap64 function, as it's useful. +inline uint64_t box_swap64(uint64_t x) +{ + return ((x & 0xff) << 56 | + (x & 0xff00LL) << 40 | + (x & 0xff0000LL) << 24 | + (x & 0xff000000LL) << 8 | + (x & 0xff00000000LL) >> 8 | + (x & 0xff0000000000LL) >> 24 | + (x & 0xff000000000000LL) >> 40 | + (x & 0xff00000000000000LL) >> 56); +} + +#ifdef WORDS_BIGENDIAN + #define box_hton64(x) (x) + #define box_ntoh64(x) (x) +#elif defined(HAVE_BSWAP64) + #ifdef HAVE_SYS_ENDIAN_H + #include + #endif + #ifdef HAVE_ASM_BYTEORDER_H + #include + #endif + + #define box_hton64(x) BSWAP64(x) + #define box_ntoh64(x) BSWAP64(x) +#else + #define box_hton64(x) box_swap64(x) + #define box_ntoh64(x) box_swap64(x) +#endif + +// overloaded auto-conversion functions +inline uint64_t hton(uint64_t in) { return box_hton64(in); } +inline uint32_t hton(uint32_t in) { return htonl(in); } +inline uint16_t hton(uint16_t in) { return htons(in); } +inline uint8_t hton(uint8_t in) { return in; } +inline int64_t hton(int64_t in) { return box_hton64(in); } +inline int32_t hton(int32_t in) { return htonl(in); } +inline int16_t hton(int16_t in) { return htons(in); } +inline int8_t hton(int8_t in) { return in; } +inline uint64_t ntoh(uint64_t in) { return box_ntoh64(in); } +inline uint32_t ntoh(uint32_t in) { return ntohl(in); } +inline uint16_t ntoh(uint16_t in) { return ntohs(in); } +inline uint8_t ntoh(uint8_t in) { return in; } +inline int64_t ntoh(int64_t in) { return box_ntoh64(in); } +inline int32_t ntoh(int32_t in) { return ntohl(in); } +inline int16_t ntoh(int16_t in) { return ntohs(in); } +inline int8_t ntoh(int8_t in) { return in; } + +#endif // BOX__H + diff --git a/lib/common/BoxConfig-MSVC.h b/lib/common/BoxConfig-MSVC.h new file mode 100644 index 00000000..2ec2edd7 --- /dev/null +++ b/lib/common/BoxConfig-MSVC.h @@ -0,0 +1,399 @@ +/* lib/common/BoxConfig.h. Generated by configure. */ +/* lib/common/BoxConfig.h.in. Generated from configure.ac by autoheader. */ +/* Hacked by hand to work for MSVC by Chris Wilson */ + +/* Define to major version for BDB_VERSION */ +/* #undef BDB_VERSION_MAJOR */ + +/* Define to minor version for BDB_VERSION */ +/* #undef BDB_VERSION_MINOR */ + +/* Define to point version for BDB_VERSION */ +/* #undef BDB_VERSION_POINT */ + +/* Name of the 64 bit endian swapping function */ +/* #undef BSWAP64 */ + +/* Define to 1 if the `closedir' function returns void instead of `int'. */ +#define CLOSEDIR_VOID 1 + +/* Define to 1 if non-aligned int16 access will fail */ +/* #undef HAVE_ALIGNED_ONLY_INT16 */ + +/* Define to 1 if non-aligned int32 access will fail */ +/* #undef HAVE_ALIGNED_ONLY_INT32 */ + +/* Define to 1 if non-aligned int64 access will fail */ +/* #undef HAVE_ALIGNED_ONLY_INT64 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ASM_BYTEORDER_H */ + +/* Define to 1 if BSWAP64 is defined to the name of a valid 64 bit endian + swapping function */ +/* #undef HAVE_BSWAP64 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DB_H */ + +/* Define to 1 if you have the declaration of `F_SETLK', and to 0 if you + don't. */ +#define HAVE_DECL_F_SETLK 0 + +/* Define to 1 if you have the declaration of `INFTIM', and to 0 if you don't. + */ +#define HAVE_DECL_INFTIM 0 + +/* Define to 1 if you have the declaration of `O_EXLOCK', and to 0 if you + don't. */ +#define HAVE_DECL_O_EXLOCK 0 + +/* Define to 1 if you have the declaration of `SO_PEERCRED', and to 0 if you + don't. */ +#define HAVE_DECL_SO_PEERCRED 0 + +/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if + you don't. */ +#define HAVE_DECL_XATTR_NOFOLLOW 0 + +/* Define to 1 if you have the declaration of `O_BINARY', and to 0 if you + don't. */ +#define HAVE_DECL_O_BINARY 1 + +/* Define to 1 if #define of pragmas works */ +/* #undef HAVE_DEFINE_PRAGMA */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_DIRENT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_READLINE_H */ + +/* define if the compiler supports exceptions */ +#define HAVE_EXCEPTIONS + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EXECINFO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `flock' function. */ +/* #undef HAVE_FLOCK */ + +/* Define to 1 if you have the `getmntent' function. */ +/* #undef HAVE_GETMNTENT */ + +/* Define to 1 if you have the `getpeereid' function. */ +/* #undef HAVE_GETPEEREID */ + +/* Define to 1 if you have the `getpid' function. */ +// #define HAVE_GETPID 1 + +/* Define to 1 if you have the `getxattr' function. */ +/* #undef HAVE_GETXATTR */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +// #define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef HAVE_KQUEUE */ + +/* Define to 1 if you have the `lchown' function. */ +/* #undef HAVE_LCHOWN */ + +/* Define to 1 if you have the `lgetxattr' function. */ +/* #undef HAVE_LGETXATTR */ + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#define HAVE_LIBCRYPTO 1 + +/* Define if you have a readline compatible library */ +/* #undef HAVE_LIBREADLINE */ + +/* Define to 1 if you have the `ssl' library (-lssl). */ +#define HAVE_LIBSSL 1 + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the `listxattr' function. */ +/* #undef HAVE_LISTXATTR */ + +/* Define to 1 if you have the `llistxattr' function. */ +/* #undef HAVE_LLISTXATTR */ + +/* Define to 1 if syscall lseek requires a dummy middle parameter */ +/* #undef HAVE_LSEEK_DUMMY_PARAM */ + +/* Define to 1 if you have the `lsetxattr' function. */ +/* #undef HAVE_LSETXATTR */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MNTENT_H */ + +/* Define to 1 if this platform supports mounts */ +/* #undef HAVE_MOUNTS */ + +/* define if the compiler implements namespaces */ +#define HAVE_NAMESPACES + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if SSL is pre-0.9.7 */ +/* #undef HAVE_OLD_SSL */ + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_SSL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PROCESS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PWD_H */ + +/* Define to 1 (and set RANDOM_DEVICE) if a random device is available */ +/* #undef HAVE_RANDOM_DEVICE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_H */ + +/* Define if your readline library has add_history */ +/* #undef HAVE_READLINE_HISTORY */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_REGEX_H */ +#define HAVE_PCREPOSIX_H 1 +#define HAVE_REGEX_SUPPORT 1 + +/* Define to 1 if you have the `setproctitle' function. */ +/* #undef HAVE_SETPROCTITLE */ +#define HAVE_SETPROCTITLE 1 +/* Define to 1 if you have the `setxattr' function. */ +/* #undef HAVE_SETXATTR */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if SSL is available */ +#define HAVE_SSL 1 + +/* Define to 1 if you have the `statfs' function. */ +/* #undef HAVE_STATFS */ + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +// #define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if `d_type' is member of `struct dirent'. */ +/* #undef HAVE_STRUCT_DIRENT_D_TYPE */ + +/* Define to 1 if `mnt_dir' is member of `struct mntent'. */ +/* #undef HAVE_STRUCT_MNTENT_MNT_DIR */ + +/* Define to 1 if `mnt_mountp' is member of `struct mnttab'. */ +/* #undef HAVE_STRUCT_MNTTAB_MNT_MOUNTP */ + +/* Define to 1 if `sin_len' is member of `struct sockaddr_in'. */ +/* #undef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + +/* Define to 1 if `f_mntonname' is member of `struct statfs'. */ +/* #undef HAVE_STRUCT_STATFS_F_MNTONNAME */ + +/* Define to 1 if `st_flags' is member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_FLAGS */ + +/* Define to 1 if `st_mtimespec' is member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC */ + +/* Define to 1 if you have the `syscall' function. */ +/* #undef HAVE_SYSCALL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYSLOG_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MNTTAB_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +// #define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCALL_H */ + +/* Define to 1 if you have the header file. */ +// #define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +// #define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_WAIT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_XATTR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +#define HAVE_UINT16_T 1 + +/* Define to 1 if the system has the type `uint32_t'. */ +#define HAVE_UINT32_T 1 + +/* Define to 1 if the system has the type `uint64_t'. */ +#define HAVE_UINT64_T 1 + +/* Define to 1 if the system has the type `uint8_t'. */ +#define HAVE_UINT8_T 1 + +/* Define to 1 if you have the header file. */ +// #define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type `u_int16_t'. */ +/* #undef HAVE_U_INT16_T */ + +/* Define to 1 if the system has the type `u_int32_t'. */ +/* #undef HAVE_U_INT32_T */ + +/* Define to 1 if the system has the type `u_int64_t'. */ +/* #undef HAVE_U_INT64_T */ + +/* Define to 1 if the system has the type `u_int8_t'. */ +/* #undef HAVE_U_INT8_T */ + +/* Define to 1 if struct dirent.d_type is valid */ +/* #undef HAVE_VALID_DIRENT_D_TYPE */ + +/* Define to 1 if the system has the type `_Bool'. */ +/* #undef HAVE__BOOL */ + +/* Define to 1 if you have the `__syscall' function. */ +/* #undef HAVE___SYSCALL */ + +/* Define to 1 if __syscall is available but needs a definition */ +/* #undef HAVE___SYSCALL_NEED_DEFN */ + +/* max value of long long calculated by configure */ +/* #undef LLONG_MAX */ + +/* min value of long long calculated by configure */ +/* #undef LLONG_MIN */ + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "box@fluffy.co.uk" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "Box Backup" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "Box Backup 0.11" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "box-backup" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.09" + +/* Define to the filename of the random device (and set HAVE_RANDOM_DEVICE) */ +/* #undef RANDOM_DEVICE */ + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* TMP directory name */ +#define TEMP_DIRECTORY_NAME "/tmp" + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if __USE_MALLOC is required work around STL memory leaks */ +/* #undef __USE_MALLOC */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +/* #undef mode_t */ + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ diff --git a/lib/common/BoxException.cpp b/lib/common/BoxException.cpp new file mode 100644 index 00000000..2503ca63 --- /dev/null +++ b/lib/common/BoxException.cpp @@ -0,0 +1,21 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxException.cpp +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "BoxException.h" + +#include "MemLeakFindOn.h" + +BoxException::BoxException() +{ +} + +BoxException::~BoxException() throw () +{ +} diff --git a/lib/common/BoxException.h b/lib/common/BoxException.h new file mode 100644 index 00000000..e3a2268a --- /dev/null +++ b/lib/common/BoxException.h @@ -0,0 +1,42 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxException.h +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- + +#ifndef BOXEXCEPTION__H +#define BOXEXCEPTION__H + +#include +#include + +// -------------------------------------------------------------------------- +// +// Class +// Name: BoxException +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- +class BoxException : public std::exception +{ +public: + BoxException(); + ~BoxException() throw (); + + virtual unsigned int GetType() const throw() = 0; + virtual unsigned int GetSubType() const throw() = 0; + virtual const std::string& GetMessage() const = 0; + +private: +}; + +#define EXCEPTION_IS_TYPE(exception_obj, type, subtype) \ + (exception_obj.GetType() == type::ExceptionType && \ + exception_obj.GetSubType() == type::subtype) + +#endif // BOXEXCEPTION__H + diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h new file mode 100644 index 00000000..c86573f6 --- /dev/null +++ b/lib/common/BoxPlatform.h @@ -0,0 +1,169 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxPlatform.h +// Purpose: Specifies what each platform supports in more detail, and includes +// extra files to get basic support for types. +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- + +#ifndef BOXPLATFORM__H +#define BOXPLATFORM__H + +#ifdef WIN32 +#define DIRECTORY_SEPARATOR "\\" +#define DIRECTORY_SEPARATOR_ASCHAR '\\' +#else +#define DIRECTORY_SEPARATOR "/" +#define DIRECTORY_SEPARATOR_ASCHAR '/' +#endif + +#define PLATFORM_DEV_NULL "/dev/null" + +#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" +#endif + +#ifdef WIN32 + #ifdef __MSVCRT_VERSION__ + #if __MSVCRT_VERSION__ < 0x0601 + #error Must include Box.h before sys/types.h + #endif + #else + // need msvcrt version 6.1 or higher for _gmtime64() + // must define this before importing + #define __MSVCRT_VERSION__ 0x0601 + #endif +#endif + +#include "emu.h" + +#ifdef HAVE_SYS_TYPES_H + #include +#endif + +// Need to define this before including stdint.h to ensure access to UINTPTR_MAX in C99: +// https://stackoverflow.com/questions/986426/what-do-stdc-limit-macros-and-stdc-constant-macros-mean +#define __STDC_LIMIT_MACROS + +#ifdef HAVE_INTTYPES_H + #include +#else + #ifdef HAVE_STDINT_H + #include + #endif +#endif + +// Slight hack; disable interception in raidfile test on Darwin and Windows +#if defined __APPLE__ || defined WIN32 + // TODO: Replace with autoconf test + #define PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE +#endif + +// Disable memory testing under Darwin, it just doesn't like it very much. +#ifdef __APPLE__ + // TODO: We really should get some decent leak detection code. + #define PLATFORM_DISABLE_MEM_LEAK_TESTING +#endif + +// Darwin also has a weird idea of permissions and dates on symlinks: +// perms are fixed at creation time by your umask, and dates can't be +// changed. This breaks unit tests if we try to compare these things. +// See: http://lists.apple.com/archives/darwin-kernel/2006/Dec/msg00057.html +#ifdef __APPLE__ + #define PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE +#endif + +// Find out if credentials on UNIX sockets can be obtained +#ifdef HAVE_GETPEEREID + // +#elif HAVE_DECL_SO_PEERCRED + // +#elif defined HAVE_UCRED_H && HAVE_GETPEERUCRED + // +#else + #define PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET +#endif + +#ifdef HAVE_DEFINE_PRAGMA + // set packing to one bytes (can't use push/pop on gcc) + #define BEGIN_STRUCTURE_PACKING_FOR_WIRE #pragma pack(1) + + // Use default packing + #define END_STRUCTURE_PACKING_FOR_WIRE #pragma pack() +#else + #define STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS +#endif + +// Handle differing xattr APIs +#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(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 + +#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 + #define INFTIM -1 +#endif + +// Define O_BINARY for Unix compatibility with Windows :-) +// MSVC 2010 and newer MinGW define this in fcntl.h, which is probably +// not included by this point, so include it now so that we can detect +// if we need O_BINARY + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifndef O_BINARY + #define O_BINARY 0 +#endif + +#ifdef WIN32 + typedef uint64_t InodeRefType; +#else + typedef ino_t InodeRefType; +#endif + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN +#endif + +#ifdef WIN32 + #define INVALID_FILE INVALID_HANDLE_VALUE + typedef HANDLE tOSFileHandle; +#else + #define INVALID_FILE -1 + typedef int tOSFileHandle; +#endif + +// Solaris has no dirfd(x) macro or function, and we need one for +// intercept tests. We cannot define macros with arguments directly +// using AC_DEFINE, so do it here instead of in configure.ac. + +#if ! defined PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE && ! HAVE_DECL_DIRFD + #ifdef HAVE_DIR_D_FD + #define dirfd(x) (x)->d_fd + #elif defined HAVE_DIR_DD_FD + #define dirfd(x) (x)->dd_fd + #else + #error No way to get file descriptor from DIR structure + #endif +#endif + +#endif // BOXPLATFORM__H diff --git a/lib/common/BoxPortsAndFiles.h.in b/lib/common/BoxPortsAndFiles.h.in new file mode 100644 index 00000000..f63e614b --- /dev/null +++ b/lib/common/BoxPortsAndFiles.h.in @@ -0,0 +1,51 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxPortsAndFiles.h +// Purpose: Central list of which tcp/ip ports and hardcoded file locations +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef BOXPORTSANDFILES__H +#define BOXPORTSANDFILES__H + +#define BOX_PORT_BASE 2200 + + +// Backup store daemon +#define BOX_PORT_BBSTORED (BOX_PORT_BASE+1) +#define BOX_PORT_BBSTORED_TEST 22011 + +// directory within the RAIDFILE root for the backup store daemon +#define BOX_RAIDFILE_ROOT_BBSTORED "backup" + +// default security level if SSLSecurityLevel is not specified: see +// https://github.com/boxbackup/boxbackup/wiki/WeakSSLCertificates +const int BOX_DEFAULT_SSL_SECURITY_LEVEL = -1; + +// configuration file paths +#ifdef WIN32 + // no default config file path, use these macros to call + // GetDefaultConfigFilePath() instead. + + #define BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE \ + GetDefaultConfigFilePath("bbackupd.conf").c_str() + #define BOX_GET_DEFAULT_RAIDFILE_CONFIG_FILE \ + GetDefaultConfigFilePath("raidfile.conf").c_str() + #define BOX_GET_DEFAULT_BBSTORED_CONFIG_FILE \ + GetDefaultConfigFilePath("bbstored.conf").c_str() +#else + #define BOX_FILE_BBACKUPD_OLD_CONFIG "@sysconfdir_expanded@/box/bbackupd.conf" + #define BOX_FILE_RAIDFILE_OLD_CONFIG "@sysconfdir_expanded@/box/raidfile.conf" + #define BOX_FILE_BBSTORED_OLD_CONFIG "@sysconfdir_expanded@/box/bbstored.conf" + #define BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE \ + std::string("@sysconfdir_expanded@/boxbackup/bbackupd.conf") + #define BOX_GET_DEFAULT_RAIDFILE_CONFIG_FILE \ + std::string("@sysconfdir_expanded@/boxbackup/raidfile.conf") + #define BOX_GET_DEFAULT_BBSTORED_CONFIG_FILE \ + std::string("@sysconfdir_expanded@/boxbackup/bbstored.conf") +#endif + +#endif // BOXPORTSANDFILES__H + diff --git a/lib/common/BoxTime.cpp b/lib/common/BoxTime.cpp new file mode 100644 index 00000000..77daae6d --- /dev/null +++ b/lib/common/BoxTime.cpp @@ -0,0 +1,159 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTime.cpp +// Purpose: Time for the box +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_SYS_TIME_H + #include +#endif + +#ifdef HAVE_TIME_H + #include +#endif + +#include +#include + +#include "BoxTime.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: GetCurrentBoxTime() +// Purpose: Returns the current time as a box time. +// (1 sec precision, or better if supported by system) +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +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 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)); +} + +std::string FormatTime(box_time_t time, bool includeDate, bool showMicros) +{ + std::ostringstream buf; + + time_t seconds = BoxTimeToSeconds(time); + int micros = BoxTimeToMicroSeconds(time) % MICRO_SEC_IN_SEC; + + struct tm tm_now, *tm_ptr = &tm_now; + + #ifdef WIN32 + if ((tm_ptr = localtime(&seconds)) != NULL) + #else + if (localtime_r(&seconds, &tm_now) != NULL) + #endif + { + buf << std::setfill('0'); + + if (includeDate) + { + buf << std::setw(4) << (tm_ptr->tm_year + 1900) << "-" << + std::setw(2) << (tm_ptr->tm_mon + 1) << "-" << + std::setw(2) << (tm_ptr->tm_mday) << " "; + } + + buf << std::setw(2) << tm_ptr->tm_hour << ":" << + std::setw(2) << tm_ptr->tm_min << ":" << + std::setw(2) << tm_ptr->tm_sec; + + if (showMicros) + { + buf << "." << std::setw(3) << (int)(micros / 1000); + } + } + else + { + buf << strerror(errno); + } + + return buf.str(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ShortSleep(box_time_t duration) +// Purpose: Sleeps for the specified duration as accurately +// and efficiently as possible. +// Created: 2011/01/11 +// +// -------------------------------------------------------------------------- + +void ShortSleep(box_time_t duration, bool logDuration) +{ + if(logDuration) + { + BOX_TRACE("Sleeping for " << BOX_FORMAT_MICROSECONDS(duration)); + } + +#ifdef WIN32 + Sleep(BoxTimeToMilliSeconds(duration)); +#else + struct timespec ts; + memset(&ts, 0, sizeof(ts)); + ts.tv_sec = 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) + { + // FIXME evil hack for OSX, where ts.tv_sec contains + // a negative number interpreted as unsigned 32-bit + // when nanosleep() returns later than expected. + + int32_t secs = (int32_t) ts.tv_sec; + int64_t remain_ns = ((int64_t)secs * 1000000000) + ts.tv_nsec; + + if (remain_ns < 0) + { + BOX_WARNING("nanosleep interrupted " << + ((float)(0 - remain_ns) / 1000000000) << + " secs late"); + return; + } + + 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 new file mode 100644 index 00000000..6afaada3 --- /dev/null +++ b/lib/common/BoxTime.h @@ -0,0 +1,53 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTime.h +// Purpose: How time is represented +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#ifndef BOXTIME__H +#define BOXTIME__H + +// 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) +#define NANO_SEC_IN_USEC_LL (1000LL) +#define MICRO_SEC_IN_SEC (1000000) +#define MICRO_SEC_IN_SEC_LL (1000000LL) +#define MICRO_SEC_IN_MILLI_SEC (1000) +#define MILLI_SEC_IN_SEC (1000) +#define MILLI_SEC_IN_SEC_LL (1000LL) + +box_time_t GetCurrentBoxTime(); + +inline box_time_t SecondsToBoxTime(time_t Seconds) +{ + return ((box_time_t)Seconds * MICRO_SEC_IN_SEC_LL); +} +inline uint64_t MilliSecondsToBoxTime(int64_t milliseconds) +{ + return ((box_time_t)milliseconds * 1000); +} +inline time_t BoxTimeToSeconds(box_time_t Time) +{ + return Time / MICRO_SEC_IN_SEC_LL; +} +inline uint64_t BoxTimeToMilliSeconds(box_time_t Time) +{ + return Time / MILLI_SEC_IN_SEC_LL; +} +inline uint64_t BoxTimeToMicroSeconds(box_time_t Time) +{ + return Time; +} + +std::string FormatTime(box_time_t time, bool includeDate, + bool showMicros = false); + +void ShortSleep(box_time_t duration, bool logDuration); + +#endif // BOXTIME__H diff --git a/lib/common/BoxTimeToText.cpp b/lib/common/BoxTimeToText.cpp new file mode 100644 index 00000000..dbeefe1b --- /dev/null +++ b/lib/common/BoxTimeToText.cpp @@ -0,0 +1,76 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTimeToText.cpp +// Purpose: Convert box time to text +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include "BoxTimeToText.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BoxTimeToISO8601String(box_time_t, bool) +// Purpose: Convert a 64 bit box time to a ISO 8601 compliant +// string, either in local or UTC time +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +std::string BoxTimeToISO8601String(box_time_t Time, bool localTime) +{ + time_t timeInSecs = BoxTimeToSeconds(Time); + char str[128]; // more than enough space + +#ifdef WIN32 + struct tm *time; + __time64_t winTime = timeInSecs; + + if(localTime) + { + time = _localtime64(&winTime); + } + else + { + time = _gmtime64(&winTime); + } + + if(time == NULL) + { + // ::sprintf(str, "%016I64x ", bob); + return std::string("unable to convert time"); + } + + sprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d", time->tm_year + 1900, + time->tm_mon + 1, time->tm_mday, time->tm_hour, + time->tm_min, time->tm_sec); +#else // ! WIN32 + struct tm time; + + if(localTime) + { + localtime_r(&timeInSecs, &time); + } + else + { + gmtime_r(&timeInSecs, &time); + } + + sprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d", time.tm_year + 1900, + time.tm_mon + 1, time.tm_mday, time.tm_hour, + time.tm_min, time.tm_sec); +#endif // WIN32 + + return std::string(str); +} + + diff --git a/lib/common/BoxTimeToText.h b/lib/common/BoxTimeToText.h new file mode 100644 index 00000000..21fa5d57 --- /dev/null +++ b/lib/common/BoxTimeToText.h @@ -0,0 +1,19 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTimeToText.h +// Purpose: Convert box time to text +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#ifndef BOXTIMETOTEXT__H +#define BOXTIMETOTEXT__H + +#include +#include "BoxTime.h" + +std::string BoxTimeToISO8601String(box_time_t Time, bool localTime); + +#endif // BOXTIMETOTEXT__H + diff --git a/lib/common/BoxTimeToUnix.h b/lib/common/BoxTimeToUnix.h new file mode 100644 index 00000000..f8a8797e --- /dev/null +++ b/lib/common/BoxTimeToUnix.h @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTimeToUnix.h +// Purpose: Convert times in 64 bit values to UNIX structures +// Created: 2003/10/07 +// +// -------------------------------------------------------------------------- + +#ifndef FILEMODIFICATIONTIMETOTIMEVAL__H +#define FILEMODIFICATIONTIMETOTIMEVAL__H + +#ifdef WIN32 +#include +#else +#include +#endif + +#include "BoxTime.h" + +inline void BoxTimeToTimeval(box_time_t Time, struct timeval &tv) +{ + tv.tv_sec = (long)(Time / MICRO_SEC_IN_SEC_LL); + tv.tv_usec = (long)(Time % MICRO_SEC_IN_SEC_LL); +} + +inline void BoxTimeToTimespec(box_time_t Time, struct timespec &tv) +{ + tv.tv_sec = (time_t)(Time / MICRO_SEC_IN_SEC_LL); + tv.tv_nsec = ((long)(Time % MICRO_SEC_IN_SEC_LL)) * NANO_SEC_IN_USEC; +} + +#endif // FILEMODIFICATIONTIMETOTIMEVAL__H + diff --git a/lib/common/BufferedStream.cpp b/lib/common/BufferedStream.cpp new file mode 100644 index 00000000..847cf66c --- /dev/null +++ b/lib/common/BufferedStream.cpp @@ -0,0 +1,209 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BufferedStream.cpp +// Purpose: Buffering read-only wrapper around IOStreams +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "BufferedStream.h" +#include "CommonException.h" + +#include + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::BufferedStream(const char *, int, int) +// Purpose: Constructor, set up buffer +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +BufferedStream::BufferedStream(IOStream& rSource) +: mrSource(rSource), mBufferSize(0), mBufferPosition(0) +{ } + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::Read(void *, int) +// Purpose: Reads bytes from the file +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +int BufferedStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + if (mBufferSize == mBufferPosition) + { + // buffer is empty, fill it. + + int numBytesRead = mrSource.Read(mBuffer, sizeof(mBuffer), + Timeout); + + if (numBytesRead < 0) + { + return numBytesRead; + } + + mBufferSize = numBytesRead; + } + + int sizeToReturn = mBufferSize - mBufferPosition; + + if (sizeToReturn > NBytes) + { + sizeToReturn = NBytes; + } + + memcpy(pBuffer, mBuffer + mBufferPosition, sizeToReturn); + mBufferPosition += sizeToReturn; + + if (mBufferPosition == mBufferSize) + { + // clear out the buffer + mBufferSize = 0; + mBufferPosition = 0; + } + + return sizeToReturn; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::BytesLeftToRead() +// Purpose: Returns number of bytes to read (may not be most efficient function ever) +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +IOStream::pos_type BufferedStream::BytesLeftToRead() +{ + return mrSource.BytesLeftToRead() + mBufferSize - mBufferPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::Write(void *, int) +// Purpose: Writes bytes to the underlying stream (not supported) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void BufferedStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::GetPosition() +// Purpose: Get position in stream +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type BufferedStream::GetPosition() const +{ + return mrSource.GetPosition() - mBufferSize + mBufferPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::Seek(pos_type, int) +// Purpose: Seeks within file, as lseek, invalidate buffer +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void BufferedStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + switch (SeekType) + { + case SeekType_Absolute: + { + // just go there + mrSource.Seek(Offset, SeekType); + } + break; + + case SeekType_Relative: + { + // Actual underlying file position is + // (mBufferSize - mBufferPosition) ahead of us. + // Need to subtract that amount from the seek + // to seek forward that much less, putting the + // real pointer in the right place. + mrSource.Seek(Offset - mBufferSize + mBufferPosition, + SeekType); + } + break; + + case SeekType_End: + { + // Actual underlying file position is + // (mBufferSize - mBufferPosition) ahead of us. + // Need to add that amount to the seek + // to seek backwards that much more, putting the + // real pointer in the right place. + mrSource.Seek(Offset + mBufferSize - mBufferPosition, + SeekType); + } + } + + // always clear the buffer for now (may be slightly wasteful) + mBufferSize = 0; + mBufferPosition = 0; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::Close() +// Purpose: Closes the underlying stream (not needed) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void BufferedStream::Close() +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::StreamDataLeft() +// Purpose: Any data left to write? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool BufferedStream::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); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedStream::StreamClosed() +// Purpose: Is the stream closed? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool BufferedStream::StreamClosed() +{ + return mrSource.StreamClosed(); +} + diff --git a/lib/common/BufferedStream.h b/lib/common/BufferedStream.h new file mode 100644 index 00000000..3984aceb --- /dev/null +++ b/lib/common/BufferedStream.h @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BufferedStream.h +// Purpose: Buffering read-only wrapper around IOStreams +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- + +#ifndef BUFFEREDSTREAM__H +#define BUFFEREDSTREAM__H + +#include "IOStream.h" + +class BufferedStream : public IOStream +{ +private: + IOStream& mrSource; + char mBuffer[4096]; + int mBufferSize; + int mBufferPosition; + +public: + BufferedStream(IOStream& rSource); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + 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(); + + 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 */ } +}; + +#endif // BUFFEREDSTREAM__H + + diff --git a/lib/common/BufferedWriteStream.cpp b/lib/common/BufferedWriteStream.cpp new file mode 100644 index 00000000..8fbabe9b --- /dev/null +++ b/lib/common/BufferedWriteStream.cpp @@ -0,0 +1,181 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BufferedWriteStream.cpp +// Purpose: Buffering write-only wrapper around IOStreams +// Created: 2010/09/13 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "BufferedWriteStream.h" +#include "CommonException.h" + +#include + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::BufferedWriteStream(const char *, int, int) +// Purpose: Constructor, set up buffer +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +BufferedWriteStream::BufferedWriteStream(IOStream& rSink) +: mrSink(rSink), mBufferPosition(0) +{ } + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::Read(void *, int) +// Purpose: Reads bytes from the file - throws exception +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +int BufferedWriteStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::BytesLeftToRead() +// Purpose: Returns number of bytes to read (may not be most efficient function ever) +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +IOStream::pos_type BufferedWriteStream::BytesLeftToRead() +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::Write(void *, int) +// Purpose: Writes bytes to the underlying stream (not supported) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void BufferedWriteStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + int numBytesRemain = NBytes; + + do + { + int maxWritable = sizeof(mBuffer) - mBufferPosition; + int numBytesToWrite = (numBytesRemain < maxWritable) ? + numBytesRemain : maxWritable; + + if(numBytesToWrite > 0) + { + memcpy(mBuffer + mBufferPosition, pBuffer, + numBytesToWrite); + mBufferPosition += numBytesToWrite; + pBuffer = ((const char *)pBuffer) + numBytesToWrite; + numBytesRemain -= numBytesToWrite; + } + + if(numBytesRemain > 0) + { + Flush(); + } + } + while(numBytesRemain > 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::GetPosition() +// Purpose: Get position in stream +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type BufferedWriteStream::GetPosition() const +{ + return mrSink.GetPosition() + mBufferPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::Seek(pos_type, int) +// Purpose: Seeks within file, as lseek, invalidate buffer +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void BufferedWriteStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + // Always flush the buffer before seeking + Flush(); + + mrSink.Seek(Offset, SeekType); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::Flush(); +// Purpose: Write out current buffer contents and invalidate +// Created: 2010/09/13 +// +// -------------------------------------------------------------------------- +void BufferedWriteStream::Flush(int Timeout) +{ + if(mBufferPosition > 0) + { + mrSink.Write(mBuffer, mBufferPosition); + } + + mBufferPosition = 0; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::Close() +// Purpose: Closes the underlying stream (not needed) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void BufferedWriteStream::Close() +{ + Flush(); + mrSink.Close(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::StreamDataLeft() +// Purpose: Any data left to write? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool BufferedWriteStream::StreamDataLeft() +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BufferedWriteStream::StreamClosed() +// Purpose: Is the stream closed? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool BufferedWriteStream::StreamClosed() +{ + return mrSink.StreamClosed(); +} + diff --git a/lib/common/BufferedWriteStream.h b/lib/common/BufferedWriteStream.h new file mode 100644 index 00000000..5f6d5f19 --- /dev/null +++ b/lib/common/BufferedWriteStream.h @@ -0,0 +1,45 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BufferedWriteStream.h +// Purpose: Buffering write-only wrapper around IOStreams +// Created: 2010/09/13 +// +// -------------------------------------------------------------------------- + +#ifndef BUFFEREDWRITESTREAM__H +#define BUFFEREDWRITESTREAM__H + +#include "IOStream.h" + +class BufferedWriteStream : public IOStream +{ +private: + IOStream& mrSink; + char mBuffer[4096]; + int mBufferPosition; + +public: + BufferedWriteStream(IOStream& rSource); + virtual ~BufferedWriteStream() { Close(); } + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + 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); + virtual void Close(); + + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + BufferedWriteStream(const BufferedWriteStream &rToCopy) + : mrSink(rToCopy.mrSink) { /* do not call */ } +}; + +#endif // BUFFEREDWRITESTREAM__H + + diff --git a/lib/common/CollectInBufferStream.cpp b/lib/common/CollectInBufferStream.cpp new file mode 100644 index 00000000..47b271f0 --- /dev/null +++ b/lib/common/CollectInBufferStream.cpp @@ -0,0 +1,274 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CollectInBufferStream.cpp +// Purpose: Collect data in a buffer, and then read it out. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "CollectInBufferStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +#define INITIAL_BUFFER_SIZE 1024 +#define MAX_BUFFER_ADDITION (1024*64) + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::CollectInBufferStream() +// Purpose: Constructor +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +CollectInBufferStream::CollectInBufferStream() + : mBuffer(INITIAL_BUFFER_SIZE), + mBufferSize(INITIAL_BUFFER_SIZE), + mBytesInBuffer(0), + mReadPosition(0), + mInWritePhase(true) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::~CollectInBufferStream() +// Purpose: Destructor +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +CollectInBufferStream::~CollectInBufferStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Read(void *, int, int) +// Purpose: As interface. But only works in read phase +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +int CollectInBufferStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + if(mInWritePhase != false) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + // Adjust to number of bytes left + if(NBytes > (mBytesInBuffer - mReadPosition)) + { + NBytes = (mBytesInBuffer - mReadPosition); + } + ASSERT(NBytes >= 0); + if(NBytes <= 0) return 0; // careful now + + // Copy in the requested number of bytes and adjust the read pointer + ::memcpy(pBuffer, ((char*)mBuffer) + mReadPosition, NBytes); + mReadPosition += NBytes; + + return NBytes; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::BytesLeftToRead() +// Purpose: As interface. But only works in read phase +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type CollectInBufferStream::BytesLeftToRead() +{ + if(mInWritePhase != false) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + return (mBytesInBuffer - mReadPosition); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Write(void *, int) +// Purpose: As interface. But only works in write phase +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + if(mInWritePhase != true) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + // Enough space in the buffer + if((mBytesInBuffer + NBytes) > mBufferSize) + { + // Need to reallocate... what's the block size we'll use? + int allocateBlockSize = mBufferSize; + if(allocateBlockSize > MAX_BUFFER_ADDITION) + { + allocateBlockSize = MAX_BUFFER_ADDITION; + } + + // Write it the easy way. Although it's not the most efficient... + int newSize = mBufferSize; + while(newSize < (mBytesInBuffer + NBytes)) + { + newSize += allocateBlockSize; + } + + // Reallocate buffer + mBuffer.Resize(newSize); + + // Store new size + mBufferSize = newSize; + } + + // Copy in data and adjust counter + ::memcpy(((char*)mBuffer) + mBytesInBuffer, pBuffer, NBytes); + mBytesInBuffer += NBytes; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::GetPosition() +// Purpose: In write phase, returns the number of bytes written, in read +// phase, the number of bytes to go +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type CollectInBufferStream::GetPosition() const +{ + return mInWritePhase?mBytesInBuffer:mReadPosition; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Seek(pos_type, int) +// Purpose: As interface. But read phase only. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::Seek(pos_type Offset, int SeekType) +{ + if(mInWritePhase != false) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + int newPos = 0; + switch(SeekType) + { + case IOStream::SeekType_Absolute: + newPos = Offset; + break; + case IOStream::SeekType_Relative: + newPos = mReadPosition + Offset; + break; + case IOStream::SeekType_End: + newPos = mBytesInBuffer + Offset; + break; + default: + THROW_EXCEPTION(CommonException, IOStreamBadSeekType) + break; + } + + // Make sure it doesn't go over + if(newPos > mBytesInBuffer) + { + newPos = mBytesInBuffer; + } + // or under + if(newPos < 0) + { + newPos = 0; + } + + // Set the new read position + mReadPosition = newPos; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::StreamDataLeft() +// Purpose: As interface +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool CollectInBufferStream::StreamDataLeft() +{ + return mInWritePhase?(false):(mReadPosition < mBytesInBuffer); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::StreamClosed() +// Purpose: As interface +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool CollectInBufferStream::StreamClosed() +{ + return !mInWritePhase; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::SetForReading() +// Purpose: Switch to read phase, after all data written +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::SetForReading() +{ + if(mInWritePhase != true) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + // Move to read phase + mInWritePhase = false; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::GetBuffer() +// Purpose: Returns the buffer +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void *CollectInBufferStream::GetBuffer() const +{ + return mBuffer.GetPtr(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::GetSize() +// Purpose: Returns the buffer size +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +int CollectInBufferStream::GetSize() const +{ + return mBytesInBuffer; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Reset() +// Purpose: Reset the stream, so it is empty and ready to be written to. +// Created: 8/12/03 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::Reset() +{ + mInWritePhase = true; + mBytesInBuffer = 0; + mReadPosition = 0; +} + diff --git a/lib/common/CollectInBufferStream.h b/lib/common/CollectInBufferStream.h new file mode 100644 index 00000000..297d2851 --- /dev/null +++ b/lib/common/CollectInBufferStream.h @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CollectInBufferStream.h +// Purpose: Collect data in a buffer, and then read it out. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#ifndef COLLECTINBUFFERSTREAM__H +#define COLLECTINBUFFERSTREAM__H + +#include "IOStream.h" +#include "Guards.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: CollectInBufferStream +// Purpose: Collect data in a buffer, and then read it out. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +class CollectInBufferStream : public IOStream +{ +public: + CollectInBufferStream(); + ~CollectInBufferStream(); + + // 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, + 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;} + +private: + MemoryBlockGuard mBuffer; + int mBufferSize; + int mBytesInBuffer; + int mReadPosition; + bool mInWritePhase; +}; + +#endif // COLLECTINBUFFERSTREAM__H + diff --git a/lib/common/CommonException.h b/lib/common/CommonException.h new file mode 100644 index 00000000..a0eb3bf5 --- /dev/null +++ b/lib/common/CommonException.h @@ -0,0 +1,17 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CommonException.h +// Purpose: Exception +// Created: 2003/07/08 +// +// -------------------------------------------------------------------------- + +#ifndef COMMONEXCEPTION__H +#define COMMONEXCEPTION__H + +// Compatibility header with old non-autogen exception scheme +#include "autogen_CommonException.h" + +#endif // COMMONEXCEPTION__H + diff --git a/lib/common/CommonException.txt b/lib/common/CommonException.txt new file mode 100644 index 00000000..610ba1a8 --- /dev/null +++ b/lib/common/CommonException.txt @@ -0,0 +1,59 @@ + +# NOTE: Exception descriptions are for public distributions of Box Backup only -- do not rely for other applications. + + +EXCEPTION Common 1 + +Internal 0 +AssertFailed 1 +OSFileOpenError 2 Can't open a file -- attempted to load a non-existant config file or bad file referenced within? +OSFileCloseError 3 +FileAlreadyClosed 4 +BadArguments 5 +ConfigNoKey 6 +ConfigNoSubConfig 7 +GetLineNoHandle 8 +OSFileError 9 Error accessing a file. Check permissions. +GetLineEOF 10 +ConfigBadIntValue 11 +GetLineTooLarge 12 Protects against very large lines using up lots of memory. +NotSupported 13 +OSFileReadError 14 +OSFileWriteError 15 +FileClosed 16 +IOStreamBadSeekType 17 +CantWriteToPartialReadStream 18 +CollectInBufferStreamNotInCorrectPhase 19 +NamedLockAlreadyLockingSomething 20 +NamedLockNotHeld 21 +StreamableMemBlockIncompleteRead 22 +MemBlockStreamNotSupported 23 +StreamDoesntHaveRequiredProperty 24 +CannotWriteToReadGatherStream 25 +ReadGatherStreamAddingBadBlock 26 +CouldNotLookUpUsername 27 +CouldNotRestoreProcessUser 28 +CouldNotChangeProcessUser 29 +RegexNotSupportedOnThisPlatform 30 Your platform does not have built in regular expression libraries. +BadRegularExpression 31 +CouldNotCreateKQueue 32 +KEventErrorAdd 33 +KEventErrorWait 34 +KEventErrorRemove 35 +KQueueNotSupportedOnThisPlatform 36 +IOStreamGetLineNotEnoughDataToIgnore 37 Bad value passed to IOStreamGetLine::IgnoreBufferedData() +TempDirPathTooLong 38 Your temporary directory path is too long. Check the TMP and TEMP environment variables. +ArchiveBlockIncompleteRead 39 The Store Object Info File is too short or corrupted, and will be rewritten automatically when the next backup completes. +AccessDenied 40 Access to the file or directory was denied. Please check the permissions. +DatabaseOpenFailed 41 Failed to open the database file +DatabaseReadFailed 42 Failed to read a record from the database file +DatabaseWriteFailed 43 Failed to write a record from the database file +DatabaseDeleteFailed 44 Failed to delete a record from the database file +DatabaseCloseFailed 45 Failed to close the database file +DatabaseRecordNotFound 46 The database does not contain the expected record +DatabaseRecordAlreadyExists 47 The database already contains a record with this key, which was not expected +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 new file mode 100644 index 00000000..9860864a --- /dev/null +++ b/lib/common/Configuration.cpp @@ -0,0 +1,933 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Configuration.cpp +// Purpose: Reading configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include + +#include "Configuration.h" +#include "CommonException.h" +#include "Guards.h" +#include "FdGetLine.h" + +#include "MemLeakFindOn.h" + +#include + +// utility whitespace function +inline bool iw(int c) +{ + return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded +} + +// boolean values +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, + int flags, + void *testFunction +) +: mName(name), + mHasDefaultValue(false), + mFlags(flags), + mTestFunction(testFunction) +{ } + +// to allow passing NULL for default ListenAddresses + +ConfigurationVerifyKey::ConfigurationVerifyKey +( + std::string name, + int flags, + NoDefaultValue_t t, + void *testFunction +) +: mName(name), + mHasDefaultValue(false), + mFlags(flags), + mTestFunction(testFunction) +{ } + +ConfigurationVerifyKey::ConfigurationVerifyKey +( + std::string name, + int flags, + std::string defaultValue, + void *testFunction +) +: mName(name), + mDefaultValue(defaultValue), + mHasDefaultValue(true), + mFlags(flags), + mTestFunction(testFunction) +{ } + +ConfigurationVerifyKey::ConfigurationVerifyKey +( + std::string name, + int flags, + const char *defaultValue, + void *testFunction +) +: mName(name), + mDefaultValue(defaultValue), + mHasDefaultValue(true), + mFlags(flags), + mTestFunction(testFunction) +{ } + +ConfigurationVerifyKey::ConfigurationVerifyKey +( + std::string name, + int flags, + int defaultValue, + void *testFunction +) +: mName(name), + mHasDefaultValue(true), + mFlags(flags), + mTestFunction(testFunction) +{ + ASSERT(flags & ConfigTest_IsInt); + std::ostringstream val; + val << defaultValue; + mDefaultValue = val.str(); +} + +ConfigurationVerifyKey::ConfigurationVerifyKey +( + std::string name, + int flags, + bool defaultValue, + void *testFunction +) +: mName(name), + mHasDefaultValue(true), + mFlags(flags), + mTestFunction(testFunction) +{ + ASSERT(flags & ConfigTest_IsBool); + mDefaultValue = defaultValue ? "yes" : "no"; +} + +ConfigurationVerifyKey::ConfigurationVerifyKey +( + const ConfigurationVerifyKey& rToCopy +) +: mName(rToCopy.mName), + mDefaultValue(rToCopy.mDefaultValue), + mHasDefaultValue(rToCopy.mHasDefaultValue), + mFlags(rToCopy.mFlags), + mTestFunction(rToCopy.mTestFunction) +{ } + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::Configuration(const std::string &) +// Purpose: Constructor +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +Configuration::Configuration(const std::string &rName) + : mName(rName) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::Configuration(const Configuration &) +// Purpose: Copy constructor +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +Configuration::Configuration(const Configuration &rToCopy) + : mName(rToCopy.mName), + mKeys(rToCopy.mKeys), + mSubConfigurations(rToCopy.mSubConfigurations) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::~Configuration() +// Purpose: Destructor +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +Configuration::~Configuration() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::LoadAndVerify(const std::string &, const ConfigurationVerify *, std::string &) +// Purpose: Loads a configuration file from disc, checks it. Returns NULL if it was faulting, in which +// case they'll be an error message. +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +std::auto_ptr Configuration::LoadAndVerify( + const std::string& rFilename, + const ConfigurationVerify *pVerify, + std::string &rErrorMsg) +{ + // Just to make sure + rErrorMsg.erase(); + + // Open the file + FileHandleGuard file(rFilename); + + // GetLine object + FdGetLine getline(file); + + // Object to create + std::auto_ptr apConfig( + new Configuration(std::string(""))); + + try + { + // Load + LoadInto(*apConfig, getline, rErrorMsg, true); + + if(!rErrorMsg.empty()) + { + // An error occured, return now + BOX_LOG_CATEGORY(Log::ERROR, ConfigurationVerify::VERIFY_ERROR, + "Error in Configuration::LoadInto: " << rErrorMsg); + return std::auto_ptr(0); + } + + // Verify? + if(pVerify) + { + if(!apConfig->Verify(*pVerify, std::string(), 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(0); + } + } + } + catch(...) + { + // Clean up + throw; + } + + // Success. Return result. + return apConfig; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: LoadInto(Configuration &, FdGetLine &, std::string &, bool) +// Purpose: Private. Load configuration information from the file into the config object. +// Returns 'abort' flag, if error, will be appended to rErrorMsg. +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel) +{ + bool startBlockExpected = false; + std::string blockName; + + //TRACE1("BLOCK: |%s|\n", rConfig.mName.c_str()); + + while(!rGetLine.IsEOF()) + { + std::string line(rGetLine.GetLine(true)); /* preprocess out whitespace and comments */ + + if(line.empty()) + { + // Ignore blank lines + continue; + } + + // Line an open block string? + if(line == "{") + { + if(startBlockExpected) + { + // New config object + Configuration subConfig(blockName); + + // Continue processing into this block + if(!LoadInto(subConfig, rGetLine, rErrorMsg, false)) + { + // Abort error + return false; + } + + startBlockExpected = false; + + // Store... + rConfig.AddSubConfig(blockName, subConfig); + } + else + { + rErrorMsg += "Unexpected start block in " + + rConfig.mName + "\n"; + } + } + else + { + // Close block? + if(line == "}") + { + if(RootLevel) + { + // error -- root level doesn't have a close + rErrorMsg += "Root level has close block -- forgot to terminate subblock?\n"; + // but otherwise ignore + } + else + { + //TRACE0("ENDBLOCK\n"); + return true; // All very good and nice + } + } + // Either a key, or a sub block beginning + else + { + // Can't be a start block + if(startBlockExpected) + { + rErrorMsg += "Block " + blockName + " wasn't started correctly (no '{' on line of it's own)\n"; + startBlockExpected = false; + } + + // Has the line got an = in it? + unsigned int equals = 0; + for(; equals < line.size(); ++equals) + { + if(line[equals] == '=') + { + // found! + break; + } + } + if(equals < line.size()) + { + // Make key value pair + unsigned int keyend = equals; + while(keyend > 0 && iw(line[keyend-1])) + { + keyend--; + } + unsigned int valuestart = equals+1; + while(valuestart < line.size() && iw(line[valuestart])) + { + valuestart++; + } + if(keyend > 0 && valuestart <= line.size()) + { + std::string key(line.substr(0, keyend)); + std::string value(line.substr(valuestart)); + rConfig.AddKeyValue(key, value); + } + else + { + rErrorMsg += "Invalid configuration key: " + line + "\n"; + } + } + else + { + // Start of sub block + blockName = line; + startBlockExpected = true; + } + } + } + } + + // End of file? + if(!RootLevel && rGetLine.IsEOF()) + { + // Error if EOF and this isn't the root level + rErrorMsg += "File ended without terminating all subblocks\n"; + } + + return true; +} + +void Configuration::AddKeyValue(const std::string& rKey, + const std::string& rValue) +{ + // Check for duplicate values + if(mKeys.find(rKey) != mKeys.end()) + { + // Multi-values allowed here, but checked later on + mKeys[rKey] += MultiValueSeparator; + mKeys[rKey] += rValue; + } + else + { + // Store + mKeys[rKey] = rValue; + } +} + +void Configuration::AddSubConfig(const std::string& rName, + const Configuration& rSubConfig) +{ + mSubConfigurations.push_back( + std::pair(rName, rSubConfig)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::KeyExists(const std::string&) +// Purpose: Checks to see if a key exists +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +bool Configuration::KeyExists(const std::string& rKeyName) const +{ + return mKeys.find(rKeyName) != mKeys.end(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValue(const std::string&) +// Purpose: Returns the value of a configuration variable +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +const std::string &Configuration::GetKeyValue(const std::string& rKeyName) const +{ + std::map::const_iterator i(mKeys.find(rKeyName)); + + if(i == mKeys.end()) + { + BOX_LOG_CATEGORY(Log::ERROR, ConfigurationVerify::VERIFY_ERROR, + "Missing configuration key: " << rKeyName); + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + return i->second; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValueInt(const std::string& rKeyName) +// Purpose: Gets a key value as an integer +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +int Configuration::GetKeyValueInt(const std::string& rKeyName) const +{ + std::map::const_iterator i(mKeys.find(rKeyName)); + + if(i == mKeys.end()) + { + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + long value = ::strtol((i->second).c_str(), NULL, + 0 /* C style handling */); + if(value == LONG_MAX || value == LONG_MIN) + { + THROW_EXCEPTION(CommonException, ConfigBadIntValue) + } + return (int)value; + } +} + + +int Configuration::GetKeyValueInt(const std::string& rKeyName, int default_value) const +{ + if(!KeyExists(rKeyName)) + { + return default_value; + } + return GetKeyValueInt(rKeyName); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValueUint32(const std::string& rKeyName) +// Purpose: Gets a key value as a 32-bit unsigned integer +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +uint32_t Configuration::GetKeyValueUint32(const std::string& rKeyName) const +{ + std::map::const_iterator i(mKeys.find(rKeyName)); + + if(i == mKeys.end()) + { + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + errno = 0; + long value = ::strtoul((i->second).c_str(), NULL, + 0 /* C style handling */); + if(errno != 0) + { + THROW_EXCEPTION(CommonException, ConfigBadIntValue) + } + return (int)value; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValueBool(const std::string&) +// Purpose: Gets a key value as a boolean +// Created: 17/2/04 +// +// -------------------------------------------------------------------------- +bool Configuration::GetKeyValueBool(const std::string& rKeyName) const +{ + std::map::const_iterator i(mKeys.find(rKeyName)); + + if(i == mKeys.end()) + { + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + bool value = false; + + // Anything this is called for should have been verified as having a correct + // string in the verification section. However, this does default to false + // if it isn't in the string table. + + for(int l = 0; sValueBooleanStrings[l] != 0; ++l) + { + if(::strcasecmp((i->second).c_str(), sValueBooleanStrings[l]) == 0) + { + // Found. + value = sValueBooleanValue[l]; + break; + } + } + + return value; + } + +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyNames() +// Purpose: Returns list of key names +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +std::vector Configuration::GetKeyNames() const +{ + std::map::const_iterator i(mKeys.begin()); + + std::vector r; + + for(; i != mKeys.end(); ++i) + { + r.push_back(i->first); + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::SubConfigurationExists(const +// std::string&) +// Purpose: Checks to see if a sub configuration exists +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +bool Configuration::SubConfigurationExists(const std::string& rSubName) const +{ + // Attempt to find it... + std::list >::const_iterator i(mSubConfigurations.begin()); + + for(; i != mSubConfigurations.end(); ++i) + { + // This the one? + if(i->first == rSubName) + { + // Yes. + return true; + } + } + + // didn't find it. + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetSubConfiguration(const +// std::string&) +// Purpose: Gets a sub configuration +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +const Configuration &Configuration::GetSubConfiguration(const std::string& + rSubName) const +{ + // Attempt to find it... + std::list >::const_iterator i(mSubConfigurations.begin()); + + for(; i != mSubConfigurations.end(); ++i) + { + // This the one? + if(i->first == rSubName) + { + // Yes. + return i->second; + } + } + + THROW_EXCEPTION(CommonException, ConfigNoSubConfig) +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetSubConfiguration(const +// std::string&) +// Purpose: Gets a sub configuration for editing +// Created: 2008/08/12 +// +// -------------------------------------------------------------------------- +Configuration &Configuration::GetSubConfigurationEditable(const std::string& + rSubName) +{ + // Attempt to find it... + + for(SubConfigListType::iterator + i = mSubConfigurations.begin(); + i != mSubConfigurations.end(); ++i) + { + // This the one? + if(i->first == rSubName) + { + // Yes. + return i->second; + } + } + + THROW_EXCEPTION(CommonException, ConfigNoSubConfig) +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetSubConfigurationNames() +// Purpose: Return list of sub configuration names +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +std::vector Configuration::GetSubConfigurationNames() const +{ + std::list >::const_iterator i(mSubConfigurations.begin()); + + std::vector r; + + for(; i != mSubConfigurations.end(); ++i) + { + r.push_back(i->first); + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::Verify(const ConfigurationVerify &, const std::string &, std::string &) +// Purpose: Checks that the configuration is valid according to the +// supplied verifier +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +bool Configuration::Verify(const ConfigurationVerify &rVerify, + const std::string &rLevel, std::string &rErrorMsg) +{ + bool ok = true; + + // First... check the keys + if(rVerify.mpKeys != 0) + { + const ConfigurationVerifyKey *pvkey = rVerify.mpKeys; + + bool todo = true; + do + { + // Can the key be found? + if(KeyExists(pvkey->Name())) + { + // Get value + const std::string &rval = GetKeyValue(pvkey->Name()); + const char *val = rval.c_str(); + + // Check it's a number? + if((pvkey->Flags() & ConfigTest_IsInt) == ConfigTest_IsInt) + { + // Test it... + char *end; + long r = ::strtol(val, &end, 0); + if(r == LONG_MIN || r == LONG_MAX || end != (val + rval.size())) + { + // not a good value + ok = false; + rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid integer.\n"; + } + } + + // Check it's a number? + if(pvkey->Flags() & ConfigTest_IsUint32) + { + // Test it... + char *end; + errno = 0; + uint32_t r = ::strtoul(val, &end, 0); + if(errno != 0 || end != (val + rval.size())) + { + // not a good value + ok = false; + rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid unsigned 32-bit integer.\n"; + } + } + + // Check it's a bool? + if((pvkey->Flags() & ConfigTest_IsBool) == ConfigTest_IsBool) + { + // See if it's one of the allowed strings. + bool found = false; + for(int l = 0; sValueBooleanStrings[l] != 0; ++l) + { + if(::strcasecmp(val, sValueBooleanStrings[l]) == 0) + { + // Found. + found = true; + break; + } + } + + // Error if it's not one of them. + if(!found) + { + ok = false; + rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid boolean value.\n"; + } + } + + // Check for multi valued statments where they're not allowed + if((pvkey->Flags() & ConfigTest_MultiValueAllowed) == 0) + { + // Check to see if this key is a multi-value -- it shouldn't be + if(rval.find(MultiValueSeparator) != rval.npos) + { + ok = false; + rErrorMsg += rLevel + mName +"." + pvkey->Name() + " (key) multi value not allowed (duplicated key?).\n"; + } + } + } + else + { + // Is it required to exist? + if((pvkey->Flags() & ConfigTest_Exists) == ConfigTest_Exists) + { + // Should exist, but doesn't. + ok = false; + rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is missing.\n"; + } + else if(pvkey->HasDefaultValue()) + { + mKeys[pvkey->Name()] = pvkey->DefaultValue(); + } + } + + if((pvkey->Flags() & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + // No more! + todo = false; + } + + // next + pvkey++; + + } while(todo); + + // Check for additional keys + for(std::map::const_iterator i = mKeys.begin(); + i != mKeys.end(); ++i) + { + // Is the name in the list? + const ConfigurationVerifyKey *scan = rVerify.mpKeys; + bool found = false; + while(scan) + { + if(scan->Name() == i->first) + { + found = true; + break; + } + + // Next? + if((scan->Flags() & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + break; + } + scan++; + } + + if(!found) + { + // Shouldn't exist, but does. + ok = false; + rErrorMsg += rLevel + mName + "." + i->first + " (key) is not a known key. Check spelling and placement.\n"; + } + } + } + + // Then the sub configurations + if(rVerify.mpSubConfigurations) + { + // Find the wildcard entry, if it exists, and check that required subconfigs are there + const ConfigurationVerify *wildcardverify = 0; + + const ConfigurationVerify *scan = rVerify.mpSubConfigurations; + while(scan) + { + if(scan->mName.length() > 0 && scan->mName[0] == '*') + { + wildcardverify = scan; + } + + // Required? + if((scan->Tests & ConfigTest_Exists) == ConfigTest_Exists) + { + if(scan->mName.length() > 0 && + scan->mName[0] == '*') + { + // Check something exists + if(mSubConfigurations.size() < 1) + { + // A sub config should exist, but doesn't. + ok = false; + rErrorMsg += rLevel + mName + ".* (block) is missing (a block must be present).\n"; + } + } + else + { + // Check real thing exists + if(!SubConfigurationExists(scan->mName)) + { + // Should exist, but doesn't. + ok = false; + rErrorMsg += rLevel + mName + "." + scan->mName + " (block) is missing.\n"; + } + } + } + + // Next? + if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + break; + } + scan++; + } + + // Go through the sub configurations, one by one + for(SubConfigListType::iterator + i = mSubConfigurations.begin(); + i != mSubConfigurations.end(); ++i) + { + // Can this be found? + const ConfigurationVerify *subverify = 0; + + const ConfigurationVerify *scan = rVerify.mpSubConfigurations; + const char *name = i->first.c_str(); + ASSERT(name); + while(scan) + { + if(scan->mName == name) + { + // found it! + subverify = scan; + } + + // Next? + if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + break; + } + scan++; + } + + // Use wildcard? + if(subverify == 0) + { + subverify = wildcardverify; + } + + // Verify + if(subverify) + { + // override const-ness here... + if(!i->second.Verify(*subverify, mName + '.', + rErrorMsg)) + { + ok = false; + } + } + } + } + + return ok; +} diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h new file mode 100644 index 00000000..f9b5eb77 --- /dev/null +++ b/lib/common/Configuration.h @@ -0,0 +1,157 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Configuration +// Purpose: Reading configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- + +#ifndef CONFIGURATION__H +#define CONFIGURATION__H + +#include +#include +#include +#include +#include + +// For defining tests +enum +{ + ConfigTest_LastEntry = 1, + ConfigTest_Exists = 2, + ConfigTest_IsInt = 4, + ConfigTest_IsUint32 = 8, + ConfigTest_MultiValueAllowed = 16, + ConfigTest_IsBool = 32 +}; + +class ConfigurationCategory : public Log::Category +{ + public: + ConfigurationCategory(const std::string& name) + : Log::Category(std::string("Configuration/") + name) + { } +}; + +class ConfigurationVerifyKey +{ +public: + typedef enum + { + NoDefaultValue = 1 + } NoDefaultValue_t; + + ConfigurationVerifyKey(std::string name, int flags, + void *testFunction = NULL); + // to allow passing ConfigurationVerifyKey::NoDefaultValue + // for default ListenAddresses + ConfigurationVerifyKey(std::string name, int flags, + NoDefaultValue_t t, void *testFunction = NULL); + ConfigurationVerifyKey(std::string name, int flags, + std::string defaultValue, void *testFunction = NULL); + ConfigurationVerifyKey(std::string name, int flags, + const char* defaultValue, void *testFunction = NULL); + ConfigurationVerifyKey(std::string name, int flags, + int defaultValue, void *testFunction = NULL); + ConfigurationVerifyKey(std::string name, int flags, + bool defaultValue, void *testFunction = NULL); + const std::string& Name() const { return mName; } + const std::string& DefaultValue() const { return mDefaultValue; } + const bool HasDefaultValue() const { return mHasDefaultValue; } + const int Flags() const { return mFlags; } + const void* TestFunction() const { return mTestFunction; } + ConfigurationVerifyKey(const ConfigurationVerifyKey& rToCopy); + +private: + ConfigurationVerifyKey& operator=(const ConfigurationVerifyKey& + noAssign); + + std::string mName; // "*" for all other keys (not implemented yet) + std::string mDefaultValue; // default for when it's not present + bool mHasDefaultValue; + int mFlags; + void *mTestFunction; // set to zero for now, will implement later +}; + +class ConfigurationVerify +{ +public: + std::string mName; // "*" for all other sub config names + const ConfigurationVerify *mpSubConfigurations; + const ConfigurationVerifyKey *mpKeys; + int Tests; + void *TestFunction; // set to zero for now, will implement later + static const ConfigurationCategory VERIFY_ERROR; +}; + +class FdGetLine; + +// -------------------------------------------------------------------------- +// +// Class +// Name: Configuration +// Purpose: Loading, checking, and representing configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +class Configuration +{ +public: + Configuration(const std::string &rName); + Configuration(const Configuration &rToCopy); + ~Configuration(); + + enum + { + // The character to separate multi-values + MultiValueSeparator = '\x01' + }; + + static std::auto_ptr LoadAndVerify( + const std::string& rFilename, + const ConfigurationVerify *pVerify, + std::string &rErrorMsg); + + static std::auto_ptr Load( + const std::string& rFilename, + std::string &rErrorMsg) + { return LoadAndVerify(rFilename, 0, rErrorMsg); } + + bool KeyExists(const std::string& rKeyName) const; + const std::string &GetKeyValue(const std::string& rKeyName) const; + int GetKeyValueInt(const std::string& rKeyName) const; + int GetKeyValueInt(const std::string& rKeyName, int default_value) const; + uint32_t GetKeyValueUint32(const std::string& rKeyName) const; + bool GetKeyValueBool(const std::string& rKeyName) const; + std::vector GetKeyNames() const; + + bool SubConfigurationExists(const std::string& rSubName) const; + const Configuration &GetSubConfiguration(const std::string& rSubName) const; + Configuration &GetSubConfigurationEditable(const std::string& rSubName); + std::vector GetSubConfigurationNames() const; + + void AddKeyValue(const std::string& rKey, const std::string& rValue); + void AddSubConfig(const std::string& rName, const Configuration& rSubConfig); + + bool Verify(const ConfigurationVerify &rVerify, std::string &rErrorMsg) + { + return Verify(rVerify, std::string(), rErrorMsg); + } + +private: + std::string mName; + // Order of keys not preserved + std::map mKeys; + // Order of sub blocks preserved + typedef std::list > SubConfigListType; + SubConfigListType mSubConfigurations; + + static bool LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel); + bool Verify(const ConfigurationVerify &rVerify, const std::string &rLevel, + std::string &rErrorMsg); +}; + +#endif // CONFIGURATION__H + diff --git a/lib/common/Conversion.h b/lib/common/Conversion.h new file mode 100644 index 00000000..cba5bb08 --- /dev/null +++ b/lib/common/Conversion.h @@ -0,0 +1,98 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Conversion.h +// Purpose: Convert between various types +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- + +#ifndef CONVERSION__H +#define CONVERSION__H + +#include + +namespace BoxConvert +{ + // -------------------------------------------------------------------------- + // + // Function + // Name: BoxConvert::Convert(to_type &, from_type) + // Purpose: Convert from types to types + // Created: 9/4/04 + // + // -------------------------------------------------------------------------- + template + inline to_type Convert(from_type From) + { + // Default conversion, simply use C++ conversion + return From; + } + + // Specialise for string -> integer + int32_t _ConvertStringToInt(const char *pString, int Size); + template<> + inline int32_t Convert(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 32); + } + template<> + inline int16_t Convert(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 16); + } + template<> + inline int8_t Convert(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 8); + } + template<> + inline int32_t Convert(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 32); + } + template<> + inline int16_t Convert(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 16); + } + template<> + inline int8_t Convert(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 8); + } + + // Specialise for integer -> string + void _ConvertIntToString(std::string &rTo, int32_t From); + template<> + inline std::string Convert(int32_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + template<> + inline std::string Convert(int16_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + template<> + inline std::string Convert(int8_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + + // Specialise for bool -> string + template<> + inline std::string Convert(bool From) + { + return std::string(From?"true":"false"); + } +}; + +#endif // CONVERSION__H + diff --git a/lib/common/ConversionException.txt b/lib/common/ConversionException.txt new file mode 100644 index 00000000..91b5fa9a --- /dev/null +++ b/lib/common/ConversionException.txt @@ -0,0 +1,8 @@ + +EXCEPTION Conversion 12 + +Internal 0 +CannotConvertEmptyStringToInt 1 +BadStringRepresentationOfInt 2 +IntOverflowInConvertFromString 3 +BadIntSize 4 diff --git a/lib/common/ConversionString.cpp b/lib/common/ConversionString.cpp new file mode 100644 index 00000000..2d0a8d58 --- /dev/null +++ b/lib/common/ConversionString.cpp @@ -0,0 +1,129 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ConversionString.cpp +// Purpose: Conversions to and from strings +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include +#include + +#include "Conversion.h" +#include "autogen_ConversionException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BoxConvert::_ConvertStringToInt(const char *, int) +// Purpose: Convert from string to integer, with range checking. +// Always does signed -- no point in unsigned as C++ type checking +// isn't up to handling it properly. +// If a null pointer is passed in, then returns 0. +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- +int32_t BoxConvert::_ConvertStringToInt(const char *pString, int Size) +{ + // Handle null strings gracefully. + if(pString == 0) + { + return 0; + } + + // Check for initial validity + if(*pString == '\0') + { + THROW_EXCEPTION(ConversionException, CannotConvertEmptyStringToInt) + } + + // Convert. + char *numEnd = 0; + errno = 0; // Some platforms don't reset it. + long r = ::strtol(pString, &numEnd, 0); + + // Check that all the characters were used + if(*numEnd != '\0') + { + THROW_EXCEPTION(ConversionException, BadStringRepresentationOfInt) + } + + // Error check + if(r == 0 && errno == EINVAL) + { + THROW_EXCEPTION(ConversionException, BadStringRepresentationOfInt) + } + + // Range check from strtol + if((r == LONG_MIN || r == LONG_MAX) && errno == ERANGE) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + + // Check range for size of integer + switch(Size) + { + case 32: + { + // No extra checking needed if long is an int32 + if(sizeof(long) > sizeof(int32_t)) + { + if(r <= (0 - 0x7fffffffL) || r > 0x7fffffffL) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + } + break; + } + + case 16: + { + if(r <= (0 - 0x7fff) || r > 0x7fff) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + break; + } + + case 8: + { + if(r <= (0 - 0x7f) || r > 0x7f) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + break; + } + + default: + { + THROW_EXCEPTION(ConversionException, BadIntSize) + break; + } + } + + // Return number + return r; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BoxConvert::_ConvertIntToString(std::string &, int32_t) +// Purpose: Convert signed interger to a string +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- +void BoxConvert::_ConvertIntToString(std::string &rTo, int32_t From) +{ + char text[64]; // size more than enough + ::sprintf(text, "%d", (int)From); + rTo = text; +} + diff --git a/lib/common/Database.h b/lib/common/Database.h new file mode 100644 index 00000000..94239ab8 --- /dev/null +++ b/lib/common/Database.h @@ -0,0 +1,31 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Database.h +// Purpose: Database (QDBM) utility macros +// Created: 2010/03/10 +// +// -------------------------------------------------------------------------- + +#ifndef DATABASE__H +#define DATABASE__H + +#include "Logging.h" + +#define BOX_DBM_MESSAGE(stuff) stuff << " (qdbm): " << dperrmsg(dpecode) + +#define BOX_LOG_DBM_ERROR(stuff) \ + BOX_ERROR(BOX_DBM_MESSAGE(stuff)) + +#define THROW_DBM_ERROR(message, filename, exception, subtype) \ + BOX_LOG_DBM_ERROR(message << ": " << filename); \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + BOX_DBM_MESSAGE(message << ": " << filename)); + +#define ASSERT_DBM_OK(operation, message, filename, exception, subtype) \ + if(!(operation)) \ + { \ + THROW_DBM_ERROR(message, filename, exception, subtype); \ + } + +#endif // DATABASE__H diff --git a/lib/common/DebugAssertFailed.cpp b/lib/common/DebugAssertFailed.cpp new file mode 100644 index 00000000..e498d641 --- /dev/null +++ b/lib/common/DebugAssertFailed.cpp @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: AssertFailed.cpp +// Purpose: Assert failure code +// Created: 2003/09/04 +// +// -------------------------------------------------------------------------- + +#ifndef BOX_RELEASE_BUILD + +#include "Box.h" + +#include + +#ifdef WIN32 + #include "emu.h" +#else + #include +#endif + +#include "MemLeakFindOn.h" + +bool AssertFailuresToSyslog = false; + +void BoxDebugAssertFailed(const char *cond, const char *file, int line) +{ + printf("ASSERT FAILED: [%s] at %s(%d)\n", cond, file, line); + if(AssertFailuresToSyslog) + { + ::syslog(LOG_ERR, "ASSERT FAILED: [%s] at %s(%d)\n", cond, file, line); + } +} + + +#endif // BOX_RELEASE_BUILD + diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp new file mode 100644 index 00000000..58a82c0e --- /dev/null +++ b/lib/common/DebugMemLeakFinder.cpp @@ -0,0 +1,730 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFinder.cpp +// Purpose: Memory leak finder +// Created: 12/1/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef BOX_MEMORY_LEAK_TESTING + +#undef malloc +#undef realloc +#undef free + +#include +#include +#include +#include + +#ifdef HAVE_PROCESS_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include // for std::atexit +#include +#include + +#include "MemLeakFinder.h" + +static bool memleakfinder_initialised = false; +bool memleakfinder_global_enable = false; + +typedef struct +{ + size_t size; + const char *file; + int line; +} MallocBlockInfo; + +typedef struct +{ + size_t size; + const char *file; + int line; + bool array; +} ObjectInfo; + +namespace +{ + static std::map sMallocBlocks; + static std::map sObjectBlocks; + static bool sTrackingDataDestroyed = false; + + static class DestructionWatchdog + { + public: + ~DestructionWatchdog() + { + sTrackingDataDestroyed = true; + } + } + sWatchdog; + + static bool sTrackMallocInSection = false; + static std::set sSectionMallocBlocks; + static bool sTrackObjectsInSection = false; + static std::map sSectionObjectBlocks; + + static std::set sNotLeaks; + + void *sNotLeaksPre[1024]; + size_t sNotLeaksPreNum = 0; +} + +void memleakfinder_report_on_signal(int unused) +{ + // this is not safe! do not send SIGUSR1 to a process + // in a production environment! + memleakfinder_report_usage_summary(); +} + +void memleakfinder_init() +{ + ASSERT(!memleakfinder_initialised); + + { + // allocates a permanent buffer on Solaris. + // not a leak? + std::ostringstream oss; + } + + memleakfinder_initialised = true; + + #if defined WIN32 + // no signals, no way to trigger event yet + #else + struct sigaction newact, oldact; + newact.sa_handler = memleakfinder_report_on_signal; + newact.sa_flags = SA_RESTART; + sigemptyset(&newact.sa_mask); + if (::sigaction(SIGUSR1, &newact, &oldact) != 0) + { + BOX_ERROR("Failed to install USR1 signal handler"); + THROW_EXCEPTION(CommonException, Internal); + } + ASSERT(oldact.sa_handler == 0); + #endif // WIN32 +} + +MemLeakSuppressionGuard::MemLeakSuppressionGuard() +{ + ASSERT(memleakfinder_global_enable); + memleakfinder_global_enable = false; +} + +MemLeakSuppressionGuard::~MemLeakSuppressionGuard() +{ + ASSERT(!memleakfinder_global_enable); + memleakfinder_global_enable = true; +} + +// these functions may well allocate memory, which we don't want to track. +static int sInternalAllocDepth = 0; + +class InternalAllocGuard +{ + public: + InternalAllocGuard () { sInternalAllocDepth++; } + ~InternalAllocGuard() { sInternalAllocDepth--; } +}; + +void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int line) +{ + InternalAllocGuard guard; + + if(b != 0) + { + MallocBlockInfo i; + i.size = size; + i.file = file; + i.line = line; + sMallocBlocks[b] = i; + + if(sTrackMallocInSection) + { + sSectionMallocBlocks.insert(b); + } + } +} + +void *memleakfinder_malloc(size_t size, const char *file, int line) +{ + InternalAllocGuard guard; + + void *b = std::malloc(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(b); + return b; + } + + if(!memleakfinder_initialised) return b; + + memleakfinder_malloc_add_block(b, size, file, line); + + //TRACE4("malloc(), %d, %s, %d, %08x\n", size, file, line, b); + return b; +} + +void *memleakfinder_calloc(size_t blocks, size_t size, const char *file, int line) +{ + void *block = memleakfinder_malloc(blocks * size, file, line); + if (block != 0) + { + memset(block, 0, blocks * size); + } + return block; +} + +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) + { + 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::iterator i(sMallocBlocks.find(ptr)); + std::set::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(i != sMallocBlocks.end()) + { + // Worked? + if(b != 0) + { + // Update map + MallocBlockInfo inf = i->second; + inf.size = size; + sMallocBlocks.erase(i); + sMallocBlocks[b] = inf; + + if(sTrackMallocInSection) + { + std::set::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + sSectionMallocBlocks.insert(b); + } + } + } + else + { + memleakfinder_malloc_add_block(b, size, "FOUND-IN-REALLOC", 0); + } + + //TRACE3("realloc(), %d, %08x->%08x\n", size, ptr, b); + return b; +} + +void memleakfinder_free(void *ptr) +{ + InternalAllocGuard guard; + + if(memleakfinder_global_enable && memleakfinder_initialised) + { + // Check it's been allocated + std::map::iterator i(sMallocBlocks.find(ptr)); + std::set::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 " + "known. Error? Or allocated in startup " + "static allocation?"); + } + + if(sTrackMallocInSection) + { + std::set::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + } + } + + //TRACE1("free(), %08x\n", ptr); + std::free(ptr); +} + + +void memleakfinder_notaleak_insert_pre() +{ + InternalAllocGuard guard; + + if(!memleakfinder_global_enable) return; + if(!memleakfinder_initialised) return; + + for(size_t l = 0; l < sNotLeaksPreNum; l++) + { + sNotLeaks.insert(sNotLeaksPre[l]); + } + + sNotLeaksPreNum = 0; +} + +bool is_leak(void *ptr) +{ + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); + memleakfinder_notaleak_insert_pre(); + return sNotLeaks.find(ptr) == sNotLeaks.end(); +} + +void memleakfinder_notaleak(void *ptr) +{ + InternalAllocGuard guard; + + ASSERT(!sTrackingDataDestroyed); + + memleakfinder_notaleak_insert_pre(); + + if(memleakfinder_initialised) + { + sNotLeaks.insert(ptr); + } + else + { + if ( sNotLeaksPreNum < + sizeof(sNotLeaksPre)/sizeof(*sNotLeaksPre) ) + sNotLeaksPre[sNotLeaksPreNum++] = ptr; + } + + /* + { + std::map::iterator i(sMallocBlocks.find(ptr)); + if(i != sMallocBlocks.end()) sMallocBlocks.erase(i); + } + { + std::set::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + } + { + std::map::iterator i(sObjectBlocks.find(ptr)); + if(i != sObjectBlocks.end()) sObjectBlocks.erase(i); + }*/ +} + + + +// start monitoring a section of code +void memleakfinder_startsectionmonitor() +{ + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); + ASSERT(!sTrackingDataDestroyed); + + sTrackMallocInSection = true; + sSectionMallocBlocks.clear(); + sTrackObjectsInSection = true; + sSectionObjectBlocks.clear(); +} + +// trace all blocks allocated and still allocated since memleakfinder_startsectionmonitor() called +void memleakfinder_traceblocksinsection() +{ + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); + ASSERT(!sTrackingDataDestroyed); + + std::set::iterator s(sSectionMallocBlocks.begin()); + for(; s != sSectionMallocBlocks.end(); ++s) + { + std::map::const_iterator i(sMallocBlocks.find(*s)); + if(i == sMallocBlocks.end()) + { + BOX_WARNING("Logical error in section block finding"); + } + else + { + BOX_TRACE("Block " << i->first << " size " << + i->second.size << " allocated at " << + i->second.file << ":" << i->second.line); + } + } + for(std::map::const_iterator i(sSectionObjectBlocks.begin()); i != sSectionObjectBlocks.end(); ++i) + { + BOX_TRACE("Object" << (i->second.array?" []":"") << " " << + i->first << " size " << i->second.size << + " allocated at " << i->second.file << + ":" << i->second.line); + } +} + +int memleakfinder_numleaks() +{ + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); + ASSERT(!sTrackingDataDestroyed); + + int n = 0; + + for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) + { + if(is_leak(i->first)) ++n; + } + + for(std::map::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) + { + const ObjectInfo& rInfo = i->second; + if(is_leak(i->first)) ++n; + } + + return n; +} + +// Summarise all blocks allocated and still allocated, for memory usage +// diagnostics. +void memleakfinder_report_usage_summary() +{ + InternalAllocGuard guard; + + ASSERT(!sTrackingDataDestroyed); + + typedef std::map > usage_map_t; + usage_map_t usage; + + for(std::map::const_iterator + i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) + { + std::ostringstream buf; + buf << i->second.file << ":" << i->second.line; + std::string key = buf.str(); + + usage_map_t::iterator ui = usage.find(key); + if(ui == usage.end()) + { + usage[key] = std::pair(1, + i->second.size); + } + else + { + ui->second.first++; + ui->second.second += i->second.size; + } + } + + for(std::map::const_iterator + i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) + { + std::ostringstream buf; + buf << i->second.file << ":" << i->second.line; + std::string key = buf.str(); + + usage_map_t::iterator ui = usage.find(key); + if(ui == usage.end()) + { + usage[key] = std::pair(1, + i->second.size); + } + else + { + ui->second.first++; + ui->second.second += i->second.size; + } + } + + #ifndef DEBUG_LEAKS + BOX_WARNING("Memory use: support not compiled in :("); + #else + if(usage.empty()) + { + BOX_WARNING("Memory use: none detected?!"); + } + else + { + uint64_t blocks = 0, bytes = 0; + BOX_WARNING("Memory use: report follows"); + + for(usage_map_t::iterator i = usage.begin(); i != usage.end(); + i++) + { + BOX_WARNING("Memory use: " << i->first << ": " << + i->second.first << " blocks, " << + i->second.second << " bytes"); + blocks += i->second.first; + bytes += i->second.second; + } + + BOX_WARNING("Memory use: report ends, total: " << blocks << + " blocks, " << bytes << " bytes"); + } + #endif // DEBUG_LEAKS +} + +void memleakfinder_reportleaks_file(FILE *file) +{ + InternalAllocGuard guard; + + ASSERT(!sTrackingDataDestroyed); + + for(std::map::const_iterator + i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) + { + if(is_leak(i->first)) + { + ::fprintf(file, "Block %p size %d allocated at " + "%s:%d\n", i->first, i->second.size, + i->second.file, i->second.line); + } + } + + for(std::map::const_iterator + i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) + { + if(is_leak(i->first)) + { + ::fprintf(file, "Object%s %p size %d allocated at " + "%s:%d\n", i->second.array?" []":"", + i->first, i->second.size, i->second.file, + i->second.line); + } + } +} + +void memleakfinder_reportleaks() +{ + InternalAllocGuard guard; + + // report to stdout + memleakfinder_reportleaks_file(stdout); +} + +void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext) +{ + InternalAllocGuard guard; + + FILE *file = ::fopen(filename, "a"); + if(file != 0) + { + if(memleakfinder_numleaks() > 0) + { +#ifdef HAVE_GETPID + fprintf(file, "MEMORY LEAKS FROM PROCESS %d (%s)\n", getpid(), markertext); +#else + fprintf(file, "MEMORY LEAKS (%s)\n", markertext); +#endif + memleakfinder_reportleaks_file(file); + } + + ::fclose(file); + } + else + { + BOX_WARNING("Couldn't open memory leak results file " << + filename << " for appending"); + } +} + +static char atexit_filename[512]; +static char atexit_markertext[512]; +static bool atexit_registered = false; + +extern "C" void memleakfinder_atexit() +{ + memleakfinder_reportleaks_appendfile(atexit_filename, atexit_markertext); +} + +void memleakfinder_setup_exit_report(const std::string& filename, + const char *markertext) +{ + 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; + if(!atexit_registered) + { + std::atexit(memleakfinder_atexit); + atexit_registered = true; + } +} + +void add_object_block(void *block, size_t size, const char *file, int line, bool array) +{ + InternalAllocGuard guard; + + if(!memleakfinder_global_enable) return; + if(!memleakfinder_initialised) return; + ASSERT(!sTrackingDataDestroyed); + + if(block != 0) + { + std::map::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; + i.line = line; + i.array = array; + sObjectBlocks[block] = i; + + if(sTrackObjectsInSection) + { + sSectionObjectBlocks[block] = i; + } + } +} + +void remove_object_block(void *block) +{ + InternalAllocGuard guard; + + if(!memleakfinder_global_enable) return; + if(!memleakfinder_initialised) return; + if(sTrackingDataDestroyed) return; + + std::map::iterator i(sObjectBlocks.find(block)); + if(i != sObjectBlocks.end()) + { + sObjectBlocks.erase(i); + } + + if(sTrackObjectsInSection) + { + std::map::iterator i(sSectionObjectBlocks.find(block)); + if(i != sSectionObjectBlocks.end()) + { + sSectionObjectBlocks.erase(i); + } + } + + // If it's not in the list, just ignore it, as lots of stuff goes this way... +} + +static void *internal_new(size_t size, const char *file, int line) +{ + void *r; + + { + InternalAllocGuard guard; + r = std::malloc(size); + } + + if (sInternalAllocDepth == 0) + { + InternalAllocGuard guard; + add_object_block(r, size, file, line, false); + //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r); + } + + return r; +} + +void *operator new(size_t size, const char *file, int line) +{ + return internal_new(size, file, line); +} + +void *operator new[](size_t size, const char *file, int line) +{ + return internal_new(size, file, line); +} + +// where there is no doctor... need to override standard new() too +// http://www.relisoft.com/book/tech/9new.html +// disabled because it causes hangs on FC2 in futex() in test/common +// while reading files. reason unknown. +/* +void *operator new(size_t size) +{ + return internal_new(size, "standard libraries", 0); +} +*/ + +void *operator new[](size_t size) throw (std::bad_alloc) +{ + return internal_new(size, "standard libraries", 0); +} + +void internal_delete(void *ptr) +{ + InternalAllocGuard guard; + + std::free(ptr); + remove_object_block(ptr); + //TRACE1("delete[]() called, %08x\n", ptr); +} + +void operator delete[](void *ptr) throw () +{ + internal_delete(ptr); +} + +void operator delete(void *ptr) throw () +{ + internal_delete(ptr); +} + +#endif // BOX_MEMORY_LEAK_TESTING diff --git a/lib/common/DebugPrintf.cpp b/lib/common/DebugPrintf.cpp new file mode 100644 index 00000000..1335d473 --- /dev/null +++ b/lib/common/DebugPrintf.cpp @@ -0,0 +1,83 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: DebugPrintf.cpp +// Purpose: Implementation of a printf function, to avoid a stdio.h include in Box.h +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- + +#ifndef BOX_RELEASE_BUILD + +#include "Box.h" + +#include +#include + +#ifdef WIN32 + #include "emu.h" +#else + #include +#endif + +#include "MemLeakFindOn.h" + +// Use this apparently superflous printf function to avoid having to +// include stdio.h in every file in the project. + +int BoxDebug_printf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + int r = vprintf(format, ap); + va_end(ap); + return r; +} + + +bool BoxDebugTraceOn = true; +bool BoxDebugTraceToStdout = true; +bool BoxDebugTraceToSyslog = false; + +int BoxDebugTrace(const char *format, ...) +{ + char text[512]; + int r = 0; + if(BoxDebugTraceOn || BoxDebugTraceToSyslog) + { + va_list ap; + va_start(ap, format); + r = vsnprintf(text, sizeof(text), format, ap); + va_end(ap); + } + + // Send to stdout if trace is on and std out is enabled + if(BoxDebugTraceOn && BoxDebugTraceToStdout) + { + printf("%s", text); + } + + // But tracing to syslog is independent of tracing being on or not + if(BoxDebugTraceToSyslog) + { +#ifdef WIN32 + // Remove trailing '\n', if it's there + if(r > 0 && text[r-1] == '\n') + { + text[r-1] = '\0'; +#else + if(r > 0 && text[r] == '\n') + { + text[r] = '\0'; +#endif + --r; + } + // Log it + ::syslog(LOG_INFO, "TRACE: %s", text); + } + + return r; +} + + +#endif // BOX_RELEASE_BUILD diff --git a/lib/common/EndStructPackForWire.h b/lib/common/EndStructPackForWire.h new file mode 100644 index 00000000..82637f33 --- /dev/null +++ b/lib/common/EndStructPackForWire.h @@ -0,0 +1,23 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: EndStructPackForWire.h +// Purpose: End structure packing for wire +// Created: 25/11/03 +// +// -------------------------------------------------------------------------- + +// No header guard -- this is intentional + +#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS + +#pragma pack() + +#else + + logical error -- check BoxPlatform.h and including file + +#endif + + + diff --git a/lib/common/ExcludeList.cpp b/lib/common/ExcludeList.cpp new file mode 100644 index 00000000..3f9f69ee --- /dev/null +++ b/lib/common/ExcludeList.cpp @@ -0,0 +1,480 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ExcludeList.cpp +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_REGEX_SUPPORT + #if defined HAVE_PCREPOSIX_H + #include + #elif defined HAVE_REGEX_H + #include + #endif + #define EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED +#endif + +#include "ExcludeList.h" +#include "Utils.h" +#include "Configuration.h" +#include "Archive.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::ExcludeList() +// Purpose: Constructor. Generates an exclude list which will allow everything +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +ExcludeList::ExcludeList() + : mpAlwaysInclude(0) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::~ExcludeList() +// Purpose: Destructor +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +ExcludeList::~ExcludeList() +{ +#ifdef HAVE_REGEX_SUPPORT + // free regex memory + while(mRegex.size() > 0) + { + regex_t *pregex = mRegex.back(); + mRegex.pop_back(); + // Free regex storage, and the structure itself + ::regfree(pregex); + delete pregex; + } +#endif + + // Clean up exceptions list + if(mpAlwaysInclude != 0) + { + delete mpAlwaysInclude; + mpAlwaysInclude = 0; + } +} + +#ifdef WIN32 +std::string ExcludeList::ReplaceSlashesDefinite(const std::string& input) const +{ + std::string output = input; + + for (std::string::size_type pos = output.find("/"); + pos != std::string::npos; + pos = output.find("/")) + { + output.replace(pos, 1, DIRECTORY_SEPARATOR); + } + + for (std::string::iterator i = output.begin(); i != output.end(); i++) + { + *i = tolower(*i); + } + + return output; +} + +std::string ExcludeList::ReplaceSlashesRegex(const std::string& input) const +{ + std::string output = input; + + for (std::string::size_type pos = output.find("/"); + pos != std::string::npos; + pos = output.find("/")) + { + output.replace(pos, 1, "\\" DIRECTORY_SEPARATOR); + } + + return output; +} +#endif + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::AddDefiniteEntries(const std::string &) +// Purpose: Adds a number of definite entries to the exclude list -- ones which +// will be excluded if and only if the test string matches exactly. +// Uses the Configuration classes' multi-value conventions, with +// multiple entires in one string separated by Configuration::MultiValueSeparator +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +void ExcludeList::AddDefiniteEntries(const std::string &rEntries) +{ + // Split strings up + std::vector ens; + SplitString(rEntries, Configuration::MultiValueSeparator, ens); + + // Add to set of excluded strings + for(std::vector::const_iterator i(ens.begin()); i != ens.end(); ++i) + { + if(i->size() > 0) + { + std::string entry = *i; + + // Convert any forward slashes in the string + // to backslashes + + #ifdef WIN32 + entry = ReplaceSlashesDefinite(entry); + #endif + + if (entry.size() > 0 && entry[entry.size() - 1] == + DIRECTORY_SEPARATOR_ASCHAR) + { + BOX_LOG_CATEGORY(Log::WARNING, + ConfigurationVerify::VERIFY_ERROR, + "Exclude entry ends in path separator, " + "will never match: " << entry); + } + + mDefinite.insert(entry); + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::AddRegexEntries(const std::string &) +// Purpose: Adds a number of regular expression entries to the exclude list -- +// if the test expression matches any of these regex, it will be excluded. +// Uses the Configuration classes' multi-value conventions, with +// multiple entires in one string separated by Configuration::MultiValueSeparator +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +void ExcludeList::AddRegexEntries(const std::string &rEntries) +{ +#ifdef HAVE_REGEX_SUPPORT + + // Split strings up + std::vector ens; + SplitString(rEntries, Configuration::MultiValueSeparator, ens); + + // Create and add new regular expressions + for(std::vector::const_iterator i(ens.begin()); i != ens.end(); ++i) + { + if(i->size() > 0) + { + // Allocate memory + regex_t *pregex = new regex_t; + + try + { + std::string entry = *i; + int flags = REG_EXTENDED | REG_NOSUB; + + // Convert any forward slashes in the string + // to appropriately escaped backslashes + + #ifdef WIN32 + entry = ReplaceSlashesRegex(entry); + flags |= REG_ICASE; // Windows convention + #endif + + // Compile + int errcode = ::regcomp(pregex, entry.c_str(), + flags); + + if (errcode != 0) + { + char buf[1024]; + regerror(errcode, pregex, buf, sizeof(buf)); + THROW_EXCEPTION_MESSAGE(CommonException, BadRegularExpression, + "Invalid regular expression: " << + entry << ": " << buf); + } + + // Store in list of regular expressions + mRegex.push_back(pregex); + // Store in list of regular expression string for Serialize + mRegexStr.push_back(entry.c_str()); + } + catch(...) + { + delete pregex; + throw; + } + } + } + +#else + THROW_EXCEPTION(CommonException, RegexNotSupportedOnThisPlatform) +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::IsExcluded(const std::string &) +// Purpose: Returns true if the entry should be excluded +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +bool ExcludeList::IsExcluded(const std::string &rTest) const +{ + std::string test = rTest; + + #ifdef WIN32 + // converts to lower case as well + test = ReplaceSlashesDefinite(test); + #endif + + // Check against the always include list + if(mpAlwaysInclude != 0) + { + if(mpAlwaysInclude->IsExcluded(test)) + { + // Because the "always include" list says it's 'excluded' + // this means it should actually be included. + return false; + } + } + + // Is it in the set of definite entries? + if(mDefinite.find(test) != mDefinite.end()) + { + return true; + } + + // Check against regular expressions +#ifdef HAVE_REGEX_SUPPORT + for(std::vector::const_iterator i(mRegex.begin()); i != mRegex.end(); ++i) + { + // Test against this expression + if(regexec(*i, test.c_str(), 0, 0 /* no match information required */, 0 /* no flags */) == 0) + { + // match happened + return true; + } + // In all other cases, including an error, just continue to the next expression + } +#endif + + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::SetAlwaysIncludeList(ExcludeList *) +// Purpose: Takes ownership of the list, deletes any pre-existing list. +// NULL is acceptable to delete the list. +// The AlwaysInclude list is a list of exceptions to the exclusions. +// Created: 19/2/04 +// +// -------------------------------------------------------------------------- +void ExcludeList::SetAlwaysIncludeList(ExcludeList *pAlwaysInclude) +{ + // Delete old list + if(mpAlwaysInclude != 0) + { + delete mpAlwaysInclude; + mpAlwaysInclude = 0; + } + + // Store the pointer + mpAlwaysInclude = pAlwaysInclude; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::Deserialize(Archive & rArchive) +// Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction. +// +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- +void ExcludeList::Deserialize(Archive & rArchive) +{ + // + // + // + mDefinite.clear(); + +#ifdef HAVE_REGEX_SUPPORT + // free regex memory + while(mRegex.size() > 0) + { + regex_t *pregex = mRegex.back(); + mRegex.pop_back(); + // Free regex storage, and the structure itself + ::regfree(pregex); + delete pregex; + } + + mRegexStr.clear(); +#endif + + // Clean up exceptions list + if(mpAlwaysInclude != 0) + { + delete mpAlwaysInclude; + mpAlwaysInclude = 0; + } + + // + // + // + int64_t iCount = 0; + rArchive.Read(iCount); + + if (iCount > 0) + { + for (int v = 0; v < iCount; v++) + { + // load each one + std::string strItem; + rArchive.Read(strItem); + mDefinite.insert(strItem); + } + } + + // + // + // +#ifdef HAVE_REGEX_SUPPORT + rArchive.Read(iCount); + + if (iCount > 0) + { + for (int v = 0; v < iCount; v++) + { + std::string strItem; + rArchive.Read(strItem); + + // Allocate memory + regex_t* pregex = new regex_t; + + try + { + // Compile + if(::regcomp(pregex, strItem.c_str(), + REG_EXTENDED | REG_NOSUB) != 0) + { + THROW_EXCEPTION(CommonException, + BadRegularExpression) + } + + // Store in list of regular expressions + mRegex.push_back(pregex); + + // Store in list of regular expression strings + // for Serialize + mRegexStr.push_back(strItem); + } + catch(...) + { + delete pregex; + throw; + } + } + } +#endif // HAVE_REGEX_SUPPORT + + // + // + // + int64_t aMagicMarker = 0; + rArchive.Read(aMagicMarker); + + if (aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP) + { + // NOOP + } + else if (aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE) + { + mpAlwaysInclude = new ExcludeList; + if (!mpAlwaysInclude) + { + throw std::bad_alloc(); + } + + mpAlwaysInclude->Deserialize(rArchive); + } + else + { + // there is something going on here + THROW_EXCEPTION(CommonException, Internal) + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::Serialize(Archive & rArchive) +// Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction. +// +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- +void ExcludeList::Serialize(Archive & rArchive) const +{ + // + // + // + int64_t iCount = mDefinite.size(); + rArchive.Write(iCount); + + for (std::set::const_iterator i = mDefinite.begin(); + i != mDefinite.end(); i++) + { + rArchive.Write(*i); + } + + // + // + // +#ifdef HAVE_REGEX_SUPPORT + // don't even try to save compiled regular expressions, + // use string copies instead. + ASSERT(mRegex.size() == mRegexStr.size()); + + iCount = mRegexStr.size(); + rArchive.Write(iCount); + + for (std::vector::const_iterator i = mRegexStr.begin(); + i != mRegexStr.end(); i++) + { + rArchive.Write(*i); + } +#endif // HAVE_REGEX_SUPPORT + + // + // + // + if (!mpAlwaysInclude) + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP; + rArchive.Write(aMagicMarker); + } + else + { + int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows + rArchive.Write(aMagicMarker); + + mpAlwaysInclude->Serialize(rArchive); + } +} diff --git a/lib/common/ExcludeList.h b/lib/common/ExcludeList.h new file mode 100644 index 00000000..3c41bd11 --- /dev/null +++ b/lib/common/ExcludeList.h @@ -0,0 +1,76 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ExcludeList.h +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef EXCLUDELIST__H +#define EXCLUDELIST__H + +#include +#include +#include + +// avoid including regex.h in lots of places +#ifndef EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED + typedef int regex_t; +#endif + +class Archive; + +// -------------------------------------------------------------------------- +// +// Class +// Name: ExcludeList +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +class ExcludeList +{ +public: + ExcludeList(); + ~ExcludeList(); + + void Deserialize(Archive & rArchive); + void Serialize(Archive & rArchive) const; + + void AddDefiniteEntries(const std::string &rEntries); + void AddRegexEntries(const std::string &rEntries); + + // Add exceptions to the exclusions (takes ownership) + void SetAlwaysIncludeList(ExcludeList *pAlwaysInclude); + + // Test function + bool IsExcluded(const std::string &rTest) const; + + // Mainly for tests + unsigned int SizeOfDefiniteList() const {return mDefinite.size();} + unsigned int SizeOfRegexList() const +#ifdef HAVE_REGEX_SUPPORT + {return mRegex.size();} +#else + {return 0;} +#endif + +private: + std::set mDefinite; +#ifdef HAVE_REGEX_SUPPORT + std::vector mRegex; + std::vector mRegexStr; // save original regular expression string-based source for Serialize +#endif + +#ifdef WIN32 + std::string ReplaceSlashesDefinite(const std::string& input) const; + std::string ReplaceSlashesRegex (const std::string& input) const; +#endif + + // For exceptions to the excludes + ExcludeList *mpAlwaysInclude; +}; + +#endif // EXCLUDELIST__H + diff --git a/lib/common/FdGetLine.cpp b/lib/common/FdGetLine.cpp new file mode 100644 index 00000000..30409d92 --- /dev/null +++ b/lib/common/FdGetLine.cpp @@ -0,0 +1,142 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FdGetLine.cpp +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include "FdGetLine.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::FdGetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +FdGetLine::FdGetLine(int fd) +: mFileHandle(fd) +{ + if(mFileHandle < 0) {THROW_EXCEPTION(CommonException, BadArguments)} + //printf("FdGetLine buffer size = %d\n", sizeof(mBuffer)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::~FdGetLine() +// Purpose: Destructor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +FdGetLine::~FdGetLine() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::GetLine(bool) +// Purpose: Returns a file from the file. If Preprocess is true, leading +// and trailing whitespace is removed, and comments (after #) +// are deleted. +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +std::string FdGetLine::GetLine(bool Preprocess) +{ + if(mFileHandle == -1) {THROW_EXCEPTION(CommonException, GetLineNoHandle)} + + std::string r; + bool result = GetLineInternal(r, Preprocess); + + if(!result) + { + // should never fail for FdGetLine + THROW_EXCEPTION(CommonException, Internal); + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::ReadMore() +// Purpose: Read more bytes from the handle, possible the +// console, into mBuffer and return the number of +// bytes read, 0 on EOF or -1 on error. +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- +int FdGetLine::ReadMore(int Timeout) +{ + int bytes; + +#ifdef WIN32 + if (mFileHandle == _fileno(stdin)) + { + bytes = console_read(mBuffer, sizeof(mBuffer)); + } + else + { + bytes = ::read(mFileHandle, mBuffer, sizeof(mBuffer)); + } +#else // !WIN32 + bytes = ::read(mFileHandle, mBuffer, sizeof(mBuffer)); +#endif // WIN32 + + if(bytes == 0) + { + mPendingEOF = true; + } + + return bytes; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::DetachFile() +// Purpose: Detaches the file handle, setting the file pointer correctly. +// Probably not good for sockets... +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +void FdGetLine::DetachFile() +{ + if(mFileHandle == -1) {THROW_EXCEPTION(CommonException, GetLineNoHandle)} + + // Adjust file pointer + int bytesOver = mBufferBegin - mBufferBegin; + ASSERT(bytesOver >= 0); + if(bytesOver > 0) + { + if(::lseek(mFileHandle, 0 - bytesOver, SEEK_CUR) == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + } + + // Unset file pointer + mFileHandle = -1; +} + diff --git a/lib/common/FdGetLine.h b/lib/common/FdGetLine.h new file mode 100644 index 00000000..2b9c268f --- /dev/null +++ b/lib/common/FdGetLine.h @@ -0,0 +1,50 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FdGetLine.h +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#ifndef FDGETLINE__H +#define FDGETLINE__H + +#include + +#include "GetLine.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: FdGetLine +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +class FdGetLine : public GetLine +{ +public: + FdGetLine(int fd); + virtual ~FdGetLine(); +private: + FdGetLine(const FdGetLine &rToCopy); + +public: + virtual std::string GetLine(bool Preprocess = false); + // Call to detach, setting file pointer correctly to last bit read. + // Only works for lseek-able file descriptors. + void DetachFile(); + // if we read 0 bytes from an fd, it must be end of stream, + // because we don't support timeouts + virtual bool IsStreamDataLeft() { return false; } + +protected: + int ReadMore(int Timeout = IOStream::TimeOutInfinite); + +private: + int mFileHandle; +}; + +#endif // FDGETLINE__H + diff --git a/lib/common/FileModificationTime.cpp b/lib/common/FileModificationTime.cpp new file mode 100644 index 00000000..50f1fb62 --- /dev/null +++ b/lib/common/FileModificationTime.cpp @@ -0,0 +1,70 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileModificationTime.cpp +// Purpose: Function for getting file modification time. +// Created: 2010/02/15 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "BoxTime.h" +#include "FileModificationTime.h" + +#include "MemLeakFindOn.h" + +box_time_t FileModificationTime(const EMU_STRUCT_STAT &st) +{ +#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; +} + +box_time_t FileAttrModificationTime(const EMU_STRUCT_STAT &st) +{ + box_time_t statusmodified = +#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 + (((int64_t)st.st_ctim.tv_nsec) / (NANO_SEC_IN_USEC_LL)) + + (((int64_t)st.st_ctim.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC + (((int64_t)st.st_ctimensec) / (NANO_SEC_IN_USEC_LL)) + + (((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL)); +#else // no nanoseconds anywhere + (((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL)); +#endif + + return statusmodified; +} + +box_time_t FileModificationTimeMaxModAndAttr(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); + box_time_t statusmodified = ((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL); +#else + 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)); + box_time_t statusmodified = (((int64_t)st.st_ctimespec.tv_nsec) / NANO_SEC_IN_USEC_LL) + + (((int64_t)st.st_ctimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#endif + + return (datamodified > statusmodified)?datamodified:statusmodified; +} + diff --git a/lib/common/FileModificationTime.h b/lib/common/FileModificationTime.h new file mode 100644 index 00000000..85424842 --- /dev/null +++ b/lib/common/FileModificationTime.h @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileModificationTime.h +// Purpose: Function for getting file modification time. +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- + +#ifndef FILEMODIFICATIONTIME__H +#define FILEMODIFICATIONTIME__H + +#include + +#include "BoxTime.h" + +box_time_t FileModificationTime(const EMU_STRUCT_STAT &st); +box_time_t FileAttrModificationTime(const EMU_STRUCT_STAT &st); +box_time_t FileModificationTimeMaxModAndAttr(const EMU_STRUCT_STAT &st); + +#endif // FILEMODIFICATIONTIME__H + diff --git a/lib/common/FileStream.cpp b/lib/common/FileStream.cpp new file mode 100644 index 00000000..51752f85 --- /dev/null +++ b/lib/common/FileStream.cpp @@ -0,0 +1,465 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileStream.cpp +// Purpose: IOStream interface to files +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "FileStream.h" +#include "CommonException.h" +#include "Logging.h" + +#include + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(const char *, int, int) +// Purpose: Constructor, opens file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(const std::string& rFilename, int flags, int mode) +#ifdef WIN32 + : mOSFileHandle(::openfile(rFilename.c_str(), flags, mode)), +#else + : mOSFileHandle(::open(rFilename.c_str(), flags, mode)), +#endif + mIsEOF(false), + mFileName(rFilename) +{ + AfterOpen(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(const char *, int, int) +// Purpose: Alternative constructor, takes a const char *, +// avoids const strings being interpreted as handles! +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(const char *pFilename, int flags, int mode) +#ifdef WIN32 + : mOSFileHandle(::openfile(pFilename, flags, mode)), +#else + : mOSFileHandle(::open(pFilename, flags, mode)), +#endif + mIsEOF(false), + mFileName(pFilename) +{ + AfterOpen(); +} + +void FileStream::AfterOpen() +{ +#ifdef WIN32 + if(mOSFileHandle == INVALID_HANDLE_VALUE) +#else + if(mOSFileHandle < 0) +#endif + { + MEMLEAKFINDER_NOT_A_LEAK(this); + +#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_SYS_FILE_ERROR("Failed to open file", mFileName, + CommonException, AccessDenied); + } + else + { + THROW_SYS_FILE_ERROR("Failed to open file", mFileName, + CommonException, OSFileOpenError); + } +#endif + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(tOSFileHandle) +// Purpose: Constructor, using existing file descriptor +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(tOSFileHandle FileDescriptor) + : mOSFileHandle(FileDescriptor), + mIsEOF(false), + mFileName("HANDLE") +{ +#ifdef WIN32 + if(mOSFileHandle == INVALID_HANDLE_VALUE) +#else + if(mOSFileHandle < 0) +#endif + { + MEMLEAKFINDER_NOT_A_LEAK(this); + BOX_ERROR("FileStream: called with invalid file handle"); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +} + +#if 0 +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(const FileStream &) +// Purpose: Copy constructor, creates a duplicate of the file handle +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(const FileStream &rToCopy) + : mOSFileHandle(::dup(rToCopy.mOSFileHandle)), + mIsEOF(rToCopy.mIsEOF) +{ +#ifdef WIN32 + if(mOSFileHandle == INVALID_HANDLE_VALUE) +#else + if(mOSFileHandle < 0) +#endif + { + MEMLEAKFINDER_NOT_A_LEAK(this); + BOX_ERROR("FileStream: copying unopened file"); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +} +#endif // 0 + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::~FileStream() +// Purpose: Destructor, closes file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::~FileStream() +{ + if(mOSFileHandle != INVALID_FILE) + { + Close(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Read(void *, int) +// Purpose: Reads bytes from the file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +int FileStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + if(mOSFileHandle == INVALID_FILE) + { + THROW_EXCEPTION(CommonException, FileClosed) + } + +#ifdef WIN32 + int r; + DWORD numBytesRead = 0; + BOOL valid = ReadFile( + this->mOSFileHandle, + pBuffer, + NBytes, + &numBytesRead, + NULL + ); + + if(valid) + { + r = numBytesRead; + } + else if(GetLastError() == ERROR_BROKEN_PIPE) + { + r = 0; + } + else + { + THROW_WIN_FILE_ERROR("Failed to read from file", mFileName, + CommonException, OSFileReadError); + } + + if(r == -1) + { + THROW_EXCEPTION(CommonException, OSFileReadError) + } +#else + int r = ::read(mOSFileHandle, pBuffer, NBytes); + if(r == -1) + { + THROW_SYS_FILE_ERROR("Failed to read from file", mFileName, + CommonException, OSFileReadError); + } +#endif + + if(r == 0) + { + mIsEOF = true; + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::BytesLeftToRead() +// Purpose: Returns number of bytes to read (may not be most efficient function ever) +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +IOStream::pos_type FileStream::BytesLeftToRead() +{ + EMU_STRUCT_STAT st; + if(EMU_FSTAT(mOSFileHandle, &st) != 0) + { + BOX_LOG_SYS_ERROR(BOX_FILE_MESSAGE("Failed to stat file", mFileName)); + } + + return st.st_size - GetPosition(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Write(void *, int) +// Purpose: Writes bytes to the file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void FileStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + if(mOSFileHandle == INVALID_FILE) + { + THROW_EXCEPTION(CommonException, FileClosed) + } + +#ifdef WIN32 + DWORD numBytesWritten = 0; + BOOL res = WriteFile( + this->mOSFileHandle, + pBuffer, + NBytes, + &numBytesWritten, + NULL + ); + + if ((res == 0) || (numBytesWritten != (DWORD)NBytes)) + { + THROW_WIN_FILE_ERROR("Failed to write to file", mFileName, + CommonException, OSFileWriteError); + } +#else + if(::write(mOSFileHandle, pBuffer, NBytes) != NBytes) + { + THROW_SYS_FILE_ERROR("Failed to write to file", mFileName, + CommonException, OSFileWriteError); + } +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::GetPosition() +// Purpose: Get position in stream +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type FileStream::GetPosition() const +{ + if(mOSFileHandle == INVALID_FILE) + { + THROW_EXCEPTION(CommonException, FileClosed) + } + +#ifdef WIN32 + LARGE_INTEGER conv; + conv.HighPart = 0; + conv.LowPart = SetFilePointer(this->mOSFileHandle, 0, &conv.HighPart, FILE_CURRENT); + + if(conv.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + { + THROW_WIN_FILE_ERROR("Failed to seek in file", mFileName, + CommonException, OSFileError); + } + + return (IOStream::pos_type)conv.QuadPart; +#else // ! WIN32 + off_t p = ::lseek(mOSFileHandle, 0, SEEK_CUR); + if(p == -1) + { + THROW_SYS_FILE_ERROR("Failed to seek in file", mFileName, + CommonException, OSFileError); + } + + return (IOStream::pos_type)p; +#endif // WIN32 +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Seek(pos_type, int) +// Purpose: Seeks within file, as lseek +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void FileStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + if(mOSFileHandle == INVALID_FILE) + { + THROW_EXCEPTION(CommonException, FileClosed) + } + +#ifdef WIN32 + LARGE_INTEGER conv; + conv.QuadPart = Offset; + DWORD retVal = SetFilePointer(this->mOSFileHandle, conv.LowPart, &conv.HighPart, ConvertSeekTypeToOSWhence(SeekType)); + + if(retVal == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + { + THROW_WIN_FILE_ERROR("Failed to seek in file", mFileName, + CommonException, OSFileError); + } +#else // ! WIN32 + if(::lseek(mOSFileHandle, Offset, ConvertSeekTypeToOSWhence(SeekType)) == -1) + { + THROW_SYS_FILE_ERROR("Failed to seek in file", mFileName, + CommonException, OSFileError); + } +#endif // WIN32 + + // Not end of file any more! + mIsEOF = false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Close() +// Purpose: Closes the underlying file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void FileStream::Close() +{ + if(mOSFileHandle == INVALID_FILE) + { + THROW_EXCEPTION(CommonException, FileAlreadyClosed) + } + +#ifdef WIN32 + if(::CloseHandle(mOSFileHandle) == 0) + { + THROW_WIN_FILE_ERROR("Failed to close file", mFileName, + CommonException, OSFileCloseError); + } +#else // ! WIN32 + if(::close(mOSFileHandle) != 0) + { + THROW_SYS_FILE_ERROR("Failed to close file", mFileName, + CommonException, OSFileCloseError); + } +#endif // WIN32 + + mOSFileHandle = INVALID_FILE; + mIsEOF = true; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::StreamDataLeft() +// Purpose: Any data left to write? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool FileStream::StreamDataLeft() +{ + return !mIsEOF; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::StreamClosed() +// Purpose: Is the stream closed? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool FileStream::StreamClosed() +{ + return (mOSFileHandle == INVALID_FILE); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::CompareWith(IOStream&, int) +// Purpose: Compare bytes in this file with other stream's data +// Created: 2009/01/03 +// +// -------------------------------------------------------------------------- +bool FileStream::CompareWith(IOStream& rOther, int Timeout) +{ + // Size + IOStream::pos_type mySize = BytesLeftToRead(); + IOStream::pos_type otherSize = 0; + + // Test the contents + char buf1[2048]; + char buf2[2048]; + while(StreamDataLeft() && rOther.StreamDataLeft()) + { + int readSize = rOther.Read(buf1, sizeof(buf1), Timeout); + otherSize += readSize; + + if(Read(buf2, readSize) != readSize || + ::memcmp(buf1, buf2, readSize) != 0) + { + return false; + } + } + + // Check read all the data from the server and file -- can't be + // equal if local and remote aren't the same length. Can't use + // StreamDataLeft() test on local file, because if it's the same + // size, it won't know it's EOF yet. + + if(rOther.StreamDataLeft() || otherSize != mySize) + { + return false; + } + + return true; +} diff --git a/lib/common/FileStream.h b/lib/common/FileStream.h new file mode 100644 index 00000000..26442212 --- /dev/null +++ b/lib/common/FileStream.h @@ -0,0 +1,73 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileStream.h +// Purpose: FileStream interface to files +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef FILESTREAM__H +#define FILESTREAM__H + +#include "IOStream.h" + +#include +#include +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +class FileStream : public IOStream +{ +public: + 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)); + + // Ensure that const char * name doesn't end up as a handle + // on Windows! + + FileStream(const char *pFilename, + int flags = (O_RDONLY | O_BINARY), + int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); + + FileStream(tOSFileHandle FileDescriptor); + + virtual ~FileStream(); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); + using IOStream::Write; + virtual pos_type GetPosition() const; + virtual void Seek(IOStream::pos_type Offset, int SeekType); + virtual void Close(); + + virtual bool StreamDataLeft(); + 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; + bool mIsEOF; + FileStream(const FileStream &rToCopy) { /* do not call */ } + void AfterOpen(); + + // for debugging.. + std::string mFileName; +}; + + +#endif // FILESTREAM__H + + diff --git a/lib/common/GetLine.cpp b/lib/common/GetLine.cpp new file mode 100644 index 00000000..e6b26c8a --- /dev/null +++ b/lib/common/GetLine.cpp @@ -0,0 +1,176 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: GetLine.cpp +// Purpose: Common base class for line based file descriptor reading +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include "GetLine.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// utility whitespace function +inline bool iw(int c) +{ + return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: GetLine::GetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- +GetLine::GetLine() +: mLineNumber(0), + mBufferBegin(0), + mBytesInBuffer(0), + mPendingEOF(false), + mEOF(false) +{ } + +// -------------------------------------------------------------------------- +// +// Function +// Name: GetLine::GetLineInternal(std::string &, bool, int) +// Purpose: Gets a line from the file, returning it in rOutput. +// If Preprocess is true, leading and trailing +// whitespace is removed, and comments (after #) are +// deleted. Returns true if a line is available now, +// false if retrying may get a line (eg timeout, +// signal), and exceptions if it's EOF. +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- +bool GetLine::GetLineInternal(std::string &rOutput, bool Preprocess, + int Timeout) +{ + // EOF? + if(mEOF) {THROW_EXCEPTION(CommonException, GetLineEOF)} + + // Initialise string to stored into + rOutput = mPendingString; + mPendingString.erase(); + + bool foundLineEnd = false; + + while(!foundLineEnd && !mEOF) + { + // Use any bytes left in the buffer + while(mBufferBegin < mBytesInBuffer) + { + int c = mBuffer[mBufferBegin++]; + if(c == '\r') + { + // Ignore nasty Windows line ending extra chars + } + else if(c == '\n') + { + // Line end! + foundLineEnd = true; + break; + } + else + { + // Add to string + rOutput += c; + } + + // Implicit line ending at EOF + if(mBufferBegin >= mBytesInBuffer && mPendingEOF) + { + foundLineEnd = true; + } + } + + // Check size + if(rOutput.size() > GETLINE_MAX_LINE_SIZE) + { + THROW_EXCEPTION(CommonException, GetLineTooLarge) + } + + // Read more in? + if(!foundLineEnd && mBufferBegin >= mBytesInBuffer && !mPendingEOF) + { + int bytes = ReadMore(Timeout); + + // Error? + if(bytes == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + // Adjust buffer info + mBytesInBuffer = bytes; + mBufferBegin = 0; + + // No data returned? + if(bytes == 0 && IsStreamDataLeft()) + { + // store string away + mPendingString = rOutput; + // Return false; + return false; + } + } + + // EOF? + if(mPendingEOF && mBufferBegin >= mBytesInBuffer) + { + // File is EOF, and now we've depleted the buffer completely, so tell caller as well. + mEOF = true; + } + } + + if(Preprocess) + { + // Check for comment char, but char before must be whitespace + // end points to a gap between characters, may equal start if + // the string to be extracted has zero length, and indexes the + // first character not in the string (== length, or a # mark + // or whitespace) + int end = 0; + int size = rOutput.size(); + while(end < size) + { + if(rOutput[end] == '#' && (end == 0 || (iw(rOutput[end-1])))) + { + break; + } + end++; + } + + // Remove whitespace + int begin = 0; + while(begin < size && iw(rOutput[begin])) + { + begin++; + } + + while(end > begin && end <= size && iw(rOutput[end-1])) + { + end--; + } + + // Return a sub string + rOutput = rOutput.substr(begin, end - begin); + } + + return true; +} + + diff --git a/lib/common/GetLine.h b/lib/common/GetLine.h new file mode 100644 index 00000000..0eeb3c71 --- /dev/null +++ b/lib/common/GetLine.h @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: GetLine.h +// Purpose: Common base class for line based file descriptor reading +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- + +#ifndef GETLINE__H +#define GETLINE__H + +#include + +#ifdef BOX_RELEASE_BUILD + #define GETLINE_BUFFER_SIZE 1024 +#elif defined WIN32 + // need enough space for at least one unicode character + // in UTF-8 when calling console_read() from bbackupquery + #define GETLINE_BUFFER_SIZE 5 +#else + #define GETLINE_BUFFER_SIZE 4 +#endif + +// Just a very large upper bound for line size to avoid +// people sending lots of data over sockets and causing memory problems. +#define GETLINE_MAX_LINE_SIZE (1024*256) + +// -------------------------------------------------------------------------- +// +// Class +// Name: GetLine +// Purpose: Common base class for line based file descriptor reading +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- +class GetLine +{ +protected: + GetLine(); + +private: + GetLine(const GetLine &rToCopy); + +public: + virtual bool IsEOF() {return mEOF;} + int GetLineNumber() {return mLineNumber;} + virtual ~GetLine() { } + +protected: + bool GetLineInternal(std::string &rOutput, + bool Preprocess = false, + int Timeout = IOStream::TimeOutInfinite); + virtual int ReadMore(int Timeout = IOStream::TimeOutInfinite) = 0; + virtual bool IsStreamDataLeft() = 0; + + char mBuffer[GETLINE_BUFFER_SIZE]; + int mLineNumber; + int mBufferBegin; + int mBytesInBuffer; + bool mPendingEOF; + std::string mPendingString; + bool mEOF; +}; + +#endif // GETLINE__H + diff --git a/lib/common/Guards.h b/lib/common/Guards.h new file mode 100644 index 00000000..3637b261 --- /dev/null +++ b/lib/common/Guards.h @@ -0,0 +1,147 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Guards.h +// Purpose: Classes which ensure things are closed/deleted properly when +// going out of scope. Easy exception proof code, etc +// Created: 2003/07/12 +// +// -------------------------------------------------------------------------- + +#ifndef GUARDS__H +#define GUARDS__H + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "CommonException.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +template +class FileHandleGuard +{ +public: + FileHandleGuard(const std::string& rFilename) + : mOSFileHandle(::open(rFilename.c_str(), flags, mode)) + { + if(mOSFileHandle < 0) + { + THROW_SYS_FILE_ERROR("Failed to open file", rFilename, + CommonException, OSFileOpenError); + } + } + + ~FileHandleGuard() + { + if(mOSFileHandle >= 0) + { + Close(); + } + } + + void Close() + { + if(mOSFileHandle < 0) + { + THROW_EXCEPTION(CommonException, FileAlreadyClosed) + } + if(::close(mOSFileHandle) != 0) + { + THROW_EXCEPTION(CommonException, OSFileCloseError) + } + mOSFileHandle = -1; + } + + operator int() const + { + return mOSFileHandle; + } + +private: + int mOSFileHandle; +}; + +template +class MemoryBlockGuard +{ +public: + MemoryBlockGuard(int BlockSize) + : mpBlock(::malloc(BlockSize)), + mBlockSize(BlockSize) + { + if(mpBlock == 0) + { + throw std::bad_alloc(); + } + } + + MemoryBlockGuard(void *pBlock) + : mpBlock(pBlock) + { + if(mpBlock == 0) + { + throw std::bad_alloc(); + } + } + + ~MemoryBlockGuard() + { + free(mpBlock); + } + + operator type() const + { + return (type)mpBlock; + } + + type GetPtr() const + { + return (type)mpBlock; + } + + int GetSize() const + { + return mBlockSize; + } + + void Resize(int NewSize) + { + void *ptrn = ::realloc(mpBlock, NewSize); + if(ptrn == 0) + { + throw std::bad_alloc(); + } + 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" + +#endif // GUARDS__H + diff --git a/lib/common/IOStream.cpp b/lib/common/IOStream.cpp new file mode 100644 index 00000000..3e126d3f --- /dev/null +++ b/lib/common/IOStream.cpp @@ -0,0 +1,274 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStream.cpp +// Purpose: I/O Stream abstraction +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "IOStream.h" +#include "CommonException.h" +#include "Guards.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::IOStream() +// Purpose: Constructor +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +IOStream::IOStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::~IOStream() +// Purpose: Destructor +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +IOStream::~IOStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::Close() +// Purpose: Close the stream +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void IOStream::Close() +{ + // Do nothing by default -- let the destructor clear everything up. +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::Seek(int, int) +// Purpose: Seek in stream (if supported) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void IOStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + THROW_EXCEPTION(CommonException, NotSupported) +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::GetPosition() +// Purpose: Returns current position in stream (if supported) +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type IOStream::GetPosition() const +{ + THROW_EXCEPTION(CommonException, NotSupported) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::ConvertSeekTypeToOSWhence(int) +// Purpose: Return an whence arg for lseek given a IOStream seek type +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +int IOStream::ConvertSeekTypeToOSWhence(int SeekType) +{ + // Should be nicely optimised out as values are choosen in header file to match OS values. + int ostype = SEEK_SET; + switch(SeekType) + { +#ifdef WIN32 + case SeekType_Absolute: + ostype = FILE_BEGIN; + break; + case SeekType_Relative: + ostype = FILE_CURRENT; + break; + case SeekType_End: + ostype = FILE_END; + break; +#else // ! WIN32 + case SeekType_Absolute: + ostype = SEEK_SET; + break; + case SeekType_Relative: + ostype = SEEK_CUR; + break; + case SeekType_End: + ostype = SEEK_END; + break; +#endif // WIN32 + + default: + THROW_EXCEPTION(CommonException, IOStreamBadSeekType) + } + + return ostype; +} + + +// -------------------------------------------------------------------------- +// +// 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. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool IOStream::ReadFullBuffer(void *pBuffer, int NBytes, int *pNBytesRead, int Timeout) +{ + int bytesToGo = NBytes; + char *buffer = (char*)pBuffer; + if(pNBytesRead) (*pNBytesRead) = 0; + + while(bytesToGo > 0) + { + int bytesRead = Read(buffer, bytesToGo, Timeout); + if(bytesRead == 0) + { + // Timeout or something + return false; + } + // Increment things + bytesToGo -= bytesRead; + buffer += bytesRead; + if(pNBytesRead) (*pNBytesRead) += bytesRead; + } + + // Got everything + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::WriteAllBuffered() +// Purpose: Ensures that any data which has been buffered is written to the stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void IOStream::WriteAllBuffered(int Timeout) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::BytesLeftToRead() +// Purpose: Numbers of bytes left to read in the stream, or +// IOStream::SizeOfStreamUnknown if this isn't known. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type IOStream::BytesLeftToRead() +{ + return IOStream::SizeOfStreamUnknown; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::CopyStreamTo(IOStream &, int Timeout) +// Purpose: Copies the entire stream to another stream (reading from this, +// writing to rCopyTo). Returns whether the copy completed (ie +// StreamDataLeft() returns false) +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool IOStream::CopyStreamTo(IOStream &rCopyTo, int Timeout, int BufferSize) +{ + // Make sure there's something to do before allocating that buffer + if(!StreamDataLeft()) + { + return true; // complete, even though nothing happened + } + + // Buffer + MemoryBlockGuard buffer(BufferSize); + + // Get copying! + while(StreamDataLeft()) + { + // Read some data + int bytes = Read(buffer, BufferSize, Timeout); + if(bytes == 0 && StreamDataLeft()) + { + return false; // incomplete, timed out + } + + // Write some data + if(bytes != 0) + { + rCopyTo.Write(buffer, bytes); + } + } + + return true; // completed +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::Flush(int Timeout) +// Purpose: Read and discard all remaining data in stream. +// Useful for protocol streams which must be flushed +// to avoid breaking the protocol. +// Created: 2008/08/20 +// +// -------------------------------------------------------------------------- +void IOStream::Flush(int Timeout) +{ + char buffer[4096]; + + while(StreamDataLeft()) + { + Read(buffer, sizeof(buffer), Timeout); + } +} + +// -------------------------------------------------------------------------- +// +// 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 +{ + return "unknown IOStream"; +} diff --git a/lib/common/IOStream.h b/lib/common/IOStream.h new file mode 100644 index 00000000..df7216c3 --- /dev/null +++ b/lib/common/IOStream.h @@ -0,0 +1,73 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStream.h +// Purpose: I/O Stream abstraction +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef IOSTREAM__H +#define IOSTREAM__H + +// -------------------------------------------------------------------------- +// +// Class +// Name: IOStream +// Purpose: Abstract interface to streams of data +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +class IOStream +{ +public: + IOStream(); + virtual ~IOStream(); + +private: + IOStream(const IOStream &rToCopy); /* forbidden */ + IOStream& operator=(const IOStream &rToCopy); /* forbidden */ + +public: + enum + { + TimeOutInfinite = -1, + SizeOfStreamUnknown = -1 + }; + + enum + { + SeekType_Absolute = 0, + SeekType_Relative = 1, + SeekType_End = 2 + }; + + // Timeout in milliseconds + // Read may return 0 -- does not mean end of stream. + 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, + 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.cpp b/lib/common/IOStreamGetLine.cpp new file mode 100644 index 00000000..ef8930b8 --- /dev/null +++ b/lib/common/IOStreamGetLine.cpp @@ -0,0 +1,127 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStreamGetLine.cpp +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "IOStreamGetLine.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::IOStreamGetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +IOStreamGetLine::IOStreamGetLine(IOStream &Stream) +: mrStream(Stream) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::~IOStreamGetLine() +// Purpose: Destructor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +IOStreamGetLine::~IOStreamGetLine() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::GetLine(std::string &, bool, int) +// Purpose: Gets a line from the file, returning it in rOutput. If Preprocess is true, leading +// and trailing whitespace is removed, and comments (after #) +// are deleted. +// Returns true if a line is available now, false if retrying may get a line (eg timeout, signal), +// and exceptions if it's EOF. +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +bool IOStreamGetLine::GetLine(std::string &rOutput, bool Preprocess, int Timeout) +{ + return GetLineInternal(rOutput, Preprocess, Timeout); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::ReadMore() +// Purpose: Read more bytes from the handle, possible the +// console, into mBuffer and return the number of +// bytes read, 0 on EOF or -1 on error. +// Created: 2011/04/22 +// +// -------------------------------------------------------------------------- +int IOStreamGetLine::ReadMore(int Timeout) +{ + int bytes = mrStream.Read(mBuffer, sizeof(mBuffer), Timeout); + + if(!mrStream.StreamDataLeft()) + { + mPendingEOF = true; + } + + return bytes; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::DetachFile() +// Purpose: Detaches the file handle, setting the file pointer correctly. +// Probably not good for sockets... +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +void IOStreamGetLine::DetachFile() +{ + // Adjust file pointer + int bytesOver = mBytesInBuffer - mBufferBegin; + ASSERT(bytesOver >= 0); + if(bytesOver > 0) + { + mrStream.Seek(0 - bytesOver, IOStream::SeekType_Relative); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::IgnoreBufferedData(int) +// Purpose: Ignore buffered bytes (effectively removing them from the +// beginning of the buffered data.) +// Cannot remove more bytes than are currently in the buffer. +// Be careful when this is used! +// Created: 22/12/04 +// +// -------------------------------------------------------------------------- +void IOStreamGetLine::IgnoreBufferedData(int BytesToIgnore) +{ + int bytesInBuffer = mBytesInBuffer - mBufferBegin; + if(BytesToIgnore < 0 || BytesToIgnore > bytesInBuffer) + { + THROW_EXCEPTION(CommonException, IOStreamGetLineNotEnoughDataToIgnore) + } + mBufferBegin += BytesToIgnore; +} + + + diff --git a/lib/common/IOStreamGetLine.h b/lib/common/IOStreamGetLine.h new file mode 100644 index 00000000..1b537031 --- /dev/null +++ b/lib/common/IOStreamGetLine.h @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStreamGetLine.h +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#ifndef IOSTREAMGETLINE__H +#define IOSTREAMGETLINE__H + +#include + +#include "GetLine.h" +#include "IOStream.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: IOStreamGetLine +// Purpose: Line based stream reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +class IOStreamGetLine : public GetLine +{ +public: + IOStreamGetLine(IOStream &Stream); + virtual ~IOStreamGetLine(); +private: + IOStreamGetLine(const IOStreamGetLine &rToCopy); + +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;} + int GetSizeOfBufferedData() const {return mBytesInBuffer - mBufferBegin;} + void IgnoreBufferedData(int BytesToIgnore); + IOStream &GetUnderlyingStream() {return mrStream;} + +protected: + int ReadMore(int Timeout = IOStream::TimeOutInfinite); + +private: + IOStream &mrStream; +}; + +#endif // IOSTREAMGETLINE__H + diff --git a/lib/common/InvisibleTempFileStream.cpp b/lib/common/InvisibleTempFileStream.cpp new file mode 100644 index 00000000..d6d04489 --- /dev/null +++ b/lib/common/InvisibleTempFileStream.cpp @@ -0,0 +1,40 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: InvisibleTempFileStream.cpp +// Purpose: IOStream interface to temporary files that +// delete themselves +// Created: 2006/10/13 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "InvisibleTempFileStream.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: InvisibleTempFileStream::InvisibleTempFileStream +// (const char *, int, int) +// Purpose: Constructor, opens invisible file +// Created: 2006/10/13 +// +// -------------------------------------------------------------------------- +InvisibleTempFileStream::InvisibleTempFileStream(const std::string& Filename, + int flags, int mode) +#ifdef WIN32 + : FileStream(Filename, flags | O_TEMPORARY, mode) +#else + : FileStream(Filename, flags, mode) +#endif +{ + #ifndef WIN32 + if(EMU_UNLINK(Filename.c_str()) != 0) + { + MEMLEAKFINDER_NOT_A_LEAK(this); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } + #endif +} diff --git a/lib/common/InvisibleTempFileStream.h b/lib/common/InvisibleTempFileStream.h new file mode 100644 index 00000000..bb6c3954 --- /dev/null +++ b/lib/common/InvisibleTempFileStream.h @@ -0,0 +1,35 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: InvisibleTempFileStream.h +// Purpose: FileStream interface to temporary files that +// delete themselves +// Created: 2006/10/13 +// +// -------------------------------------------------------------------------- + +#ifndef INVISIBLETEMPFILESTREAM__H +#define INVISIBLETEMPFILESTREAM__H + +#include "FileStream.h" + +class InvisibleTempFileStream : public FileStream +{ +public: + InvisibleTempFileStream(const std::string& Filename, +#ifdef WIN32 + int flags = (O_RDONLY | O_BINARY), +#else + int flags = O_RDONLY, +#endif + int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); + +private: + InvisibleTempFileStream(const InvisibleTempFileStream &rToCopy) + : FileStream(INVALID_FILE) + { /* do not call */ } +}; + +#endif // INVISIBLETEMPFILESTREAM__H + + diff --git a/lib/common/Logging.cpp b/lib/common/Logging.cpp new file mode 100644 index 00000000..0928a4d4 --- /dev/null +++ b/lib/common/Logging.cpp @@ -0,0 +1,766 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Logging.cpp +// Purpose: Generic logging core routines implementation +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include // for stderror + +#ifdef HAVE_PROCESS_H +# include +#endif + +#ifdef HAVE_SYSLOG_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include + +#include "BoxTime.h" +#include "Logging.h" + +bool Logging::sLogToSyslog = false; +bool Logging::sLogToConsole = false; +bool Logging::sContextSet = false; + +bool HideExceptionMessageGuard::sHiddenState = false; + +std::vector Logging::sLoggers; +std::string Logging::sContext; +Console* Logging::spConsole = NULL; +Syslog* Logging::spSyslog = NULL; +Logging Logging::sGlobalLogging; // automatic initialisation +std::string Logging::sProgramName; +const Log::Category Logging::UNCATEGORISED("Uncategorised"); +std::auto_ptr Logging::sapHideFileGuard; + +HideSpecificExceptionGuard::SuppressedExceptions_t + HideSpecificExceptionGuard::sSuppressedExceptions; + +Logging::Logging() +{ + ASSERT(!spConsole); + ASSERT(!spSyslog); + spConsole = new Console(); + spSyslog = new Syslog(); + sLogToConsole = true; + sLogToSyslog = true; +} + +Logging::~Logging() +{ + sLogToConsole = false; + sLogToSyslog = false; + delete spConsole; + delete spSyslog; + spConsole = NULL; + spSyslog = NULL; +} + +void Logging::ToSyslog(bool enabled) +{ + if (!sLogToSyslog && enabled) + { + Add(spSyslog); + } + + if (sLogToSyslog && !enabled) + { + Remove(spSyslog); + } + + sLogToSyslog = enabled; +} + +void Logging::ToConsole(bool enabled) +{ + if (!sLogToConsole && enabled) + { + Add(spConsole); + } + + if (sLogToConsole && !enabled) + { + Remove(spConsole); + } + + sLogToConsole = enabled; +} + +void Logging::FilterConsole(Log::Level level) +{ + spConsole->Filter(level); +} + +void Logging::FilterSyslog(Log::Level level) +{ + spSyslog->Filter(level); +} + +void Logging::Add(Logger* pNewLogger) +{ + for (std::vector::iterator i = sLoggers.begin(); + i != sLoggers.end(); i++) + { + if (*i == pNewLogger) + { + return; + } + } + + sLoggers.insert(sLoggers.begin(), pNewLogger); +} + +void Logging::Remove(Logger* pOldLogger) +{ + for (std::vector::iterator i = sLoggers.begin(); + i != sLoggers.end(); i++) + { + if (*i == pOldLogger) + { + sLoggers.erase(i); + return; + } + } +} + +void Logging::Log(Log::Level level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) +{ + std::string newMessage; + + if (sContextSet) + { + newMessage += "[" + sContext + "] "; + } + + newMessage += message; + + for (std::vector::iterator i = sLoggers.begin(); + i != sLoggers.end(); i++) + { + bool result = (*i)->Log(level, file, line, function, category, + newMessage); + if (!result) + { + return; + } + } +} + +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; + } + + std::string newMessage; + + if (sContextSet) + { + newMessage += "[" + sContext + "] "; + } + + newMessage += message; + + spSyslog->Log(level, rFile, line, function, category, newMessage); +} + +void Logging::SetContext(std::string context) +{ + sContext = context; + sContextSet = true; +} + +Log::Level Logging::GetNamedLevel(const std::string& rName) +{ + if (rName == "nothing") { return Log::NOTHING; } + else if (rName == "fatal") { return Log::FATAL; } + else if (rName == "error") { return Log::ERROR; } + else if (rName == "warning") { return Log::WARNING; } + else if (rName == "notice") { return Log::NOTICE; } + else if (rName == "info") { return Log::INFO; } + else if (rName == "trace") { return Log::TRACE; } + else if (rName == "everything") { return Log::EVERYTHING; } + else + { + BOX_ERROR("Unknown verbosity level: " << rName); + return Log::INVALID; + } +} + +void Logging::ClearContext() +{ + sContextSet = false; +} + +void Logging::SetProgramName(const std::string& rProgramName) +{ + sProgramName = rProgramName; + + for (std::vector::iterator i = sLoggers.begin(); + i != sLoggers.end(); i++) + { + (*i)->SetProgramName(rProgramName); + } +} + +void Logging::SetFacility(int facility) +{ + spSyslog->SetFacility(facility); +} + +Logger::Logger() +: mCurrentLevel(Log::EVERYTHING) +{ + Logging::Add(this); +} + +Logger::Logger(Log::Level Level) +: mCurrentLevel(Level) +{ + Logging::Add(this); +} + +Logger::~Logger() +{ + Logging::Remove(this); +} + +bool Logger::IsEnabled(Log::Level level) +{ + return (int)mCurrentLevel >= (int)level; +} + +bool Console::sShowTime = false; +bool Console::sShowTimeMicros = false; +bool Console::sShowTag = false; +bool Console::sShowPID = false; +std::string Console::sTag; + +void Console::SetProgramName(const std::string& rProgramName) +{ + sTag = rProgramName; +} + +void Console::SetShowTag(bool enabled) +{ + sShowTag = enabled; +} + +void Console::SetShowTime(bool enabled) +{ + sShowTime = enabled; +} + +void Console::SetShowTimeMicros(bool enabled) +{ + sShowTimeMicros = enabled; +} + +void Console::SetShowPID(bool enabled) +{ + sShowPID = enabled; +} + +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()) + { + return true; + } + + FILE* target = stdout; + std::ostringstream buf; + + if (sShowTime) + { + buf << FormatTime(GetCurrentBoxTime(), false, sShowTimeMicros); + buf << " "; + } + + if (sShowTag) + { + if (sShowPID) + { + buf << "[" << sTag << " " << getpid() << "] "; + } + else + { + buf << "[" << sTag << "] "; + } + } + else if (sShowPID) + { + buf << "[" << getpid() << "] "; + } + + if (level <= Log::FATAL) + { + buf << "FATAL: "; + } + else if (level <= Log::ERROR) + { + buf << "ERROR: "; + } + else if (level <= Log::WARNING) + { + buf << "WARNING: "; + } + else if (level <= Log::NOTICE) + { + buf << "NOTICE: "; + } + else if (level <= Log::INFO) + { + buf << "INFO: "; + } + else if (level <= Log::TRACE) + { + buf << "TRACE: "; + } + + buf << message; + + #ifdef WIN32 + std::string output = buf.str(); + if(ConvertUtf8ToConsole(output.c_str(), output) == false) + { + fprintf(target, "%s (and failed to convert to console encoding)\n", + output.c_str()); + } + else + { + fprintf(target, "%s\n", output.c_str()); + } + #else + fprintf(target, "%s\n", buf.str().c_str()); + #endif + + fflush(target); + + return true; +} + +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()) + { + return true; + } + + int syslogLevel = LOG_ERR; + + switch(level) + { + case Log::NOTHING: /* fall through */ + case Log::INVALID: /* fall through */ + case Log::FATAL: syslogLevel = LOG_CRIT; break; + case Log::ERROR: syslogLevel = LOG_ERR; break; + case Log::WARNING: syslogLevel = LOG_WARNING; break; + case Log::NOTICE: syslogLevel = LOG_NOTICE; break; + case Log::INFO: syslogLevel = LOG_INFO; break; + case Log::TRACE: /* fall through */ + case Log::EVERYTHING: syslogLevel = LOG_DEBUG; break; + } + + std::string msg; + + if (level <= Log::FATAL) + { + msg = "FATAL: "; + } + else if (level <= Log::ERROR) + { + msg = "ERROR: "; + } + else if (level <= Log::WARNING) + { + msg = "WARNING: "; + } + else if (level <= Log::NOTICE) + { + msg = "NOTICE: "; + } + + msg += message; + + syslog(syslogLevel, "%s", msg.c_str()); + + return true; +} + +Syslog::Syslog() : mFacility(LOG_LOCAL6) +{ + ::openlog("Box Backup", LOG_PID, mFacility); +} + +Syslog::~Syslog() +{ + Shutdown(); +} + +void Syslog::Shutdown() +{ + ::closelog(); +} + +void Syslog::SetProgramName(const std::string& rProgramName) +{ + mName = rProgramName; + ::closelog(); + ::openlog(mName.c_str(), LOG_PID, mFacility); +} + +void Syslog::SetFacility(int facility) +{ + mFacility = facility; + ::closelog(); + ::openlog(mName.c_str(), LOG_PID, mFacility); +} + +int Syslog::GetNamedFacility(const std::string& rFacility) +{ + #define CASE_RETURN(x) if (rFacility == #x) { return LOG_ ## x; } + CASE_RETURN(LOCAL0) + CASE_RETURN(LOCAL1) + CASE_RETURN(LOCAL2) + CASE_RETURN(LOCAL3) + CASE_RETURN(LOCAL4) + CASE_RETURN(LOCAL5) + CASE_RETURN(LOCAL6) + CASE_RETURN(DAEMON) + #undef CASE_RETURN + + BOX_ERROR("Unknown log facility '" << rFacility << "', " + "using default LOCAL6"); + return LOG_LOCAL6; +} + +bool FileLogger::Log(Log::Level Level, const std::string& file, int line, + const std::string& function, const Log::Category& category, + const std::string& message) +{ + if (mLogFile.StreamClosed()) + { + /* skip this logger to allow logging failure to open + the log file, without causing an infinite loop */ + return true; + } + + if (Level > GetLevel()) + { + return true; + } + + /* avoid infinite loop if this throws an exception */ + Log::Level oldLevel = GetLevel(); + Filter(Log::NOTHING); + + std::ostringstream buf; + buf << FormatTime(GetCurrentBoxTime(), true, false); + buf << " "; + + if (Level <= Log::FATAL) + { + buf << "[FATAL] "; + } + else if (Level <= Log::ERROR) + { + buf << "[ERROR] "; + } + else if (Level <= Log::WARNING) + { + buf << "[WARNING] "; + } + else if (Level <= Log::NOTICE) + { + buf << "[NOTICE] "; + } + else if (Level <= Log::INFO) + { + buf << "[INFO] "; + } + else if (Level <= Log::TRACE) + { + buf << "[TRACE] "; + } + + buf << message << "\n"; + std::string output = buf.str(); + + #ifdef WIN32 + ConvertUtf8ToConsole(output.c_str(), output); + #endif + + mLogFile.Write(output.c_str(), output.length()); + + // no infinite loop, reset to saved logging level + Filter(oldLevel); + return true; +} + +std::string PrintEscapedBinaryData(const std::string& rInput) +{ + std::ostringstream output; + + for (size_t i = 0; i < rInput.length(); i++) + { + if (isprint(rInput[i])) + { + output << rInput[i]; + } + else + { + output << "\\x" << std::hex << std::setw(2) << + std::setfill('0') << (int) rInput[i] << + std::dec; + } + } + + return output.str(); +} + +bool HideSpecificExceptionGuard::IsHidden(int type, int subtype) +{ + for (SuppressedExceptions_t::iterator + i = sSuppressedExceptions.begin(); + i != sSuppressedExceptions.end(); i++) + { + if(i->first == type && i->second == subtype) + { + return true; + } + } + 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 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 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 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::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::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 new file mode 100644 index 00000000..3dc3e69c --- /dev/null +++ b/lib/common/Logging.h @@ -0,0 +1,698 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Logging.h +// Purpose: Generic logging core routines declarations and macros +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +#ifndef LOGGING__H +#define LOGGING__H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "FileStream.h" + +#define BOX_LOG(level, stuff) \ +{ \ + std::ostringstream _box_log_line; \ + _box_log_line << stuff; \ + 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__, __FUNCTION__, \ + Logging::UNCATEGORISED, _box_log_line.str()); \ +} + +#define BOX_FATAL(stuff) BOX_LOG(Log::FATAL, stuff) +#define BOX_ERROR(stuff) BOX_LOG(Log::ERROR, stuff) +#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) BOX_LOG(Log::TRACE, stuff) + +#define BOX_SYS_ERRNO_MESSAGE(error_number, stuff) \ + stuff << ": " << std::strerror(error_number) << \ + " (" << error_number << ")" + +#define BOX_FILE_MESSAGE(filename, message) \ + message << ": " << filename + +#define BOX_SYS_FILE_ERRNO_MESSAGE(filename, error_number, message) \ + BOX_SYS_ERRNO_MESSAGE(error_number, BOX_FILE_MESSAGE(filename, message)) + +#define BOX_SYS_ERROR_MESSAGE(stuff) \ + BOX_SYS_ERRNO_MESSAGE(errno, stuff) + +#define BOX_LOG_SYS_WARNING(stuff) \ + BOX_WARNING(BOX_SYS_ERROR_MESSAGE(stuff)) +#define BOX_LOG_SYS_ERROR(stuff) \ + BOX_ERROR(BOX_SYS_ERROR_MESSAGE(stuff)) +#define BOX_LOG_SYS_ERRNO(error_number, stuff) \ + BOX_ERROR(BOX_SYS_ERRNO_MESSAGE(error_number, stuff)) +#define BOX_LOG_SYS_FATAL(stuff) \ + BOX_FATAL(BOX_SYS_ERROR_MESSAGE(stuff)) + +#define THROW_SYS_ERROR_NUMBER(message, error_number, exception, subtype) \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + BOX_SYS_ERRNO_MESSAGE(error_number, message)) + +#define THROW_SYS_ERROR(message, exception, subtype) \ + THROW_SYS_ERROR_NUMBER(message, errno, exception, subtype) + +#define THROW_SYS_FILE_ERROR(message, filename, exception, subtype) \ + THROW_SYS_ERROR_NUMBER(BOX_FILE_MESSAGE(filename, message), \ + errno, exception, subtype) + +#define THROW_SYS_FILE_ERRNO(message, filename, error_number, exception, subtype) \ + THROW_SYS_ERROR_NUMBER(BOX_FILE_MESSAGE(filename, message), \ + error_number, exception, subtype) + +#define THROW_FILE_ERROR(message, filename, exception, subtype) \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + 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(BOX_WIN_ERRNO_MESSAGE(GetLastError(), stuff)) + #define BOX_LOG_WIN_WARNING(stuff) \ + BOX_WARNING(BOX_WIN_ERRNO_MESSAGE(GetLastError(), stuff)) + #define BOX_LOG_WIN_ERROR_NUMBER(stuff, number) \ + BOX_ERROR(BOX_WIN_ERRNO_MESSAGE(number, stuff)) + #define BOX_LOG_WIN_WARNING_NUMBER(stuff, 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 THROW_WIN_ERROR_NUMBER(message, error_number, exception, subtype) \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + BOX_WIN_ERRNO_MESSAGE(error_number, message)) + #define THROW_WIN_FILE_ERRNO(message, filename, error_number, exception, subtype) \ + THROW_WIN_ERROR_NUMBER(BOX_FILE_MESSAGE(filename, message), \ + error_number, exception, subtype) + #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_SOCKET_ERROR_MESSAGE(_type, _name, _port, stuff) \ + BOX_WIN_ERRNO_MESSAGE(WSAGetLastError(), stuff << " (type " << _type << \ + ", name " << _name << ", port " << _port << ")") +#else +# 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) \ + std::hex << \ + std::showbase << \ + std::internal << \ + std::setw(10) << \ + std::setfill('0') << \ + (number) << \ + std::dec + +#define BOX_FORMAT_ACCOUNT(accno) \ + BOX_FORMAT_HEX32(accno) + +#define BOX_FORMAT_OBJECTID(objectid) \ + std::hex << \ + std::showbase << \ + (objectid) << \ + std::dec + +#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(3) << \ + std::setfill('0') << \ + (int)((t % 1000000) / 1000) << " seconds" + +#undef ERROR + +namespace Log +{ + enum Level + { + NOTHING = 1, + FATAL, + ERROR, + WARNING, + NOTICE, + INFO, + TRACE, + 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; } + }; +} + +// -------------------------------------------------------------------------- +// +// Class +// Name: Logger +// Purpose: Abstract base class for log targets +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +class Logger +{ + private: + Log::Level mCurrentLevel; + + public: + Logger(); + Logger(Log::Level level); + virtual ~Logger(); + + 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) + { + mCurrentLevel = level; + } + + virtual const char* GetType() = 0; + Log::Level GetLevel() { return mCurrentLevel; } + bool IsEnabled(Log::Level level); + + virtual void SetProgramName(const std::string& rProgramName) = 0; + + class LevelGuard + { + private: + Logger& mLogger; + Log::Level mOldLevel; + + public: + LevelGuard(Logger& Logger, Log::Level newLevel = Log::INVALID) + : mLogger(Logger) + { + mOldLevel = Logger.GetLevel(); + if (newLevel != Log::INVALID) + { + Logger.Filter(newLevel); + } + } + ~LevelGuard() + { + mLogger.Filter(mOldLevel); + } + }; +}; + +// -------------------------------------------------------------------------- +// +// Class +// Name: Console +// Purpose: Console logging target +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +class Console : public Logger +{ + private: + static bool sShowTag; + static bool sShowTime; + static bool sShowTimeMicros; + static bool sShowPID; + static std::string sTag; + + public: + 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); + + static void SetShowTag(bool enabled); + 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; + } + }; +}; + +// -------------------------------------------------------------------------- +// +// Class +// Name: Syslog +// Purpose: Syslog (or Windows Event Viewer) logging target +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +class Syslog : public Logger +{ + private: + std::string mName; + int mFacility; + + public: + Syslog(); + virtual ~Syslog(); + + 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 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& GetMessages() const { return mMessages; } + std::string GetString() const + { + std::ostringstream oss; + for (std::vector::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. +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +class Logging +{ + private: + static std::vector sLoggers; + static bool sLogToSyslog, sLogToConsole; + static std::string sContext; + static bool sContextSet; + static Console* spConsole; + static Syslog* spSyslog; + static Logging sGlobalLogging; + static std::string sProgramName; + static std::auto_ptr sapHideFileGuard; + + public: + Logging (); + ~Logging(); + static void ToSyslog (bool enabled); + static void ToConsole (bool enabled); + static void FilterSyslog (Log::Level level); + 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& 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 Log::Level GetNamedLevel(const std::string& rName); + 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 ShowTagOnConsole + { + private: + bool mOldShowTag; + + public: + ShowTagOnConsole() + : mOldShowTag(Console::GetShowTag()) + { + Console::SetShowTag(true); + } + ~ShowTagOnConsole() + { + Console::SetShowTag(mOldShowTag); + } + }; + + class Tagger + { + private: + std::string mOldTag; + bool mReplace; + + public: + Tagger() + : mOldTag(Logging::GetProgramName()), + mReplace(false) + { + } + Tagger(const std::string& rTempTag, bool replace = false) + : mOldTag(Logging::GetProgramName()), + mReplace(replace) + { + Change(rTempTag); + } + ~Tagger() + { + Logging::SetProgramName(mOldTag); + } + + void Change(const std::string& 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 +{ + private: + FileStream mLogFile; + FileLogger(const FileLogger& forbidden) + : mLogFile("") { /* do not call */ } + + public: + FileLogger(const std::string& rFileName, Log::Level Level, bool append) + : Logger(Level), + mLogFile(rFileName, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC)) + { } + + 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) { } +}; + +class HideExceptionMessageGuard +{ + public: + HideExceptionMessageGuard() + { + mOldHiddenState = sHiddenState; + sHiddenState = true; + } + ~HideExceptionMessageGuard() + { + sHiddenState = mOldHiddenState; + } + static bool ExceptionsHidden() { return sHiddenState; } + + private: + static bool sHiddenState; + bool mOldHiddenState; +}; + +class HideSpecificExceptionGuard +{ + private: + std::pair mExceptionCode; + + public: + typedef std::vector > SuppressedExceptions_t; + static SuppressedExceptions_t sSuppressedExceptions; + + HideSpecificExceptionGuard(int type, int subtype) + : mExceptionCode(std::pair(type, subtype)) + { + sSuppressedExceptions.push_back(mExceptionCode); + } + ~HideSpecificExceptionGuard() + { + SuppressedExceptions_t::reverse_iterator i = + sSuppressedExceptions.rbegin(); + assert(*i == mExceptionCode); + sSuppressedExceptions.pop_back(); + } + static bool IsHidden(int type, int subtype); +}; + +class HideCategoryGuard : public Logger +{ + private: + std::list 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 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 new file mode 100644 index 00000000..f52607bf --- /dev/null +++ b/lib/common/MainHelper.h @@ -0,0 +1,56 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MainHelper.h +// Purpose: Helper stuff for main() programs +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- + +#ifndef MAINHELPER__H +#define MAINHELPER__H + +#include + +#ifdef NEED_BOX_VERSION_H +# include "BoxVersion.h" +#endif + +#include "BannerText.h" +#include "BoxException.h" +#include "Logging.h" + +#define MAINHELPER_START \ + /* need to init memleakfinder early because we already called MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT */ \ + MEMLEAKFINDER_INIT \ + if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ + { \ + printf("Version: " BOX_VERSION "\n"); \ + printf("Build: " BOX_BUILD_SIGNATURE "\n"); \ + return 0; \ + } \ + MEMLEAKFINDER_START \ + try { + +#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(...) { \ + BOX_FATAL("UNKNOWN"); \ + return 1; \ + } + +#ifdef BOX_MEMORY_LEAK_TESTING + #define MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT(file, marker) \ + memleakfinder_setup_exit_report(file, marker); +#else + #define MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT(file, marker) +#endif // BOX_MEMORY_LEAK_TESTING + + +#endif // MAINHELPER__H + diff --git a/lib/common/Makefile.extra b/lib/common/Makefile.extra new file mode 100644 index 00000000..cc3f3a7a --- /dev/null +++ b/lib/common/Makefile.extra @@ -0,0 +1,11 @@ + +MAKEEXCEPTION = ../../lib/common/makeexception.pl + +# AUTOGEN SEEDING +autogen_CommonException.h autogen_CommonException.cpp: $(MAKEEXCEPTION) CommonException.txt + $(_PERL) $(MAKEEXCEPTION) CommonException.txt + +# AUTOGEN SEEDING +autogen_ConversionException.h autogen_ConversionException.cpp: $(MAKEEXCEPTION) ConversionException.txt + $(_PERL) $(MAKEEXCEPTION) ConversionException.txt + diff --git a/lib/common/MemBlockStream.cpp b/lib/common/MemBlockStream.cpp new file mode 100644 index 00000000..f49ac96f --- /dev/null +++ b/lib/common/MemBlockStream.cpp @@ -0,0 +1,268 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemBlockStream.cpp +// Purpose: Stream out data from any memory block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "MemBlockStream.h" +#include "CommonException.h" +#include "StreamableMemBlock.h" +#include "CollectInBufferStream.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream() +// Purpose: Constructor with no contents +// Created: 2012/11/07 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream() +: mpBuffer(NULL), + mBytesInBuffer(0), + mReadPosition(0) +{ } + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream() +// Purpose: Constructor (doesn't copy block, careful with lifetimes) +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const void *pBuffer, int Size) + : mpBuffer((char*)pBuffer), + mBytesInBuffer(Size), + mReadPosition(0) +{ + ASSERT(pBuffer != 0); + ASSERT(Size >= 0); +} + +// -------------------------------------------------------------------------- +// +// 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 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const StreamableMemBlock &rBlock) + : mpBuffer((char*)rBlock.GetBuffer()), + mBytesInBuffer(rBlock.GetSize()), + mReadPosition(0) +{ + 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 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const CollectInBufferStream &rBuffer) + : mpBuffer((char*)rBuffer.GetBuffer()), + mBytesInBuffer(rBuffer.GetSize()), + mReadPosition(0) +{ + ASSERT(mpBuffer != 0); + ASSERT(mBytesInBuffer >= 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream(const MemBlockStream &) +// Purpose: Copy constructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const MemBlockStream &rToCopy) + : mpBuffer(rToCopy.mpBuffer), + mBytesInBuffer(rToCopy.mBytesInBuffer), + mReadPosition(0) +{ + ASSERT(mpBuffer != 0); + ASSERT(mBytesInBuffer >= 0); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::~MemBlockStream() +// Purpose: Destructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::~MemBlockStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::Read(void *, int, int) +// Purpose: As interface. But only works in read phase +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +int MemBlockStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + // Adjust to number of bytes left + if(NBytes > (mBytesInBuffer - mReadPosition)) + { + NBytes = (mBytesInBuffer - mReadPosition); + } + ASSERT(NBytes >= 0); + if(NBytes <= 0) return 0; // careful now + + // Copy in the requested number of bytes and adjust the read pointer + ::memcpy(pBuffer, mpBuffer + mReadPosition, NBytes); + mReadPosition += NBytes; + + return NBytes; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::BytesLeftToRead() +// Purpose: As interface. But only works in read phase +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +IOStream::pos_type MemBlockStream::BytesLeftToRead() +{ + return (mBytesInBuffer - mReadPosition); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::Write(void *, int) +// Purpose: As interface. But only works in write phase +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void MemBlockStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, MemBlockStreamNotSupported) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::GetPosition() +// Purpose: In write phase, returns the number of bytes written, in read +// phase, the number of bytes to go +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +IOStream::pos_type MemBlockStream::GetPosition() const +{ + return mReadPosition; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::Seek(pos_type, int) +// Purpose: As interface. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void MemBlockStream::Seek(pos_type Offset, int SeekType) +{ + int newPos = 0; + switch(SeekType) + { + case IOStream::SeekType_Absolute: + newPos = Offset; + break; + case IOStream::SeekType_Relative: + newPos = mReadPosition + Offset; + break; + case IOStream::SeekType_End: + newPos = mBytesInBuffer + Offset; + break; + default: + THROW_EXCEPTION(CommonException, IOStreamBadSeekType) + break; + } + + // Make sure it doesn't go over + if(newPos > mBytesInBuffer) + { + newPos = mBytesInBuffer; + } + // or under + if(newPos < 0) + { + newPos = 0; + } + + // Set the new read position + mReadPosition = newPos; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::StreamDataLeft() +// Purpose: As interface +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +bool MemBlockStream::StreamDataLeft() +{ + return mReadPosition < mBytesInBuffer; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::StreamClosed() +// Purpose: As interface +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +bool MemBlockStream::StreamClosed() +{ + return true; +} + diff --git a/lib/common/MemBlockStream.h b/lib/common/MemBlockStream.h new file mode 100644 index 00000000..1ba4b0a6 --- /dev/null +++ b/lib/common/MemBlockStream.h @@ -0,0 +1,60 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemBlockStream.h +// Purpose: Stream out data from any memory block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#ifndef MEMBLOCKSTREAM__H +#define MEMBLOCKSTREAM__H + +#include "CollectInBufferStream.h" +#include "IOStream.h" + +class StreamableMemBlock; + +// -------------------------------------------------------------------------- +// +// Class +// Name: MemBlockStream +// Purpose: Stream out data from any memory block -- be careful the lifetime +// of the block is greater than the lifetime of this stream. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +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); + ~MemBlockStream(); +public: + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + 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(); + virtual const void* GetBuffer() const { return mpBuffer; } + 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; +}; + +#endif // MEMBLOCKSTREAM__H + diff --git a/lib/common/MemLeakFindOff.h b/lib/common/MemLeakFindOff.h new file mode 100644 index 00000000..1cc98bac --- /dev/null +++ b/lib/common/MemLeakFindOff.h @@ -0,0 +1,27 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFindOff.h +// Purpose: Switch memory leak finding off +// Created: 13/1/04 +// +// -------------------------------------------------------------------------- + +// no header guard + +#ifdef BOX_MEMORY_LEAK_TESTING + +#undef new + +#ifndef MEMLEAKFINDER_FULL_MALLOC_MONITORING + #ifdef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED + #undef malloc + #undef realloc + #undef free + #undef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED + #endif +#endif + +#undef MEMLEAKFINDER_ENABLED + +#endif diff --git a/lib/common/MemLeakFindOn.h b/lib/common/MemLeakFindOn.h new file mode 100644 index 00000000..f1113184 --- /dev/null +++ b/lib/common/MemLeakFindOn.h @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFindOn.h +// Purpose: Switch memory leak finding on +// Created: 13/1/04 +// +// -------------------------------------------------------------------------- + +// no header guard + +#ifdef BOX_MEMORY_LEAK_TESTING + +#define new DEBUG_NEW + +#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 +#endif + +#define MEMLEAKFINDER_ENABLED + +#endif diff --git a/lib/common/MemLeakFinder.h b/lib/common/MemLeakFinder.h new file mode 100644 index 00000000..07b52e26 --- /dev/null +++ b/lib/common/MemLeakFinder.h @@ -0,0 +1,68 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFinder.h +// Purpose: Memory leak finder +// Created: 12/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef MEMLEAKFINDER__H +#define MEMLEAKFINDER__H + +#ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING + // include stdlib now, to avoid problems with having the macros defined already + #include +#endif + +// global enable flag +extern bool memleakfinder_global_enable; + +class MemLeakSuppressionGuard +{ + public: + MemLeakSuppressionGuard(); + ~MemLeakSuppressionGuard(); +}; + +extern "C" +{ + void *memleakfinder_malloc(size_t size, const char *file, int line); + void *memleakfinder_calloc(size_t blocks, size_t size, const char *file, int line); + void *memleakfinder_realloc(void *ptr, size_t size); + void memleakfinder_free(void *ptr); +} + +void memleakfinder_init(); + +int memleakfinder_numleaks(); + +void memleakfinder_report_usage_summary(); + +void memleakfinder_reportleaks(); + +void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext); + +void memleakfinder_setup_exit_report(const std::string& filename, const char *markertext); + +void memleakfinder_startsectionmonitor(); + +void memleakfinder_traceblocksinsection(); + +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. 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__) + #define realloc memleakfinder_realloc + #define free memleakfinder_free + #define MEMLEAKFINDER_MALLOC_MONITORING_DEFINED +#endif + +#endif // MEMLEAKFINDER__H + diff --git a/lib/common/NamedLock.cpp b/lib/common/NamedLock.cpp new file mode 100644 index 00000000..08d7c461 --- /dev/null +++ b/lib/common/NamedLock.cpp @@ -0,0 +1,299 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: NamedLock.cpp +// Purpose: A global named lock, implemented as a lock file in +// file system +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +#ifdef HAVE_FLOCK + #include +#endif + +#include "CommonException.h" +#include "NamedLock.h" +#include "Utils.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::NamedLock() +// Purpose: Constructor +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +NamedLock::NamedLock() +#ifdef WIN32 +: mFileDescriptor(INVALID_HANDLE_VALUE) +#else +: mFileDescriptor(-1) +#endif +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::~NamedLock() +// Purpose: Destructor (automatically unlocks if locked) +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +NamedLock::~NamedLock() +{ +#ifdef WIN32 + if(mFileDescriptor != INVALID_HANDLE_VALUE) +#else + if(mFileDescriptor != -1) +#endif + { + ReleaseLock(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::TryAndGetLock(const char *, int) +// Purpose: Tries to get a lock on the name in the file system. +// IMPORTANT NOTE: If a file exists with this name, it +// will be deleted. +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +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 + 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 + +#ifdef WIN32 + HANDLE fd = openfile(rFilename.c_str(), flags, mode); + if(fd == INVALID_HANDLE_VALUE) +#else + int fd = ::open(rFilename.c_str(), flags, mode); + if(fd == -1) +#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_SYS_FILE_ERROR("Failed to open lockfile with O_EXLOCK", + rFilename, CommonException, OSFileError); + } + } +#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_SYS_FILE_ERROR("Failed to open lockfile with O_EXCL", + rFilename, CommonException, OSFileError); + } + } + + 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; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::ReleaseLock() +// Purpose: Release the lock. Exceptions if the lock is not held +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +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(EMU_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_EMU_ERROR( + BOX_FILE_MESSAGE(mFileName, "Failed to close lockfile"), + CommonException, OSFileError); + } + + // 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(EMU_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 new file mode 100644 index 00000000..a7d0d778 --- /dev/null +++ b/lib/common/NamedLock.h @@ -0,0 +1,50 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: NamedLock.h +// Purpose: A global named lock, implemented as a lock file in file system +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- + +#ifndef NAMEDLOCK__H +#define NAMEDLOCK__H + +// -------------------------------------------------------------------------- +// +// Class +// Name: NamedLock +// Purpose: A global named lock, implemented as a lock file in file system +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +class NamedLock +{ +public: + NamedLock(); + ~NamedLock(); +private: + // No copying allowed + NamedLock(const NamedLock &); + +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 new file mode 100644 index 00000000..b5f99bb5 --- /dev/null +++ b/lib/common/PartialReadStream.cpp @@ -0,0 +1,138 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: PartialReadStream.h +// Purpose: Read part of another stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "PartialReadStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::PartialReadStream(IOStream &, +// pos_type) +// Purpose: Constructor, taking another stream and the number of +// bytes to be read from it. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +PartialReadStream::PartialReadStream(IOStream &rSource, + pos_type BytesToRead) + : mrSource(rSource), + mBytesLeft(BytesToRead) +{ + ASSERT(BytesToRead > 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::~PartialReadStream() +// Purpose: Destructor. Won't absorb any unread bytes. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +PartialReadStream::~PartialReadStream() +{ + // Warn in debug mode + if(mBytesLeft != 0) + { + BOX_TRACE("PartialReadStream destroyed with " << mBytesLeft << + " bytes remaining"); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::Read(void *, int, int) +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +int PartialReadStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + // Finished? + if(mBytesLeft <= 0) + { + return 0; + } + + // Asking for more than is allowed? + if(NBytes > mBytesLeft) + { + // Adjust downwards + NBytes = mBytesLeft; + } + + // Route the request to the source + int read = mrSource.Read(pBuffer, NBytes, Timeout); + ASSERT(read <= mBytesLeft); + + // Adjust the count + mBytesLeft -= read; + + // Return the number read + return read; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::BytesLeftToRead() +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type PartialReadStream::BytesLeftToRead() +{ + return mBytesLeft; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::Write(const void *, int) +// Purpose: As interface. But will exception. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void PartialReadStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, CantWriteToPartialReadStream) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::StreamDataLeft() +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool PartialReadStream::StreamDataLeft() +{ + return mBytesLeft != 0; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::StreamClosed() +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool PartialReadStream::StreamClosed() +{ + // always closed + return true; +} + diff --git a/lib/common/PartialReadStream.h b/lib/common/PartialReadStream.h new file mode 100644 index 00000000..61bdd7d1 --- /dev/null +++ b/lib/common/PartialReadStream.h @@ -0,0 +1,47 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: PartialReadStream.h +// Purpose: Read part of another stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#ifndef PARTIALREADSTREAM__H +#define PARTIALREADSTREAM__H + +#include "IOStream.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: PartialReadStream +// Purpose: Read part of another stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +class PartialReadStream : public IOStream +{ +public: + PartialReadStream(IOStream &rSource, pos_type BytesToRead); + ~PartialReadStream(); +private: + // no copying allowed + PartialReadStream(const IOStream &); + PartialReadStream(const PartialReadStream &); + +public: + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + IOStream &mrSource; + pos_type mBytesLeft; +}; + +#endif // PARTIALREADSTREAM__H + diff --git a/lib/common/PathUtils.cpp b/lib/common/PathUtils.cpp new file mode 100644 index 00000000..924d47d2 --- /dev/null +++ b/lib/common/PathUtils.cpp @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: PathUtils.cpp +// Purpose: Platform-independent path manipulation +// Created: 2007/01/17 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include + +// -------------------------------------------------------------------------- +// +// Function +// Name: MakeFullPath(const std::string& rDir, const std::string& rFile) +// Purpose: Combine directory and file name +// Created: 2006/08/10 +// +// -------------------------------------------------------------------------- +std::string MakeFullPath(const std::string& rDir, const std::string& rEntry) +{ + std::string result(rDir); + + if (result.size() > 0 && + result[result.size()-1] != DIRECTORY_SEPARATOR_ASCHAR) + { + result += DIRECTORY_SEPARATOR; + } + + result += rEntry; + + return result; +} diff --git a/lib/common/PathUtils.h b/lib/common/PathUtils.h new file mode 100644 index 00000000..1cf2e507 --- /dev/null +++ b/lib/common/PathUtils.h @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: PathUtils.h +// Purpose: Platform-independent path manipulation +// Created: 2007/01/17 +// +// -------------------------------------------------------------------------- + +#ifndef PATHUTILS_H +#define PATHUTILS_H + +#include + +// -------------------------------------------------------------------------- +// +// Function +// Name: MakeFullPath(const std::string& rDir, const std::string& rFile) +// Purpose: Combine directory and file name +// Created: 2006/08/10 +// +// -------------------------------------------------------------------------- + +std::string MakeFullPath(const std::string& rDir, const std::string& rEntry); + +#endif // !PATHUTILS_H diff --git a/lib/common/RateLimitingStream.cpp b/lib/common/RateLimitingStream.cpp new file mode 100644 index 00000000..8876f146 --- /dev/null +++ b/lib/common/RateLimitingStream.cpp @@ -0,0 +1,95 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: RateLimitingStream.cpp +// Purpose: Rate-limiting write-only wrapper around IOStreams +// Created: 2011/01/11 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "RateLimitingStream.h" +#include "CommonException.h" + +#include + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: RateLimitingStream::RateLimitingStream(const char *, int, int) +// Purpose: Constructor, set up buffer +// Created: 2011/01/11 +// +// -------------------------------------------------------------------------- +RateLimitingStream::RateLimitingStream(IOStream& rSink, size_t targetBytesPerSecond) +: mrSink(rSink), mStartTime(GetCurrentBoxTime()), mTotalBytesRead(0), + mTargetBytesPerSecond(targetBytesPerSecond) +{ } + +// -------------------------------------------------------------------------- +// +// Function +// Name: RateLimitingStream::Read(void *pBuffer, int NBytes, +// int Timeout) +// Purpose: Reads bytes to the underlying stream at no more than +// a fixed rate +// Created: 2011/01/11 +// +// -------------------------------------------------------------------------- +int RateLimitingStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + if(NBytes > 0 && (size_t)NBytes > mTargetBytesPerSecond) + { + // Limit to one second's worth of data for performance + BOX_TRACE("Reducing read size from " << NBytes << " to " << + mTargetBytesPerSecond << " to smooth upload rate"); + NBytes = mTargetBytesPerSecond; + } + + int bytesReadThisTime = mrSink.Read(pBuffer, NBytes, Timeout); + + // How many bytes we will have written after this write finishes? + mTotalBytesRead += bytesReadThisTime; + + // When should it be completed by? + box_time_t desiredFinishTime = mStartTime + + SecondsToBoxTime(mTotalBytesRead / mTargetBytesPerSecond); + + // How long do we have to wait? + box_time_t currentTime = GetCurrentBoxTime(); + int64_t waitTime = desiredFinishTime - currentTime; + + // How are we doing so far? (for logging only) + box_time_t currentDuration = currentTime - mStartTime; + + // in case our timer is not very accurate, don't divide by zero on first pass + if(currentDuration == 0) + { + BOX_TRACE("Current rate not yet known, sending immediately"); + return bytesReadThisTime; + } + + uint64_t effectiveRateSoFar = (mTotalBytesRead * MICRO_SEC_IN_SEC_LL) + / currentDuration; + + if(waitTime > 0) + { + BOX_TRACE("Current rate " << effectiveRateSoFar << + " higher than desired rate " << mTargetBytesPerSecond << + ", sleeping for " << BoxTimeToMilliSeconds(waitTime) << + " ms"); + ShortSleep(waitTime, false); + } + else + { + BOX_TRACE("Current rate " << effectiveRateSoFar << + " lower than desired rate " << mTargetBytesPerSecond << + ", sending immediately (would have sent " << + (BoxTimeToMilliSeconds(-waitTime)) << " ms ago)"); + } + + return bytesReadThisTime; +} + diff --git a/lib/common/RateLimitingStream.h b/lib/common/RateLimitingStream.h new file mode 100644 index 00000000..818c90af --- /dev/null +++ b/lib/common/RateLimitingStream.h @@ -0,0 +1,72 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: RateLimitingStream.h +// Purpose: Rate-limiting write-only wrapper around IOStreams +// Created: 2011/01/11 +// +// -------------------------------------------------------------------------- + +#ifndef RATELIMITINGSTREAM__H +#define RATELIMITINGSTREAM__H + +#include "BoxTime.h" +#include "IOStream.h" + +class RateLimitingStream : public IOStream +{ +private: + IOStream& mrSink; + box_time_t mStartTime; + uint64_t mTotalBytesRead; + size_t mTargetBytesPerSecond; + +public: + RateLimitingStream(IOStream& rSink, size_t targetBytesPerSecond); + virtual ~RateLimitingStream() { } + + // This is the only magic + virtual int Read(void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); + + // Everything else is delegated to the sink + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) + { + mrSink.Write(pBuffer, NBytes, Timeout); + } + virtual pos_type BytesLeftToRead() + { + return mrSink.BytesLeftToRead(); + } + virtual pos_type GetPosition() const + { + return mrSink.GetPosition(); + } + virtual void Seek(IOStream::pos_type Offset, int SeekType) + { + mrSink.Seek(Offset, SeekType); + } + virtual void Flush(int Timeout = IOStream::TimeOutInfinite) + { + mrSink.Flush(Timeout); + } + virtual void Close() + { + mrSink.Close(); + } + virtual bool StreamDataLeft() + { + return mrSink.StreamDataLeft(); + } + virtual bool StreamClosed() + { + return mrSink.StreamClosed(); + } + +private: + RateLimitingStream(const RateLimitingStream &rToCopy) + : mrSink(rToCopy.mrSink) { /* do not call */ } +}; + +#endif // RATELIMITINGSTREAM__H diff --git a/lib/common/ReadGatherStream.cpp b/lib/common/ReadGatherStream.cpp new file mode 100644 index 00000000..ae252832 --- /dev/null +++ b/lib/common/ReadGatherStream.cpp @@ -0,0 +1,263 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ReadGatherStream.cpp +// Purpose: Build a stream (for reading only) out of a number of other streams. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include "ReadGatherStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::ReadGatherStream(bool) +// Purpose: Constructor. Args says whether or not all the component streams will be deleted when this +// object is deleted. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +ReadGatherStream::ReadGatherStream(bool DeleteComponentStreamsOnDestruction) + : mDeleteComponentStreamsOnDestruction(DeleteComponentStreamsOnDestruction), + mCurrentPosition(0), + mTotalSize(0), + mCurrentBlock(0), + mPositionInCurrentBlock(0), + mSeekDoneForCurrent(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::~ReadGatherStream() +// Purpose: Destructor. Will delete all the stream objects, if required. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +ReadGatherStream::~ReadGatherStream() +{ + // Delete compoenent streams? + if(mDeleteComponentStreamsOnDestruction) + { + for(unsigned int l = 0; l < mComponents.size(); ++l) + { + delete mComponents[l]; + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::AddComponent(IOStream *) +// Purpose: Add a component to this stream, returning the index +// of this component in the internal list. Use this +// with AddBlock() +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +int ReadGatherStream::AddComponent(IOStream *pStream) +{ + ASSERT(pStream != 0); + + // Just add the component to the list, returning it's index. + int index = mComponents.size(); + mComponents.push_back(pStream); + return index; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::AddBlock(int, pos_type, bool, pos_type) +// Purpose: Add a block to the list of blocks being gathered into one stream. +// Length is length of block to read from this component, Seek == true +// if a seek is required, and if true, SeekTo is the position (absolute) +// in the stream to be seeked to when this block is required. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +void ReadGatherStream::AddBlock(int Component, pos_type Length, bool Seek, pos_type SeekTo) +{ + // Check block + if(Component < 0 || Component >= (int)mComponents.size() || Length < 0 || SeekTo < 0) + { + THROW_EXCEPTION(CommonException, ReadGatherStreamAddingBadBlock); + } + + // Add to list + Block b; + b.mLength = Length; + b.mSeekTo = SeekTo; + b.mComponent = Component; + b.mSeek = Seek; + + mBlocks.push_back(b); + + // And update the total size + mTotalSize += Length; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::Read(void *, int, int) +// Purpose: As interface. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +int ReadGatherStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + int bytesToRead = NBytes; + uint8_t *buffer = (uint8_t*)pBuffer; + + while(bytesToRead > 0) + { + // Done? + if(mCurrentBlock >= mBlocks.size()) + { + // Stop now, as have finished the last block + return NBytes - bytesToRead; + } + + // Seek? + if(mPositionInCurrentBlock == 0 && mBlocks[mCurrentBlock].mSeek && !mSeekDoneForCurrent) + { + // Do seeks in this manner so that seeks are done regardless of whether the block + // has length > 0, and it will only be done once, and at as late a stage as possible. + + mComponents[mBlocks[mCurrentBlock].mComponent]->Seek(mBlocks[mCurrentBlock].mSeekTo, IOStream::SeekType_Absolute); + + mSeekDoneForCurrent = true; + } + + // Anything in the current block? + if(mPositionInCurrentBlock < mBlocks[mCurrentBlock].mLength) + { + // Read! + pos_type s = mBlocks[mCurrentBlock].mLength - mPositionInCurrentBlock; + if(s > bytesToRead) s = bytesToRead; + + pos_type r = mComponents[mBlocks[mCurrentBlock].mComponent]->Read(buffer, s, Timeout); + + // update variables + mPositionInCurrentBlock += r; + buffer += r; + bytesToRead -= r; + mCurrentPosition += r; + + if(r != s) + { + // Stream returned less than requested. To avoid blocking when not necessary, + // return now. + return NBytes - bytesToRead; + } + } + else + { + // Move to next block + ++mCurrentBlock; + mPositionInCurrentBlock = 0; + mSeekDoneForCurrent = false; + } + } + + return NBytes - bytesToRead; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::GetPosition() +// Purpose: As interface +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ReadGatherStream::GetPosition() const +{ + return mCurrentPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::BytesLeftToRead() +// Purpose: As interface +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ReadGatherStream::BytesLeftToRead() +{ + return mTotalSize - mCurrentPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::Write(const void *, int) +// Purpose: As interface. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +void ReadGatherStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, CannotWriteToReadGatherStream); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::StreamDataLeft() +// Purpose: As interface. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +bool ReadGatherStream::StreamDataLeft() +{ + if(mCurrentBlock >= mBlocks.size()) + { + // Done all the blocks + return false; + } + + if(mCurrentBlock == (mBlocks.size() - 1) + && mPositionInCurrentBlock >= mBlocks[mCurrentBlock].mLength) + { + // Are on the last block, and have got all the data from it. + return false; + } + + // Otherwise, there's more data to be read + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::StreamClosed() +// Purpose: As interface. But the stream is always closed. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +bool ReadGatherStream::StreamClosed() +{ + return true; +} + + diff --git a/lib/common/ReadGatherStream.h b/lib/common/ReadGatherStream.h new file mode 100644 index 00000000..9a44480b --- /dev/null +++ b/lib/common/ReadGatherStream.h @@ -0,0 +1,68 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ReadGatherStream.h +// Purpose: Build a stream (for reading only) out of a number of other streams. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- + +#ifndef READGATHERSTREAM_H +#define READGATHERSTREAM_H + +#include "IOStream.h" + +#include + +// -------------------------------------------------------------------------- +// +// Class +// Name: ReadGatherStream +// Purpose: Build a stream (for reading only) out of a number of other streams. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +class ReadGatherStream : public IOStream +{ +public: + ReadGatherStream(bool DeleteComponentStreamsOnDestruction); + ~ReadGatherStream(); +private: + ReadGatherStream(const ReadGatherStream &); + ReadGatherStream &operator=(const ReadGatherStream &); +public: + + int AddComponent(IOStream *pStream); + void AddBlock(int Component, pos_type Length, bool Seek = false, pos_type SeekTo = 0); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + virtual pos_type GetPosition() const; + +private: + bool mDeleteComponentStreamsOnDestruction; + std::vector mComponents; + + typedef struct + { + pos_type mLength; + pos_type mSeekTo; + int mComponent; + bool mSeek; + } Block; + + std::vector mBlocks; + + pos_type mCurrentPosition; + pos_type mTotalSize; + unsigned int mCurrentBlock; + pos_type mPositionInCurrentBlock; + bool mSeekDoneForCurrent; +}; + + +#endif // READGATHERSTREAM_H diff --git a/lib/common/ReadLoggingStream.cpp b/lib/common/ReadLoggingStream.cpp new file mode 100644 index 00000000..df493344 --- /dev/null +++ b/lib/common/ReadLoggingStream.cpp @@ -0,0 +1,203 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ReadLoggingStream.cpp +// Purpose: Buffering wrapper around IOStreams +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "ReadLoggingStream.h" +#include "CommonException.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::ReadLoggingStream(const char *, int, int) +// Purpose: Constructor, set up buffer +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +ReadLoggingStream::ReadLoggingStream(IOStream& rSource, Logger& rLogger) +: mrSource(rSource), + mOffset(0), + mLength(mrSource.BytesLeftToRead()), + mTotalRead(0), + mStartTime(GetCurrentBoxTime()), + mrLogger(rLogger) +{ } + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::Read(void *, int) +// Purpose: Reads bytes from the file +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +int ReadLoggingStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + int numBytesRead = mrSource.Read(pBuffer, NBytes, Timeout); + + if (numBytesRead > 0) + { + mTotalRead += numBytesRead; + mOffset += numBytesRead; + } + + if (mLength == 0) + { + mrLogger.Log(numBytesRead, mOffset); + } + else if (mTotalRead == 0) + { + mrLogger.Log(numBytesRead, mOffset, mLength); + } + else + { + box_time_t timeNow = GetCurrentBoxTime(); + box_time_t elapsed = timeNow - mStartTime; + box_time_t finish = (elapsed * mLength) / mTotalRead; + // box_time_t remain = finish - elapsed; + mrLogger.Log(numBytesRead, mOffset, mLength, elapsed, finish); + } + + return numBytesRead; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::BytesLeftToRead() +// Purpose: Returns number of bytes to read (may not be most efficient function ever) +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ReadLoggingStream::BytesLeftToRead() +{ + return mLength - mOffset; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::Write(void *, int) +// Purpose: Writes bytes to the underlying stream (not supported) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void ReadLoggingStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::GetPosition() +// Purpose: Get position in stream +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ReadLoggingStream::GetPosition() const +{ + return mOffset; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::Seek(pos_type, int) +// Purpose: Seeks within file, as lseek, invalidate buffer +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void ReadLoggingStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + mrSource.Seek(Offset, SeekType); + + switch (SeekType) + { + case SeekType_Absolute: + { + // just go there + mOffset = Offset; + } + break; + + case SeekType_Relative: + { + // Actual underlying file position is + // (mBufferSize - mBufferPosition) ahead of us. + // Need to subtract that amount from the seek + // to seek forward that much less, putting the + // real pointer in the right place. + mOffset += Offset; + } + break; + + case SeekType_End: + { + // Actual underlying file position is + // (mBufferSize - mBufferPosition) ahead of us. + // Need to add that amount to the seek + // to seek backwards that much more, putting the + // real pointer in the right place. + mOffset = mLength - Offset; + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::Close() +// Purpose: Closes the underlying stream (not needed) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void ReadLoggingStream::Close() +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::StreamDataLeft() +// Purpose: Any data left to write? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool ReadLoggingStream::StreamDataLeft() +{ + return mrSource.StreamDataLeft(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadLoggingStream::StreamClosed() +// Purpose: Is the stream closed? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool ReadLoggingStream::StreamClosed() +{ + return mrSource.StreamClosed(); +} + diff --git a/lib/common/ReadLoggingStream.h b/lib/common/ReadLoggingStream.h new file mode 100644 index 00000000..bee7e1d6 --- /dev/null +++ b/lib/common/ReadLoggingStream.h @@ -0,0 +1,59 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ReadLoggingStream.h +// Purpose: Wrapper around IOStreams that logs read progress +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- + +#ifndef READLOGGINGSTREAM__H +#define READLOGGINGSTREAM__H + +#include "IOStream.h" +#include "BoxTime.h" + +class ReadLoggingStream : public IOStream +{ +public: + class Logger + { + public: + virtual ~Logger() { } + virtual void Log(int64_t readSize, int64_t offset, + int64_t length, box_time_t elapsed, + box_time_t finish) = 0; + virtual void Log(int64_t readSize, int64_t offset, + int64_t length) = 0; + virtual void Log(int64_t readSize, int64_t offset) = 0; + }; + +private: + IOStream& mrSource; + IOStream::pos_type mOffset, mLength, mTotalRead; + box_time_t mStartTime; + Logger& mrLogger; + +public: + ReadLoggingStream(IOStream& rSource, Logger& rLogger); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + 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(); + + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + ReadLoggingStream(const ReadLoggingStream &rToCopy) + : mrSource(rToCopy.mrSource), mrLogger(rToCopy.mrLogger) + { /* do not call */ } +}; + +#endif // READLOGGINGSTREAM__H + + diff --git a/lib/common/SelfFlushingStream.h b/lib/common/SelfFlushingStream.h new file mode 100644 index 00000000..b4efa294 --- /dev/null +++ b/lib/common/SelfFlushingStream.h @@ -0,0 +1,78 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: SelfFlushingStream.h +// Purpose: A stream wrapper that always flushes the underlying +// stream, to ensure protocol safety. +// Created: 2008/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef SELFFLUSHINGSTREAM__H +#define SELFFLUSHINGSTREAM__H + +#include "IOStream.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: SelfFlushingStream +// Purpose: A stream wrapper that always flushes the underlying +// stream, to ensure protocol safety. +// Created: 2008/08/20 +// +// -------------------------------------------------------------------------- +class SelfFlushingStream : public IOStream +{ +public: + SelfFlushingStream(IOStream &rSource) + : mrSource(rSource) { } + + SelfFlushingStream(const SelfFlushingStream &rToCopy) + : mrSource(rToCopy.mrSource) { } + + ~SelfFlushingStream() + { + if(StreamDataLeft()) + { + BOX_WARNING("Not all data was read from stream, " + "discarding the rest"); + } + + Flush(); + } + +private: + // no copying from IOStream allowed + SelfFlushingStream(const IOStream& rToCopy); + +public: + virtual int Read(void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) + { + return mrSource.Read(pBuffer, NBytes, Timeout); + } + virtual pos_type BytesLeftToRead() + { + return mrSource.BytesLeftToRead(); + } + virtual void Write(const void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) + { + mrSource.Write(pBuffer, NBytes, Timeout); + } + virtual bool StreamDataLeft() + { + return mrSource.StreamDataLeft(); + } + virtual bool StreamClosed() + { + return mrSource.StreamClosed(); + } + +private: + IOStream &mrSource; +}; + +#endif // SELFFLUSHINGSTREAM__H + diff --git a/lib/common/StreamableMemBlock.cpp b/lib/common/StreamableMemBlock.cpp new file mode 100644 index 00000000..9abf78d3 --- /dev/null +++ b/lib/common/StreamableMemBlock.cpp @@ -0,0 +1,371 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: StreamableMemBlock.cpp +// Purpose: Memory blocks which can be loaded and saved from streams +// with a header indicating the size of the block. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include "StreamableMemBlock.h" +#include "IOStream.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock() +// Purpose: Constructor, making empty block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock() + : mpBuffer(0), + mSize(0) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock(void *, int) +// Purpose: Create block, copying data from another bit of memory +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock(void *pBuffer, int Size) + : mpBuffer(0), + mSize(0) +{ + AllocateBlock(Size); + ::memcpy(mpBuffer, pBuffer, Size); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock(int) +// Purpose: Create block, initialising it to all zeros +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock(int Size) + : mpBuffer(0), + mSize(0) +{ + AllocateBlock(Size); + ::memset(mpBuffer, 0, Size); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock(const StreamableMemBlock &) +// Purpose: Copy constructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock(const StreamableMemBlock &rToCopy) + : mpBuffer(0), + mSize(0) +{ + AllocateBlock(rToCopy.mSize); + ::memcpy(mpBuffer, rToCopy.mpBuffer, mSize); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::Set(void *, int) +// Purpose: Set the contents of the block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::Set(void *pBuffer, int Size) +{ + FreeBlock(); + AllocateBlock(Size); + ::memcpy(mpBuffer, pBuffer, Size); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::Set(IOStream &) +// Purpose: Set from stream. Stream must support BytesLeftToRead() +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::Set(IOStream &rStream, int Timeout) +{ + // Get size + IOStream::pos_type size = rStream.BytesLeftToRead(); + if(size == IOStream::SizeOfStreamUnknown) + { + THROW_EXCEPTION(CommonException, StreamDoesntHaveRequiredProperty) + } + + // Allocate a new block (this way to be exception safe) + char *pblock = (char*)malloc(size); + if(pblock == 0) + { + throw std::bad_alloc(); + } + + try + { + // Read in + if(!rStream.ReadFullBuffer(pblock, size, + 0 /* not interested in bytes read if this fails */, + Timeout)) + { + THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) + } + + // Free the block ready for replacement + FreeBlock(); + } + catch(...) + { + ::free(pblock); + throw; + } + + // store... + ASSERT(mpBuffer == 0); + mpBuffer = pblock; + mSize = size; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::Set(const StreamableMemBlock &) +// Purpose: Set from other block. +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::Set(const StreamableMemBlock &rBlock) +{ + Set(rBlock.mpBuffer, rBlock.mSize); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::~StreamableMemBlock() +// Purpose: Destructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::~StreamableMemBlock() +{ + FreeBlock(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::FreeBlock() +// Purpose: Protected. Frees block of memory +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::FreeBlock() +{ + if(mpBuffer != 0) + { + ::free(mpBuffer); + } + mpBuffer = 0; + mSize = 0; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::AllocateBlock(int) +// Purpose: Protected. Allocate the block of memory +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::AllocateBlock(int Size) +{ + ASSERT(mpBuffer == 0); + if(Size > 0) + { + mpBuffer = ::malloc(Size); + if(mpBuffer == 0) + { + throw std::bad_alloc(); + } + } + mSize = Size; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::ResizeBlock(int) +// Purpose: Protected. Resizes the allocated block. +// Created: 3/12/03 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::ResizeBlock(int Size) +{ + ASSERT(Size > 0); + if(Size > 0) + { + void *pnewBuffer = ::realloc(mpBuffer, Size); + if(pnewBuffer == 0) + { + throw std::bad_alloc(); + } + mpBuffer = pnewBuffer; + } + mSize = Size; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::ReadFromStream(IOStream &, int) +// Purpose: Read the block in from a stream +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +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 */ + Timeout)) + { + THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) + } + + int size = ntohl(size_s); + + + // Allocate a new block (this way to be exception safe) + char *pblock = (char*)malloc(size); + if(pblock == 0) + { + throw std::bad_alloc(); + } + + try + { + // Read in + if(!rStream.ReadFullBuffer(pblock, size, + 0, /* not interested in bytes read if this fails */ + Timeout)) + { + THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) + } + + // Free the block ready for replacement + FreeBlock(); + } + catch(...) + { + ::free(pblock); + throw; + } + + // store... + ASSERT(mpBuffer == 0); + mpBuffer = pblock; + mSize = size; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::WriteToStream(IOStream &) +// Purpose: Write the block to a stream +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::WriteToStream(IOStream &rStream) const +{ + int32_t sizenbo = htonl(mSize); + // Size + rStream.Write(&sizenbo, sizeof(sizenbo)); + // Buffer + if(mSize > 0) + { + rStream.Write(mpBuffer, mSize); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::WriteEmptyBlockToStream(IOStream &) +// Purpose: Writes an empty block to a stream. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::WriteEmptyBlockToStream(IOStream &rStream) +{ + int32_t sizenbo = htonl(0); + rStream.Write(&sizenbo, sizeof(sizenbo)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::GetBuffer() +// Purpose: Get pointer to buffer +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void *StreamableMemBlock::GetBuffer() const +{ + if(mSize == 0) + { + // Return something which isn't a null pointer + static const int validptr = 0; + return (void*)&validptr; + } + + // return the buffer + ASSERT(mpBuffer != 0); + return mpBuffer; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::operator==(const StreamableMemBlock &) +// Purpose: Test for equality of memory blocks +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +bool StreamableMemBlock::operator==(const StreamableMemBlock &rCompare) const +{ + if(mSize != rCompare.mSize) return false; + if(mSize == 0 && rCompare.mSize == 0) return true; // without memory comparison! + return ::memcmp(mpBuffer, rCompare.mpBuffer, mSize) == 0; +} + + diff --git a/lib/common/StreamableMemBlock.h b/lib/common/StreamableMemBlock.h new file mode 100644 index 00000000..6c1c5ab8 --- /dev/null +++ b/lib/common/StreamableMemBlock.h @@ -0,0 +1,72 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: StreamableMemBlock.h +// Purpose: Memory blocks which can be loaded and saved from streams, +// with a header indicating the size of the block. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#ifndef STREAMABLEMEMBLOCK__H +#define STREAMABLEMEMBLOCK__H + +class IOStream; + +// -------------------------------------------------------------------------- +// +// Class +// Name: StreamableMemBlock +// Purpose: Memory blocks which can be loaded and saved from streams +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +class StreamableMemBlock +{ +public: + StreamableMemBlock(); + StreamableMemBlock(int Size); + StreamableMemBlock(void *pBuffer, int Size); + StreamableMemBlock(const StreamableMemBlock &rToCopy); + ~StreamableMemBlock(); + + void Set(const StreamableMemBlock &rBlock); + void Set(void *pBuffer, int Size); + void Set(IOStream &rStream, int Timeout); + StreamableMemBlock &operator=(const StreamableMemBlock &rBlock) + { + Set(rBlock); + return *this; + } + + void ReadFromStream(IOStream &rStream, int Timeout); + void WriteToStream(IOStream &rStream) const; + + static void WriteEmptyBlockToStream(IOStream &rStream); + + void *GetBuffer() const; + + // Size of block + int GetSize() const {return mSize;} + + // Buffer empty? + bool IsEmpty() const {return mSize == 0;} + + // Clear the contents of the block + void Clear() {FreeBlock();} + + bool operator==(const StreamableMemBlock &rCompare) const; + + void ResizeBlock(int Size); + +protected: // be careful with these! + void AllocateBlock(int Size); + void FreeBlock(); + +private: + void *mpBuffer; + int mSize; +}; + +#endif // STREAMABLEMEMBLOCK__H + diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp new file mode 100644 index 00000000..c6b28738 --- /dev/null +++ b/lib/common/Test.cpp @@ -0,0 +1,518 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Test.cpp +// Purpose: Useful stuff for tests +// Created: 2008/04/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include "BoxTime.h" +#include "FileStream.h" +#include "Test.h" +#include "Utils.h" + +int num_tests_selected = 0; +int num_failures = 0; +static int old_failure_count = 0; // do not expose! +int first_fail_line; +std::string original_working_dir; +std::string first_fail_file; +std::string current_test_name; +std::list run_only_named_tests; +std::map 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::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" || + filename == "bbackupd-data" || + filename == "ca" || + 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) || + filename == "tmp" || + EndsWith(".qdbm", filename)) + { + std::string filepath = std::string("testfiles\\") + filename; + + int filetype = ObjectExists(filepath); + if(filetype == ObjectExists_File) + { + if(EMU_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 " + "testfiles/tmp " + ) == 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::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) +{ + EMU_STRUCT_STAT st; + return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0; +} + +bool TestFileNotEmpty(const char *Filename) +{ + EMU_STRUCT_STAT st; + return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0 && + st.st_size > 0; +} + +bool TestDirExists(const char *Filename) +{ + EMU_STRUCT_STAT st; + return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR; +} + +// -1 if doesn't exist +int TestGetFileSize(const std::string& Filename) +{ + EMU_STRUCT_STAT st; + if(EMU_STAT(Filename.c_str(), &st) == 0) + { + return st.st_size; + } + return -1; +} + +std::string ConvertPaths(const std::string& rOriginal) +{ +#ifdef WIN32 + // convert UNIX paths to native + + std::string converted; + for (size_t i = 0; i < rOriginal.size(); i++) + { + if (rOriginal[i] == '/') + { + converted += '\\'; + } + else + { + converted += rOriginal[i]; + } + } + return converted; + +#else // !WIN32 + return rOriginal; +#endif +} + +int RunCommand(const std::string& rCommandLine) +{ + return ::system(ConvertPaths(rCommandLine).c_str()); +} + +#ifdef WIN32 +#include +#endif + +bool ServerIsAlive(int pid) +{ + #ifdef WIN32 + + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, + false, pid); + if (hProcess == NULL) + { + if (GetLastError() != ERROR_INVALID_PARAMETER) + { + BOX_ERROR("Failed to open process " << pid << + ": " << + GetErrorMessage(GetLastError())); + } + return false; + } + + DWORD exitCode; + BOOL result = GetExitCodeProcess(hProcess, &exitCode); + CloseHandle(hProcess); + + if (result == 0) + { + BOX_ERROR("Failed to get exit code for process " << + pid << ": " << + GetErrorMessage(GetLastError())) + return false; + } + + if (exitCode == STILL_ACTIVE) + { + return true; + } + + return false; + + #else // !WIN32 + + if(pid == 0) return false; + return ::kill(pid, 0) != -1; + + #endif // WIN32 +} + +int ReadPidFile(const char *pidFile) +{ + if(!TestFileNotEmpty(pidFile)) + { + TEST_FAIL_WITH_MESSAGE("Server didn't save PID file " + "(perhaps one was already running?)"); + return -1; + } + + int pid = -1; + + FILE *f = fopen(pidFile, "r"); + if(f == NULL || fscanf(f, "%d", &pid) != 1) + { + TEST_FAIL_WITH_MESSAGE("Couldn't read PID file"); + return -1; + } + fclose(f); + + return pid; +} + +void TestRemoteProcessMemLeaksFunc(const char *filename, + const char* file, int line) +{ +#ifdef BOX_MEMORY_LEAK_TESTING + // Does the file exist? + if(!TestFileExists(filename)) + { + if (num_failures == 0) + { + first_fail_file = file; + first_fail_line = line; + } + ++num_failures; + printf("FAILURE: MemLeak report not available (file %s) " + "at %s:%d\n", filename, file, line); + } + else + { + // Is it empty? + if(TestGetFileSize(filename) > 0) + { + if (num_failures == 0) + { + first_fail_file = file; + first_fail_line = line; + } + ++num_failures; + printf("FAILURE: Memory leaks found in other process " + "(file %s) at %s:%d\n==========\n", + filename, file, line); + FILE *f = fopen(filename, "r"); + char linebuf[512]; + while(::fgets(linebuf, sizeof(linebuf), f) != 0) + { + printf("%s", linebuf); + } + fclose(f); + printf("==========\n"); + } + + // Delete it + EMU_UNLINK(filename); + } +#endif +} + +void force_sync() +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "force-sync") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); +} + +void wait_for_sync_start() +{ + 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) +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "terminate") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); + + for (int i = 0; i < 20; i++) + { + if (!ServerIsAlive(pid)) break; + fprintf(stdout, "."); + fflush(stdout); + sleep(1); + } + + TEST_THAT(!ServerIsAlive(pid)); + TestRemoteProcessMemLeaks("bbackupd.memleaks"); +} + + +// Wait a given number of seconds for something to complete +void wait_for_operation(int seconds, const char* message) +{ + BOX_INFO("Waiting " << seconds << " seconds for " << message); + + for(int l = 0; l < seconds; ++l) + { + sleep(1); + } + + BOX_TRACE("Finished waiting for " << message); +} + +void safe_sleep(int seconds) +{ + ShortSleep(SecondsToBoxTime(seconds), true); +} + +std::auto_ptr load_config_file(const std::string& config_file, + const ConfigurationVerify& verify) +{ + std::string errs; + std::auto_ptr 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 new file mode 100644 index 00000000..35443c29 --- /dev/null +++ b/lib/common/Test.h @@ -0,0 +1,260 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Test.h +// Purpose: Useful stuff for tests +// Created: 2003/07/11 +// +// -------------------------------------------------------------------------- + +#ifndef TEST__H +#define TEST__H + +#include +#include +#include + +#include "Configuration.h" + +#ifdef WIN32 +#define BBACKUPCTL "..\\..\\bin\\bbackupctl\\bbackupctl.exe" +#define BBACKUPD "..\\..\\bin\\bbackupd\\bbackupd.exe" +#define BBSTORED "..\\..\\bin\\bbstored\\bbstored.exe" +#define BBACKUPQUERY "..\\..\\bin\\bbackupquery\\bbackupquery.exe" +#define BBSTOREACCOUNTS "..\\..\\bin\\bbstoreaccounts\\bbstoreaccounts.exe" +#define TEST_RETURN(actual, expected) TEST_EQUAL(expected, actual); +#define TEST_RETURN_COMMAND(actual, expected, command) TEST_EQUAL_LINE(expected, actual, command); +#else +#define BBACKUPCTL "../../bin/bbackupctl/bbackupctl" +#define BBACKUPD "../../bin/bbackupd/bbackupd" +#define BBSTORED "../../bin/bbstored/bbstored" +#define BBACKUPQUERY "../../bin/bbackupquery/bbackupquery" +#define BBSTOREACCOUNTS "../../bin/bbstoreaccounts/bbstoreaccounts" +#define TEST_RETURN(actual, expected) TEST_EQUAL((expected << 8), actual); +#define TEST_RETURN_COMMAND(actual, expected, command) TEST_EQUAL_LINE((expected << 8), actual, command); +#endif + +#define DEFAULT_BBSTORED_CONFIG_FILE "testfiles/bbstored.conf" +#define DEFAULT_BBACKUPD_CONFIG_FILE "testfiles/bbackupd.conf" +#define DEFAULT_S3_CACHE_DIR "testfiles/bbackupd-cache" + +extern int num_failures; +extern int first_fail_line; +extern int num_tests_selected; +extern std::string first_fail_file; +extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; +extern std::list run_only_named_tests; +extern std::string current_test_name; +extern std::map 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 (num_failures == 0) \ + { \ + first_fail_file = __FILE__; \ + first_fail_line = __LINE__; \ + } \ + 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_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_AND_OR(statement, excepttype, subtype, and_command, or_command) \ + { \ + bool didthrow = false; \ + HideExceptionMessageGuard hide; \ + BOX_TRACE("Exception logging disabled at " __FILE__ ":" \ + << __LINE__); \ + try \ + { \ + statement; \ + } \ + catch(excepttype &e) \ + { \ + if(e.GetSubType() != ((unsigned int)excepttype::subtype) \ + && e.GetSubType() != (unsigned int)(0-excepttype::subtype)) \ + { \ + throw; \ + } \ + didthrow = true; \ + and_command; \ + } \ + catch(...) \ + { \ + throw; \ + } \ + if(!didthrow) \ + { \ + 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_OR(_expected, _found, or_command) \ +{ \ + std::ostringstream _oss1; \ + _oss1 << _expected; \ + std::string _exp_str = _oss1.str(); \ + \ + std::ostringstream _oss2; \ + _oss2 << _found; \ + std::string _found_str = _oss2.str(); \ + \ + if(_exp_str != _found_str) \ + { \ + BOX_ERROR("Expected <" << _exp_str << "> but found <" << \ + _found_str << ">"); \ + \ + std::ostringstream _oss3; \ + _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) \ +{ \ + std::ostringstream _oss1; \ + _oss1 << _expected; \ + std::string _exp_str = _oss1.str(); \ + \ + std::ostringstream _oss2; \ + _oss2 << _found; \ + std::string _found_str = _oss2.str(); \ + \ + if(_exp_str != _found_str) \ + { \ + std::ostringstream _ossl; \ + _ossl << _line; \ + std::string _line_str = _ossl.str(); \ + printf("Expected <%s> but found <%s> in <%s>\n", \ + _exp_str.c_str(), _found_str.c_str(), _line_str.c_str()); \ + \ + std::ostringstream _oss3; \ + _oss3 << #_found << " != " << #_expected << " in " << _line; \ + \ + TEST_FAIL_WITH_MESSAGE(_oss3.str().c_str()); \ + } \ +} + +// utility macros for testing a string/output line +#define TEST_LINE(_condition, _line) \ + TEST_THAT(_condition); \ + if (!(_condition)) \ + { \ + std::ostringstream _ossl; \ + _ossl << _line; \ + std::string _line_str = _ossl.str(); \ + 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); +bool TestFileNotEmpty(const char *Filename); + +// -1 if doesn't exist +int TestGetFileSize(const std::string& Filename); +std::string ConvertPaths(const std::string& rOriginal); +int RunCommand(const std::string& rCommandLine); +bool ServerIsAlive(int pid); +int ReadPidFile(const char *pidFile); + +#define TestRemoteProcessMemLeaks(filename) \ + TestRemoteProcessMemLeaksFunc(filename, __FILE__, __LINE__) + +void TestRemoteProcessMemLeaksFunc(const char *filename, + const char* file, int line); + +void force_sync(); +void wait_for_sync_start(); +void wait_for_sync_end(); +void sync_and_wait(); +void terminate_bbackupd(int pid); + +// Wait a given number of seconds for something to complete +void wait_for_operation(int seconds, const char* message); +void safe_sleep(int seconds); +std::auto_ptr 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 new file mode 100644 index 00000000..4f8c989e --- /dev/null +++ b/lib/common/Timer.cpp @@ -0,0 +1,648 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Timer.cpp +// Purpose: Generic timers which execute arbitrary code when +// they expire. +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef WIN32 +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +# elif _WIN32_WINNT < 0x0500 +# error Timers require at least Windows 2000 headers +# endif +#endif + +#include +#include + +#include "Timer.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +std::vector* Timers::spTimers = NULL; +bool Timers::sRescheduleNeeded = false; + +#define TIMER_ID "timer " << mName << " (" << this << ") " +#define TIMER_ID_OF(t) "timer " << (t).GetName() << " (" << &(t) << ")" + +typedef void (*sighandler_t)(int); + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::Init() +// Purpose: Initialise timers, prepare signal handler +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- +void Timers::Init() +{ + ASSERT(!spTimers); + + #if defined WIN32 && ! defined PLATFORM_CYGWIN + // no init needed + #else + struct sigaction newact, oldact; + newact.sa_handler = Timers::SignalHandler; + newact.sa_flags = SA_RESTART; + sigemptyset(&newact.sa_mask); + if (::sigaction(SIGALRM, &newact, &oldact) != 0) + { + THROW_SYS_ERROR("Failed to install signal handler", + CommonException, Internal); + } + ASSERT(oldact.sa_handler == 0); + #endif // WIN32 && !PLATFORM_CYGWIN + + spTimers = new std::vector; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::Cleanup() +// Purpose: Clean up timers, stop signal handler +// Created: 6/11/2006 +// +// -------------------------------------------------------------------------- +void Timers::Cleanup(bool throw_exception_if_not_initialised) +{ + if (throw_exception_if_not_initialised) + { + 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 + // no cleanup needed + #else + struct itimerval timeout; + memset(&timeout, 0, sizeof(timeout)); + + 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; + newact.sa_flags = SA_RESTART; + sigemptyset(&(newact.sa_mask)); + if (::sigaction(SIGALRM, &newact, &oldact) != 0) + { + THROW_SYS_ERROR("Failed to remove signal handler", + CommonException, Internal); + } + ASSERT(oldact.sa_handler == Timers::SignalHandler); + #endif // WIN32 && !PLATFORM_CYGWIN + + spTimers->clear(); + delete spTimers; + spTimers = NULL; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::Add(Timer&) +// Purpose: Add a new timer to the set, and reschedule next wakeup +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- +void Timers::Add(Timer& rTimer) +{ + ASSERT(spTimers); + BOX_TRACE(TIMER_ID_OF(rTimer) " added to global queue, rescheduling"); + spTimers->push_back(&rTimer); + Reschedule(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::Remove(Timer&) +// Purpose: Removes the timer from the set (preventing it from +// being called) and reschedule next wakeup +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- +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); + BOX_TRACE(TIMER_ID_OF(rTimer) " removed from global queue, rescheduling"); + + bool restart = true; + while (restart) + { + restart = false; + + for (std::vector::iterator i = spTimers->begin(); + i != spTimers->end(); i++) + { + if (&rTimer == *i) + { + spTimers->erase(i); + restart = true; + break; + } + } + } + + Reschedule(); +} + +void Timers::RequestReschedule() +{ + sRescheduleNeeded = true; +} + +void Timers::RescheduleIfNeeded() +{ + if (sRescheduleNeeded) + { + Reschedule(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::Reschedule() +// Purpose: Recalculate when the next wakeup is due +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- +void Timers::Reschedule() +{ + ASSERT(spTimers); + if (spTimers == NULL) + { + THROW_EXCEPTION(CommonException, TimersNotInitialised); + } + + #ifndef WIN32 + struct sigaction oldact; + if (::sigaction(SIGALRM, NULL, &oldact) != 0) + { + THROW_SYS_ERROR("Failed to check signal handler", + CommonException, Internal); + } + + ASSERT(oldact.sa_handler == Timers::SignalHandler); + + if (oldact.sa_handler != Timers::SignalHandler) + { + THROW_EXCEPTION_MESSAGE(CommonException, Internal, + "Signal handler was " << (void *)oldact.sa_handler << + ", expected " << (void *)Timers::SignalHandler); + } + #endif + + // Clear the reschedule-needed flag to false before we start. + // If a timer event occurs while we are scheduling, then we + // may or may not need to reschedule again, but this way + // we will do it anyway. + sRescheduleNeeded = false; + +#ifdef WIN32 + // 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. + bool restart = true; + while (restart) + { + restart = false; + timeToNextEvent = 0; + + for (std::vector::iterator i = spTimers->begin(); + i != spTimers->end(); i++) + { + Timer& rTimer = **i; + int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow; + + if (timeToExpiry <= 0) + { + BOX_TRACE(TIMER_ID_OF(**i) " has expired, " + "triggering " << + BOX_FORMAT_MICROSECONDS(-timeToExpiry) << + " late"); + rTimer.OnExpire(); + spTimers->erase(i); + restart = true; + break; + } + else + { + /* + BOX_TRACE(TIMER_ID_OF(**i) " has not expired, " + "triggering in " << + FORMAT_MICROSECONDS(timeToExpiry) << + " seconds"); + */ + } + + if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry) + { + timeToNextEvent = timeToExpiry; + nameOfNextEvent = rTimer.GetName(); + } + } + } + + if (timeToNextEvent == 0) + { + BOX_TRACE("timer: no more events, going to sleep."); + } + else + { + BOX_TRACE("timer: next event: " << nameOfNextEvent << " at " << + FormatTime(timeNow + timeToNextEvent, false, true)); + } + + struct itimerval timeout; + memset(&timeout, 0, sizeof(timeout)); + + timeout.it_value.tv_sec = BoxTimeToSeconds(timeToNextEvent); + timeout.it_value.tv_usec = (int) + (BoxTimeToMicroSeconds(timeToNextEvent) + % MICRO_SEC_IN_SEC); + + if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0) + { + THROW_SYS_ERROR("Failed to initialise system timer", + CommonException, Internal); + } +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::SignalHandler(unused) +// Purpose: Called as signal handler. Nothing is safe in a signal +// handler, not even traversing the list of timers, so +// just request a reschedule in future, which will do +// that for us, and trigger any expired timers at that +// time. +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- +void Timers::SignalHandler(int unused) +{ + Timers::RequestReschedule(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Timer(size_t timeoutMillis, +// const std::string& rName) +// Purpose: Standard timer constructor, takes a timeout in +// seconds from now, and an optional name for +// logging purposes. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer::Timer(size_t timeoutMillis, const std::string& rName) +: mExpires(0), + mExpired(false), + mName(rName) +#ifdef WIN32 +, mTimerHandle(INVALID_HANDLE_VALUE) +#endif +{ + Set(timeoutMillis, true /* isInit */); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Start() +// Purpose: This internal function recalculates the remaining +// time (timeout) from the expiry time, and then calls +// Start(timeoutMillis). +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::Start() +{ + box_time_t timeNow = GetCurrentBoxTime(); + int64_t timeToExpiry = mExpires - timeNow; + + if (timeToExpiry <= 0) + { + BOX_WARNING(TIMER_ID << "fudging expiry from -" << + BOX_FORMAT_MICROSECONDS(-timeToExpiry)) + timeToExpiry = 1; + } + + Start(timeToExpiry / MICRO_SEC_IN_MILLI_SEC); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Start(int64_t timeoutMillis) +// Purpose: This internal function adds this timer to the global +// timer list, and on Windows it initialises an OS +// TimerQueue timer for it. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::Start(int64_t timeoutMillis) +{ + ASSERT(mExpires != 0); + Timers::Add(*this); + +#ifdef WIN32 + // only call me once! + ASSERT(mTimerHandle == INVALID_HANDLE_VALUE); + + // Windows XP always seems to fire timers up to 20 ms late, + // at least on my test laptop. Not critical in practice, but our + // tests are precise enough that they will fail if we don't + // correct for it. + timeoutMillis -= 20; + + // Set a system timer to call our timer routine + if (CreateTimerQueueTimer(&mTimerHandle, NULL, TimerRoutine, + (PVOID)this, timeoutMillis, 0, WT_EXECUTEINTIMERTHREAD) + == FALSE) + { + BOX_ERROR(TIMER_ID "failed to create timer: " << + GetErrorMessage(GetLastError())); + mTimerHandle = INVALID_HANDLE_VALUE; + } + else + { + BOX_INFO(TIMER_ID << "set for " << timeoutMillis << " ms"); + } +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Stop() +// Purpose: This internal function removes us from the global +// list of timers, resets our expiry time, and on +// Windows it deletes the associated OS TimerQueue timer. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::Stop() +{ + if (mExpires != 0) + { + Timers::Remove(*this); + } + +#ifdef WIN32 + if (mTimerHandle != INVALID_HANDLE_VALUE) + { + if (DeleteTimerQueueTimer(NULL, mTimerHandle, + INVALID_HANDLE_VALUE) == FALSE) + { + BOX_ERROR(TIMER_ID "failed to delete timer: " << + GetErrorMessage(GetLastError())); + } + mTimerHandle = INVALID_HANDLE_VALUE; + } +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::~Timer() +// Purpose: Destructor for Timer objects. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer::~Timer() +{ + #ifndef BOX_RELEASE_BUILD + BOX_TRACE(TIMER_ID "destroyed"); + #endif + + Stop(); +} + +void Timer::LogAssignment(const Timer &From) +{ + #ifndef BOX_RELEASE_BUILD + BOX_TRACE(TIMER_ID "initialised from " << TIMER_ID_OF(From)); + + if (From.mExpired) + { + BOX_TRACE(TIMER_ID "already expired, will not fire"); + } + else if (From.mExpires == 0) + { + BOX_TRACE(TIMER_ID "has no expiry time, will not fire"); + } + else + { + BOX_TRACE(TIMER_ID "will fire at " << + FormatTime(From.mExpires, false, true)); + } + #endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Timer(Timer& rToCopy) +// Purpose: Copy constructor for Timer objects. Creates a new +// timer that will trigger at the same time as the +// original. The original will usually be discarded. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer::Timer(const Timer& rToCopy) +: mExpires(rToCopy.mExpires), + mExpired(rToCopy.mExpired), + mName(rToCopy.mName) +#ifdef WIN32 +, mTimerHandle(INVALID_HANDLE_VALUE) +#endif +{ + LogAssignment(rToCopy); + + if (!mExpired && mExpires != 0) + { + Start(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::operator=(const Timer& rToCopy) +// Purpose: Assignment operator for Timer objects. Works +// exactly the same as the copy constructor, except +// that if the receiving timer is already running, +// it is stopped first. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer& Timer::operator=(const Timer& rToCopy) +{ + LogAssignment(rToCopy); + + Stop(); + + mExpires = rToCopy.mExpires; + mExpired = rToCopy.mExpired; + mName = rToCopy.mName; + + if (!mExpired && mExpires != 0) + { + Start(); + } + + return *this; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Reset(size_t timeoutMillis) +// Purpose: Simple reset operation for an existing Timer. Avoids +// the need to create a temporary timer just to modify +// an existing one. +// Created: 17/11/2012 +// +// -------------------------------------------------------------------------- + +void Timer::Reset(size_t timeoutMillis) +{ + Set(timeoutMillis, false /* isInit */); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Reset(size_t timeoutMillis) +// Purpose: Internal set/reset operation for an existing Timer. +// Shared by constructor and Reset(). +// Created: 17/11/2012 +// +// -------------------------------------------------------------------------- + +void Timer::Set(size_t timeoutMillis, bool isInit) +{ + Stop(); + mExpired = false; + + if (timeoutMillis == 0) + { + mExpires = 0; + } + else + { + mExpires = GetCurrentBoxTime() + + MilliSecondsToBoxTime(timeoutMillis); + } + + #ifndef BOX_RELEASE_BUILD + if (timeoutMillis == 0) + { + BOX_TRACE(TIMER_ID << (isInit ? "initialised" : "reset") << + " for " << timeoutMillis << " ms, will not fire"); + } + else + { + BOX_TRACE(TIMER_ID << (isInit ? "initialised" : "reset") << + " for " << timeoutMillis << " ms, to fire at " << + FormatTime(mExpires, false, true)); + } + #endif + + if (mExpires != 0) + { + Start(timeoutMillis); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::OnExpire() +// Purpose: Method called by Timers::Reschedule (on Unixes) +// on next poll after timer expires, or from +// Timer::TimerRoutine (on Windows) from a separate +// thread managed by the OS. Marks the timer as +// expired for future reference. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::OnExpire() +{ + #ifndef BOX_RELEASE_BUILD + BOX_TRACE(TIMER_ID "fired"); + #endif + + mExpired = true; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::TimerRoutine(PVOID lpParam, +// BOOLEAN TimerOrWaitFired) +// Purpose: Static method called by the Windows OS when a +// TimerQueue timer expires. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +#ifdef WIN32 +VOID CALLBACK Timer::TimerRoutine(PVOID lpParam, + BOOLEAN TimerOrWaitFired) +{ + Timer* pTimer = (Timer*)lpParam; + pTimer->OnExpire(); + // is it safe to write to write debug output from a timer? + // e.g. to write to the Event Log? +} +#endif diff --git a/lib/common/Timer.h b/lib/common/Timer.h new file mode 100644 index 00000000..17233203 --- /dev/null +++ b/lib/common/Timer.h @@ -0,0 +1,92 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Timer.h +// Purpose: Generic timers which execute arbitrary code when +// they expire. +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- + +#ifndef TIMER__H +#define TIMER__H + +#ifdef HAVE_SYS_TIME_H + #include +#endif + +#include + +#include "BoxTime.h" + +#include "MemLeakFindOn.h" + +class Timer; + +// -------------------------------------------------------------------------- +// +// Class +// Name: Timers +// Purpose: Static class to manage all timers and arrange +// efficient delivery of wakeup signals +// Created: 19/3/04 +// +// -------------------------------------------------------------------------- +class Timers +{ + private: + static std::vector* spTimers; + static void Reschedule(); + + static bool sRescheduleNeeded; + static void SignalHandler(int iUnused); + + public: + static void Init(); + static void Cleanup(bool throw_exception_if_not_initialised = true); + static void Add (Timer& rTimer); + static void Remove(Timer& rTimer); + static void RequestReschedule(); + static void RescheduleIfNeeded(); +}; + +class Timer +{ +public: + Timer(size_t timeoutMillis, const std::string& rName = ""); + virtual ~Timer(); + Timer(const Timer &); + Timer &operator=(const Timer &); + + box_time_t GetExpiryTime() { return mExpires; } + virtual void OnExpire(); + bool HasExpired() + { + Timers::RescheduleIfNeeded(); + return mExpired; + } + + const std::string& GetName() const { return mName; } + virtual void Reset(size_t timeoutMillis); + +private: + box_time_t mExpires; + bool mExpired; + std::string mName; + + void Start(); + void Start(int64_t timeoutMillis); + void Stop(); + void LogAssignment(const Timer &From); + virtual void Set(size_t timeoutMillis, bool isReset); + + #ifdef WIN32 + HANDLE mTimerHandle; + static VOID CALLBACK TimerRoutine(PVOID lpParam, + BOOLEAN TimerOrWaitFired); + #endif +}; + +#include "MemLeakFindOff.h" + +#endif // TIMER__H diff --git a/lib/common/UnixUser.cpp b/lib/common/UnixUser.cpp new file mode 100644 index 00000000..32acacfa --- /dev/null +++ b/lib/common/UnixUser.cpp @@ -0,0 +1,123 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: UnixUser.cpp +// Purpose: Interface for managing the UNIX user of the current process +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_PWD_H + #include +#endif + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include "UnixUser.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::UnixUser(const char *) +// Purpose: Constructor, initialises to info of given username +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- +UnixUser::UnixUser(const std::string& Username) + : mUID(0), + mGID(0), + mRevertOnDestruction(false) +{ + // Get password info + struct passwd *pwd = ::getpwnam(Username.c_str()); + if(pwd == 0) + { + THROW_EXCEPTION(CommonException, CouldNotLookUpUsername) + } + + // Store UID and GID + mUID = pwd->pw_uid; + mGID = pwd->pw_gid; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::UnixUser(uid_t, gid_t) +// Purpose: Construct from given UNIX user ID and group ID +// Created: 15/3/04 +// +// -------------------------------------------------------------------------- +UnixUser::UnixUser(uid_t UID, gid_t GID) + : mUID(UID), + mGID(GID), + mRevertOnDestruction(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::~UnixUser() +// Purpose: Destructor -- reverts to previous user if the change wasn't perminant +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- +UnixUser::~UnixUser() +{ + if(mRevertOnDestruction) + { + // Revert to "real" user and group id of the process + if(::setegid(::getgid()) != 0 || ::seteuid(::getuid()) != 0) + { + THROW_EXCEPTION(CommonException, CouldNotRestoreProcessUser) + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::ChangeProcessUser(bool) +// Purpose: Change the process user and group ID to the user. If Temporary == true +// the process username will be changed back when the object is destructed. +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- +void UnixUser::ChangeProcessUser(bool Temporary) +{ + if(Temporary) + { + // Change temporarily (change effective only) + if(::setegid(mGID) != 0 || ::seteuid(mUID) != 0) + { + THROW_EXCEPTION(CommonException, CouldNotChangeProcessUser) + } + + // Mark for change on destruction + mRevertOnDestruction = true; + } + else + { + // Change permanently (change all UIDs and GIDs) + if(::setgid(mGID) != 0 || ::setuid(mUID) != 0) + { + THROW_EXCEPTION(CommonException, CouldNotChangeProcessUser) + } + } +} + + + + diff --git a/lib/common/UnixUser.h b/lib/common/UnixUser.h new file mode 100644 index 00000000..dafe6aa7 --- /dev/null +++ b/lib/common/UnixUser.h @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: UnixUser.h +// Purpose: Interface for managing the UNIX user of the current process +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef UNIXUSER__H +#define UNIXUSER__H + +class UnixUser +{ +public: + UnixUser(const std::string& Username); + UnixUser(uid_t UID, gid_t GID); + ~UnixUser(); +private: + // no copying allowed + UnixUser(const UnixUser &); + UnixUser &operator=(const UnixUser &); +public: + + void ChangeProcessUser(bool Temporary = false); + + uid_t GetUID() {return mUID;} + gid_t GetGID() {return mGID;} + +private: + uid_t mUID; + gid_t mGID; + bool mRevertOnDestruction; +}; + +#endif // UNIXUSER__H + diff --git a/lib/common/Utils.cpp b/lib/common/Utils.cpp new file mode 100644 index 00000000..0915f29a --- /dev/null +++ b/lib/common/Utils.cpp @@ -0,0 +1,383 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Utils.cpp +// Purpose: Utility function +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include + +#ifdef HAVE_EXECINFO_H + #include + #include +#endif + +#ifdef HAVE_CXXABI_H + #include +#endif + +#ifdef HAVE_DLFCN_H + #include +#endif + +#ifdef NEED_BOX_VERSION_H +# include "BoxVersion.h" +#endif + +#include "CommonException.h" +#include "Logging.h" +#include "Utils.h" + +#include "MemLeakFindOn.h" + +std::string GetBoxBackupVersion() +{ + return BOX_VERSION; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: SplitString(const std::string &, char, std::vector &) +// Purpose: Splits a string at a given character +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void SplitString(std::string String, char SplitOn, std::vector &rOutput) +{ + // Split it up. + std::string::size_type begin = 0, end = 0, pos = 0; + + while(end = String.find_first_of(SplitOn, pos), end != String.npos) + { + // 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 + { + // 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; + } + } + // Last string + if(begin < String.size()) + { + rOutput.push_back(String.substr(begin)); + } +/*#ifndef BOX_RELEASE_BUILD + BOX_TRACE("Splitting string '" << String << " on " << (char)SplitOn); + for(unsigned int l = 0; l < rOutput.size(); ++l) + { + BOX_TRACE(l << " = '" << rOutput[l] << "'"); + } +#endif*/ +} + +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); + + char* result = abi::__cxa_demangle(mangled_name.c_str(), + buffer, &length, &status); + + if (status == 0) + { + 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 + { + BOX_WARNING("Demangle failed with " + "with unknown error " << status << + ": " << mangled_name); + } + #endif // HAVE_CXXABI_H + + return demangled_name; +} + +void DumpStackBacktrace() +{ +#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++) + { + std::ostringstream output; + output << "Stack frame " << i << ": "; + + #ifdef HAVE_DLADDR + Dl_info info; + int result = dladdr(array[i], &info); + + if(result == 0) + { + BOX_LOG_SYS_WARNING("Failed to resolve " + "backtrace address " << array[i]); + output << "unresolved address " << array[i]; + } + else if(info.dli_sname == NULL) + { + output << "unknown address " << array[i]; + } + else + { + uint64_t diff = (uint64_t) array[i]; + diff -= (uint64_t) info.dli_saddr; + output << demangle(info.dli_sname) << "+" << + (void *)diff; + } + #else + output << "address " << array[i]; + #endif // HAVE_DLADDR + + BOX_TRACE(output.str()); + } +#else // !HAVE_EXECINFO_H + BOX_TRACE("Backtrace support was not compiled in"); +#endif // HAVE_EXECINFO_H +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileExists(const std::string& rFilename) +// Purpose: Does a file exist? +// Created: 20/11/03 +// +// -------------------------------------------------------------------------- +bool FileExists(const std::string& rFilename, int64_t *pFileSize, + bool TreatLinksAsNotExisting) +{ + EMU_STRUCT_STAT st; + if(EMU_LSTAT(rFilename.c_str(), &st) != 0) + { + if(errno == ENOENT) + { + return false; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError); + } + } + + // is it a file? + if((st.st_mode & S_IFDIR) == 0) + { + if(TreatLinksAsNotExisting && ((st.st_mode & S_IFLNK) != 0)) + { + return false; + } + + // Yes. Tell caller the size? + if(pFileSize != 0) + { + *pFileSize = st.st_size; + } + + return true; + } + else + { + return false; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ObjectExists(const std::string& rFilename) +// Purpose: Does a object exist, and if so, is it a file or a directory? +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +int ObjectExists(const std::string& rFilename) +{ + EMU_STRUCT_STAT st; + if(EMU_STAT(rFilename.c_str(), &st) != 0) + { + if(errno == ENOENT) + { + return ObjectExists_NoObject; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError); + } + } + + // is it a file or a dir? + return ((st.st_mode & S_IFDIR) == 0)?ObjectExists_File:ObjectExists_Dir; +} + +std::string HumanReadableSize(int64_t Bytes) +{ + double readableValue = Bytes; + std::string units = " B"; + + if (readableValue > 1024) + { + readableValue /= 1024; + units = "kB"; + } + + if (readableValue > 1024) + { + readableValue /= 1024; + units = "MB"; + } + + if (readableValue > 1024) + { + readableValue /= 1024; + units = "GB"; + } + + std::ostringstream result; + result << std::fixed << std::setprecision(2) << readableValue << + " " << units; + return result.str(); +} + +std::string FormatUsageBar(int64_t Blocks, int64_t Bytes, int64_t Max, + bool MachineReadable) +{ + std::ostringstream result; + + + if (MachineReadable) + { + result << (Bytes >> 10) << " kB, " << + std::setprecision(0) << ((Bytes*100)/Max) << "%"; + } + else + { + // Bar graph + char bar[17]; + unsigned int b = (int)((Bytes * (sizeof(bar)-1)) / Max); + if(b > sizeof(bar)-1) {b = sizeof(bar)-1;} + for(unsigned int l = 0; l < b; l++) + { + bar[l] = '*'; + } + for(unsigned int l = b; l < sizeof(bar) - 1; l++) + { + bar[l] = ' '; + } + bar[sizeof(bar)-1] = '\0'; + + result << std::fixed << + std::setw(10) << Blocks << " blocks, " << + std::setw(10) << HumanReadableSize(Bytes) << ", " << + std::setw(3) << std::setprecision(0) << + ((Bytes*100)/Max) << "% |" << bar << "|"; + } + + return result.str(); +} + +std::string FormatUsageLineStart(const std::string& rName, + bool MachineReadable) +{ + std::ostringstream result; + + if (MachineReadable) + { + result << rName << ": "; + } + else + { + result << std::setw(20) << std::right << rName << ": "; + } + + return result.str(); +} + diff --git a/lib/common/Utils.h b/lib/common/Utils.h new file mode 100644 index 00000000..d306ce1c --- /dev/null +++ b/lib/common/Utils.h @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Utils.h +// Purpose: Utility function +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef UTILS__H +#define UTILS__H + +#include +#include + +#include "MemLeakFindOn.h" + +std::string GetBoxBackupVersion(); + +void SplitString(std::string String, char SplitOn, std::vector &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); + +void DumpStackBacktrace(); + +bool FileExists(const std::string& rFilename, int64_t *pFileSize = 0, + bool TreatLinksAsNotExisting = false); + +enum +{ + ObjectExists_NoObject = 0, + ObjectExists_File = 1, + ObjectExists_Dir = 2 +}; +int ObjectExists(const std::string& rFilename); +std::string HumanReadableSize(int64_t Bytes); +std::string FormatUsageBar(int64_t Blocks, int64_t Bytes, int64_t Max, + bool MachineReadable); +std::string FormatUsageLineStart(const std::string& rName, + bool MachineReadable); + +std::string BoxGetTemporaryDirectoryName(); + +#include "MemLeakFindOff.h" + +#endif // UTILS__H diff --git a/lib/common/WaitForEvent.cpp b/lib/common/WaitForEvent.cpp new file mode 100644 index 00000000..5646bfbf --- /dev/null +++ b/lib/common/WaitForEvent.cpp @@ -0,0 +1,197 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: WaitForEvent.cpp +// Purpose: Generic waiting for events, using an efficient method (platform dependent) +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include +#include + +#include "WaitForEvent.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::WaitForEvent() +// Purpose: Constructor +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +#ifdef HAVE_KQUEUE +WaitForEvent::WaitForEvent(int Timeout) + : mKQueue(::kqueue()), + mpTimeout(0) +{ + if(mKQueue == -1) + { + THROW_EXCEPTION(CommonException, CouldNotCreateKQueue) + } + + // Set the choosen timeout + SetTimeout(Timeout); +} +#else +WaitForEvent::WaitForEvent(int Timeout) + : mTimeout(Timeout), + mpPollInfo(0) +{ +} +#endif + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::~WaitForEvent() +// Purpose: Destructor +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +WaitForEvent::~WaitForEvent() +{ +#ifdef HAVE_KQUEUE + ::close(mKQueue); + mKQueue = -1; +#else + if(mpPollInfo != 0) + { + ::free(mpPollInfo); + mpPollInfo = 0; + } +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::SetTimeout +// Purpose: Sets the timeout for future wait calls +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +void WaitForEvent::SetTimeout(int Timeout) +{ +#ifdef HAVE_KQUEUE + // Generate timeout + if(Timeout != TimeoutInfinite) + { + mTimeout.tv_sec = Timeout / 1000; + mTimeout.tv_nsec = (Timeout % 1000) * 1000000; + } + + // Infinite or not? + mpTimeout = (Timeout != TimeoutInfinite)?(&mTimeout):(NULL); +#else + mTimeout = Timeout; +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::Wait(int) +// Purpose: Wait for an event to take place. Returns a pointer to the object +// which has been signalled, or returns 0 for the timeout condition. +// Timeout specified in milliseconds. +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +void *WaitForEvent::Wait() +{ +#ifdef HAVE_KQUEUE + // Event return structure + struct kevent e; + ::memset(&e, 0, sizeof(e)); + + switch(::kevent(mKQueue, NULL, 0, &e, 1, mpTimeout)) + { + case 0: + // Timeout + return 0; + break; + + case 1: + // Event happened! + return e.udata; + break; + + default: + // Interrupted system calls aren't an error, just equivalent to a timeout + if(errno != EINTR) + { + THROW_EXCEPTION(CommonException, KEventErrorWait) + } + return 0; + break; + } +#else + // Use poll() instead. + // Need to build the structures? + if(mpPollInfo == 0) + { + // Yes... + mpPollInfo = (struct pollfd *)::malloc((sizeof(struct pollfd) * mItems.size()) + 4); + if(mpPollInfo == 0) + { + throw std::bad_alloc(); + } + + // Build... + for(unsigned int l = 0; l < mItems.size(); ++l) + { + mpPollInfo[l].fd = mItems[l].fd; + mpPollInfo[l].events = mItems[l].events; + mpPollInfo[l].revents = 0; + } + } + + // Make sure everything is reset (don't really have to do this, but don't trust the OS) + for(unsigned int l = 0; l < mItems.size(); ++l) + { + mpPollInfo[l].revents = 0; + } + + // Poll! + switch(::poll(mpPollInfo, mItems.size(), mTimeout)) + { + case -1: + // Interrupted system calls aren't an error, just equivalent to a timeout + if(errno != EINTR) + { + THROW_EXCEPTION(CommonException, KEventErrorWait) + } + return 0; + break; + case 0: // timed out + return 0; + break; + default: // got some thing... + // control flows on... + break; + } + + // Find the item which was ready + for(unsigned int s = 0; s < mItems.size(); ++s) + { + if(mpPollInfo[s].revents & POLLIN) + { + return mItems[s].item; + break; + } + } +#endif + + return 0; +} + diff --git a/lib/common/WaitForEvent.h b/lib/common/WaitForEvent.h new file mode 100644 index 00000000..a80761ef --- /dev/null +++ b/lib/common/WaitForEvent.h @@ -0,0 +1,152 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: WaitForEvent.h +// Purpose: Generic waiting for events, using an efficient method (platform dependent) +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- + +#ifndef WAITFOREVENT__H +#define WAITFOREVENT__H + +#include + +#ifdef HAVE_KQUEUE + #include + #include +#else + #include + #ifndef WIN32 + #include + #endif +#endif + +#include + +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +class WaitForEvent +{ +public: + WaitForEvent(int Timeout = TimeoutInfinite); + ~WaitForEvent(); +private: + // No copying. + WaitForEvent(const WaitForEvent &); + WaitForEvent &operator=(const WaitForEvent &); +public: + + enum + { + TimeoutInfinite = -1 + }; + + void SetTimeout(int Timeout = TimeoutInfinite); + + void *Wait(); + +#ifndef HAVE_KQUEUE + typedef struct + { + int fd; + short events; + void *item; + } ItemInfo; +#endif + + // -------------------------------------------------------------------------- + // + // Function + // Name: WaitForEvent::Add(const Type &, int) + // Purpose: Adds an event to the list of items to wait on. The flags are passed to the object. + // Created: 9/3/04 + // + // -------------------------------------------------------------------------- + template + void Add(const T *pItem, int Flags = 0) + { + ASSERT(pItem != 0); +#ifdef HAVE_KQUEUE + struct kevent e; + pItem->FillInKEvent(e, Flags); + // Fill in extra flags to say what to do + e.flags |= EV_ADD; + e.udata = (void*)pItem; + if(::kevent(mKQueue, &e, 1, NULL, 0, NULL) == -1) + { + THROW_EXCEPTION(CommonException, KEventErrorAdd) + } +#else + // Add item + ItemInfo i; + pItem->FillInPoll(i.fd, i.events, Flags); + i.item = (void*)pItem; + mItems.push_back(i); + // Delete any pre-prepared poll info, as it's now out of date + if(mpPollInfo != 0) + { + ::free(mpPollInfo); + mpPollInfo = 0; + } +#endif + } + + // -------------------------------------------------------------------------- + // + // Function + // Name: WaitForEvent::Remove(const Type &, int) + // Purpose: Removes an event from the list of items to wait on. The flags are passed to the object. + // Created: 9/3/04 + // + // -------------------------------------------------------------------------- + template + void Remove(const T *pItem, int Flags = 0) + { + ASSERT(pItem != 0); +#ifdef HAVE_KQUEUE + struct kevent e; + pItem->FillInKEvent(e, Flags); + // Fill in extra flags to say what to do + e.flags |= EV_DELETE; + e.udata = (void*)pItem; + if(::kevent(mKQueue, &e, 1, NULL, 0, NULL) == -1) + { + THROW_EXCEPTION(CommonException, KEventErrorRemove) + } +#else + if(mpPollInfo != 0) + { + ::free(mpPollInfo); + mpPollInfo = 0; + } + for(std::vector::iterator i(mItems.begin()); i != mItems.end(); ++i) + { + if(i->item == pItem) + { + mItems.erase(i); + return; + } + } +#endif + } + +private: +#ifdef HAVE_KQUEUE + int mKQueue; + struct timespec mTimeout; + struct timespec *mpTimeout; +#else + int mTimeout; + std::vector mItems; + struct pollfd *mpPollInfo; +#endif +}; + +#include "MemLeakFindOff.h" + +#endif // WAITFOREVENT__H + + diff --git a/lib/common/ZeroStream.cpp b/lib/common/ZeroStream.cpp new file mode 100644 index 00000000..e1342e6f --- /dev/null +++ b/lib/common/ZeroStream.cpp @@ -0,0 +1,170 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ZeroStream.cpp +// Purpose: An IOStream which returns all zeroes up to a certain size +// Created: 2007/04/28 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "ZeroStream.h" +#include "CommonException.h" + +#include + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::ZeroStream(IOStream::pos_type) +// Purpose: Constructor +// Created: 2007/04/28 +// +// -------------------------------------------------------------------------- +ZeroStream::ZeroStream(IOStream::pos_type size) +: mSize(size), mPosition(0) +{ } + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::Read(void *, int) +// Purpose: Reads bytes from the file +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +int ZeroStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + ASSERT(NBytes > 0); + + int bytesToRead = NBytes; + + if (bytesToRead > mSize - mPosition) + { + bytesToRead = mSize - mPosition; + } + + memset(pBuffer, 0, bytesToRead); + mPosition += bytesToRead; + + return bytesToRead; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::BytesLeftToRead() +// Purpose: Returns number of bytes to read (may not be most efficient function ever) +// Created: 2007/01/16 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ZeroStream::BytesLeftToRead() +{ + return mSize - mPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::Write(void *, int) +// Purpose: Writes bytes to the underlying stream (not supported) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void ZeroStream::Write(const void *pBuffer, int NBytes, int Timeout) +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::GetPosition() +// Purpose: Get position in stream +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ZeroStream::GetPosition() const +{ + return mPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::Seek(pos_type, int) +// Purpose: Seeks within file, as lseek, invalidate buffer +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void ZeroStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + switch (SeekType) + { + case SeekType_Absolute: + { + mPosition = Offset; + } + break; + + case SeekType_Relative: + { + mPosition += Offset; + } + break; + + case SeekType_End: + { + mPosition = mSize - Offset; + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::Close() +// Purpose: Closes the underlying stream (not needed) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void ZeroStream::Close() +{ + THROW_EXCEPTION(CommonException, NotSupported); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::StreamDataLeft() +// Purpose: Any data left to write? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool ZeroStream::StreamDataLeft() +{ + return (BytesLeftToRead() > 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ZeroStream::StreamClosed() +// Purpose: Is the stream closed? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool ZeroStream::StreamClosed() +{ + return false; +} + diff --git a/lib/common/ZeroStream.h b/lib/common/ZeroStream.h new file mode 100644 index 00000000..f91221b0 --- /dev/null +++ b/lib/common/ZeroStream.h @@ -0,0 +1,40 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ZeroStream.h +// Purpose: An IOStream which returns all zeroes up to a certain size +// Created: 2007/04/28 +// +// -------------------------------------------------------------------------- + +#ifndef ZEROSTREAM__H +#define ZEROSTREAM__H + +#include "IOStream.h" + +class ZeroStream : public IOStream +{ +private: + IOStream::pos_type mSize, mPosition; + +public: + ZeroStream(IOStream::pos_type mSize); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + 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(); + + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + ZeroStream(const ZeroStream &rToCopy); +}; + +#endif // ZEROSTREAM__H + + diff --git a/lib/common/makeexception.pl.in b/lib/common/makeexception.pl.in new file mode 100755 index 00000000..bddaa94a --- /dev/null +++ b/lib/common/makeexception.pl.in @@ -0,0 +1,250 @@ +#!@PERL@ + +# global exception list file +my $global_list = '../../ExceptionCodes.txt'; + + +my @exception; +my @exception_desc; +my $class; +my $class_number; + +# read the description! + +open EXCEPTION_DESC,$ARGV[0] or die "Can't open $ARGV[0]"; + +while() +{ + chomp; s/\A\s+//; s/#.+\Z//; s/\s+\Z//; s/\s+/ /g; + next unless m/\S/; + + if(m/\AEXCEPTION\s+(.+)\s+(\d+)\Z/) + { + $class = $1; + $class_number = $2; + } + else + { + my ($name,$number,$description) = split /\s+/,$_,3; + if($name eq '' || $number =~ m/\D/) + { + die "Bad line '$_'"; + } + if($exception[$number] ne '') + { + die "Duplicate exception number $number"; + } + $exception[$number] = $name; + $exception_desc[$number] = $description; + } +} + +die "Exception class and number not specified" unless $class ne '' && $class_number ne ''; + +close EXCEPTION_DESC; + +# write the code +print "Generating $class exception...\n"; + +open CPP,">autogen_${class}Exception.cpp" or die "Can't open cpp file for writing"; +open H,">autogen_${class}Exception.h" or die "Can't open h file for writing"; + +# write header file +my $guardname = uc 'AUTOGEN_'.$class.'EXCEPTION_H'; +print H <<__E; + +// Auto-generated file -- do not edit + +#ifndef $guardname +#define $guardname + +#include "BoxException.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: ${class}Exception +// Purpose: Exception +// Created: autogen +// +// -------------------------------------------------------------------------- +class ${class}Exception : public BoxException +{ +public: + ${class}Exception(unsigned int SubType, + const std::string& 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), mWhat(rToCopy.mWhat) + { + } + + ~${class}Exception() throw () + { + } + + enum + { + ExceptionType = $class_number + }; + + enum + { +__E + +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + print H "\t\t".$exception[$e].' = '.$e.(($e==$#exception)?'':',')."\n" + } +} + +print H <<__E; + }; + + virtual unsigned int GetType() const throw(); + virtual unsigned int GetSubType() const throw(); + virtual const char *what() const throw(); + virtual const std::string& GetMessage() const + { + return mMessage; + } + static const char* GetMessage(int SubType); +private: + unsigned int mSubType; + std::string mMessage; + std::string mWhat; +}; + +#endif // $guardname +__E + +# ----------------------------------------------------------------------------------------------------------- + +print CPP <<__E; + +// Auto-generated file -- do not edit + +#include "Box.h" +#include "autogen_${class}Exception.h" + +#include "MemLeakFindOn.h" + +unsigned int ${class}Exception::GetType() const throw() +{ + return ${class}Exception::ExceptionType; +} + +unsigned int ${class}Exception::GetSubType() const throw() +{ + return mSubType; +} + +const char * ${class}Exception::what() const throw() +{ + return mWhat.c_str(); +} + +const char * ${class}Exception::GetMessage(int SubType) +{ + switch(SubType) + { +__E + +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + print CPP "\t\tcase ".$exception[$e].': return "'.$exception[$e].'";'."\n"; + } +} + +print CPP <<__E; + default: return "Unknown"; + } +} +__E + +close H; +close CPP; + +# update the global exception list +my $list_before; +my $list_after; +my $is_after = 0; +if(open CURRENT,$global_list) +{ + while() + { + next if m/\A#/; + + if(m/\AEXCEPTION TYPE (\w+) (\d+)/) + { + # check that the number isn't being reused + if($2 == $class_number && $1 ne $class) + { + die "Class number $class_number is being used by $class and $1 -- correct this.\n"; + } + if($2 > $class_number) + { + # This class comes after the current one (ensures numerical ordering) + $is_after = 1; + } + if($1 eq $class) + { + # skip this entry + while() + { + last if m/\AEND TYPE/; + } + $_ = ''; + } + } + + if($is_after) + { + $list_after .= $_; + } + else + { + $list_before .= $_; + } + } + + close CURRENT; +} + +open GLOBAL,">$global_list" or die "Can't open global exception code listing for writing"; + +print GLOBAL <<__E; +# +# automatically generated file, do not edit. +# +# This file lists all the exception codes used by the system. +# Use to look up more detailed descriptions of meanings of errors. +# +__E + +print GLOBAL $list_before; + +print GLOBAL "EXCEPTION TYPE $class $class_number\n"; +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + my $ext = ($exception_desc[$e] ne '')?" - $exception_desc[$e]":''; + print GLOBAL "($class_number/$e) - ${class} ".$exception[$e].$ext."\n"; + } +} +print GLOBAL "END TYPE\n"; + +print GLOBAL $list_after; + +close GLOBAL; + + -- cgit v1.2.3 From fc8dc54df85dbcdc7acd94394bff7ba5087ca5ff Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Tue, 31 Mar 2009 21:43:58 +0200 Subject: change default syslog facility from LOG_LOCAL6 to LOG_DAEMON Gbp-Pq: Name 03-adjust-syslog-facility.diff --- lib/common/Logging.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/common') diff --git a/lib/common/Logging.cpp b/lib/common/Logging.cpp index 0928a4d4..1cff1762 100644 --- a/lib/common/Logging.cpp +++ b/lib/common/Logging.cpp @@ -411,7 +411,7 @@ bool Syslog::Log(Log::Level level, const std::string& file, int line, return true; } -Syslog::Syslog() : mFacility(LOG_LOCAL6) +Syslog::Syslog() : mFacility(LOG_DAEMON) { ::openlog("Box Backup", LOG_PID, mFacility); } @@ -454,8 +454,8 @@ int Syslog::GetNamedFacility(const std::string& rFacility) #undef CASE_RETURN BOX_ERROR("Unknown log facility '" << rFacility << "', " - "using default LOCAL6"); - return LOG_LOCAL6; + "using default DAEMON"); + return LOG_DAEMON; } bool FileLogger::Log(Log::Level Level, const std::string& file, int line, -- cgit v1.2.3 From 8957b5e255318e1319daf73538477c710bce0047 Mon Sep 17 00:00:00 2001 From: Simon Chopin Date: Wed, 5 Jan 2022 08:24:10 -0500 Subject: Mark the comparator object as const callabled This fixes the build when using C++17 Gbp-Pq: Name c++17.diff --- lib/common/DebugMemLeakFinder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/common') diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp index 58a82c0e..dd42173d 100644 --- a/lib/common/DebugMemLeakFinder.cpp +++ b/lib/common/DebugMemLeakFinder.cpp @@ -703,7 +703,7 @@ void *operator new(size_t size) } */ -void *operator new[](size_t size) throw (std::bad_alloc) +void *operator new[](size_t size) noexcept(false) { return internal_new(size, "standard libraries", 0); } @@ -717,12 +717,12 @@ void internal_delete(void *ptr) //TRACE1("delete[]() called, %08x\n", ptr); } -void operator delete[](void *ptr) throw () +void operator delete[](void *ptr) noexcept { internal_delete(ptr); } -void operator delete(void *ptr) throw () +void operator delete(void *ptr) noexcept { internal_delete(ptr); } -- cgit v1.2.3