diff options
Diffstat (limited to 'lib/common')
84 files changed, 12306 insertions, 0 deletions
diff --git a/lib/common/Archive.h b/lib/common/Archive.h new file mode 100644 index 00000000..b70f12c4 --- /dev/null +++ b/lib/common/Archive.h @@ -0,0 +1,161 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Archive.h +// Purpose: Backup daemon state archive +// Created: 2005/04/11 +// +// -------------------------------------------------------------------------- + +#ifndef ARCHIVE__H +#define ARCHIVE__H + +#include <vector> +#include <string> +#include <memory> + +#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 Write(int Item) + { + int32_t privItem = htonl(Item); + mrStream.Write(&privItem, sizeof(privItem)); + } + void Write(int64_t Item) + { + int64_t privItem = box_hton64(Item); + mrStream.Write(&privItem, sizeof(privItem)); + } + void Write(uint64_t Item) + { + uint64_t privItem = box_hton64(Item); + mrStream.Write(&privItem, sizeof(privItem)); + } + 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); + } + // + // + // + void Read(bool &rItemOut) + { + int privItem; + Read(privItem); + + if (privItem) + { + rItemOut = true; + } + else + { + rItemOut = false; + } + } + void Read(int &rItemOut) + { + int32_t privItem; + if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */)) + { + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + } + rItemOut = ntohl(privItem); + } + void Read(int64_t &rItemOut) + { + int64_t privItem; + if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */)) + { + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + } + rItemOut = box_ntoh64(privItem); + } + void Read(uint64_t &rItemOut) + { + uint64_t privItem; + if(!mrStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */)) + { + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + } + rItemOut = box_ntoh64(privItem); + } + void Read(uint8_t &rItemOut) + { + int privItem; + Read(privItem); + rItemOut = privItem; + } + void Read(std::string &rItemOut) + { + int size; + Read(size); + + // Assume most strings are relatively small + char buf[256]; + if(size < (int) sizeof(buf)) + { + // Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us + if(!mrStream.ReadFullBuffer(buf, size, 0 /* not interested in bytes read if this fails */, mTimeout)) + { + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + } + // assign to this string, storing the header and the extra payload + rItemOut.assign(buf, size); + } + else + { + // Block of memory to hold it + MemoryBlockGuard<char*> dataB(size); + char *ppayload = dataB; + + // Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us + if(!mrStream.ReadFullBuffer(ppayload, size, 0 /* not interested in bytes read if this fails */, mTimeout)) + { + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + } + // 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.h b/lib/common/BannerText.h new file mode 100644 index 00000000..e40224da --- /dev/null +++ b/lib/common/BannerText.h @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BannerText.h +// Purpose: Banner text for daemons and utilities +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef BANNERTEXT__H +#define BANNERTEXT__H + +#define BANNER_TEXT(UtilityName) \ + "Box " UtilityName " v" BOX_VERSION ", (c) Ben Summers and " \ + "contributors 2003-2010" + +#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..158fab7b --- /dev/null +++ b/lib/common/Box.h @@ -0,0 +1,185 @@ +// -------------------------------------------------------------------------- +// +// 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" + +// 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 + + 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 + #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 + +#endif + +#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()) \ + { \ + 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()) \ + { \ + OPTIONAL_DO_BACKTRACE \ + BOX_WARNING("Exception thrown: " \ + #type "(" #subtype ") (" << message << \ + ") 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 <netinet/in.h> +#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 <sys/endian.h> + #endif + #ifdef HAVE_ASM_BYTEORDER_H + #include <asm/byteorder.h> + #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..bb3ffb30 --- /dev/null +++ b/lib/common/BoxConfig-MSVC.h @@ -0,0 +1,402 @@ +/* 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 <asm/byteorder.h> 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 <db.h> 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 <dirent.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_DIRENT_H */ + +/* Define to 1 if you have the <editline/readline.h> header file. */ +/* #undef HAVE_EDITLINE_READLINE_H */ + +/* define if the compiler supports exceptions */ +#define HAVE_EXCEPTIONS + +/* Define to 1 if you have the <execinfo.h> header file. */ +/* #undef HAVE_EXECINFO_H */ + +/* 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 <history.h> header file. */ +/* #undef HAVE_HISTORY_H */ + +/* Define to 1 if you have the <inttypes.h> 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 <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <mntent.h> 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 <ndir.h> header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the <netinet/in.h> 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 <openssl/ssl.h> header file. */ +#define HAVE_OPENSSL_SSL_H 1 + +/* Define to 1 if you have the <process.h> header file. */ +#define HAVE_PROCESS_H 1 + +/* Define to 1 if you have the <pwd.h> 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 <readline.h> 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 <readline/history.h> header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the <readline/readline.h> header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the <regex.h> 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 to 1 if you have the `setxattr' function. */ +/* #undef HAVE_SETXATTR */ + +/* Define to 1 if you have the <signal.h> 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 <stdint.h> header file. */ +// #define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> 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 <syslog.h> header file. */ +/* #undef HAVE_SYSLOG_H */ + +/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the <sys/endian.h> header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the <sys/mnttab.h> header file. */ +/* #undef HAVE_SYS_MNTTAB_H */ + +/* Define to 1 if you have the <sys/mount.h> header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the <sys/param.h> header file. */ +// #define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/syscall.h> header file. */ +/* #undef HAVE_SYS_SYSCALL_H */ + +/* Define to 1 if you have the <sys/time.h> header file. */ +// #define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +// #define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/wait.h> header file. */ +/* #undef HAVE_SYS_WAIT_H */ + +/* Define to 1 if you have the <sys/xattr.h> header file. */ +/* #undef HAVE_SYS_XATTR_H */ + +/* Define to 1 if you have the <time.h> 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 <unistd.h> 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 <sys/time.h> and <time.h>. */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to 1 if your <sys/time.h> 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 <sys/types.h> doesn't define. */ +#define gid_t int + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef mode_t */ + +/* Define to `long' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +/* #undef size_t */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +#define uid_t int diff --git a/lib/common/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..a8f5d7a6 --- /dev/null +++ b/lib/common/BoxException.h @@ -0,0 +1,38 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxException.h +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- + +#ifndef BOXEXCEPTION__H +#define BOXEXCEPTION__H + +#include <exception> +#include <string> + +// -------------------------------------------------------------------------- +// +// 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; + +private: +}; + + +#endif // BOXEXCEPTION__H + diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h new file mode 100644 index 00000000..617aa031 --- /dev/null +++ b/lib/common/BoxPlatform.h @@ -0,0 +1,201 @@ +// -------------------------------------------------------------------------- +// +// 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" + +#ifdef _MSC_VER +#include "BoxConfig-MSVC.h" +#include "BoxVersion.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 <sys/types.h> + #define __MSVCRT_VERSION__ 0x0601 + #endif +#endif + +#ifdef HAVE_SYS_TYPES_H + #include <sys/types.h> +#endif +#ifdef HAVE_INTTYPES_H + #include <inttypes.h> +#else + #ifdef HAVE_STDINT_H + #include <stdint.h> + #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 +#ifdef HAVE_SYS_XATTR_H + #if !defined(HAVE_LLISTXATTR) && defined(HAVE_LISTXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define llistxattr(a,b,c) listxattr(a,b,c,XATTR_NOFOLLOW) + #endif + #if !defined(HAVE_LGETXATTR) && defined(HAVE_GETXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define lgetxattr(a,b,c,d) getxattr(a,b,c,d,0,XATTR_NOFOLLOW) + #endif + #if !defined(HAVE_LSETXATTR) && defined(HAVE_SETXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define lsetxattr(a,b,c,d,e) setxattr(a,b,c,d,0,(e)|XATTR_NOFOLLOW) + #endif +#endif + +#if defined WIN32 && !defined __MINGW32__ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + + typedef unsigned __int8 u_int8_t; + typedef unsigned __int16 u_int16_t; + typedef unsigned __int32 u_int32_t; + typedef unsigned __int64 u_int64_t; + + #define HAVE_U_INT8_T + #define HAVE_U_INT16_T + #define HAVE_U_INT32_T + #define HAVE_U_INT64_T +#endif // WIN32 && !__MINGW32__ + +// Define missing types +#ifndef HAVE_UINT8_T + typedef u_int8_t uint8_t; +#endif + +#ifndef HAVE_UINT16_T + typedef u_int16_t uint16_t; +#endif + +#ifndef HAVE_UINT32_T + typedef u_int32_t uint32_t; +#endif + +#ifndef HAVE_UINT64_T + typedef u_int64_t uint64_t; +#endif + +#ifndef HAVE_U_INT8_T + typedef uint8_t u_int8_t; +#endif + +#ifndef HAVE_U_INT16_T + typedef uint16_t u_int16_t; +#endif + +#ifndef HAVE_U_INT32_T + typedef uint32_t u_int32_t; +#endif + +#ifndef HAVE_U_INT64_T + typedef uint64_t u_int64_t; +#endif + +#if !HAVE_DECL_INFTIM + #define INFTIM -1 +#endif + +// for Unix compatibility with Windows :-) +#ifndef O_BINARY + #define O_BINARY 0 +#endif + +#ifdef WIN32 + typedef u_int64_t InodeRefType; +#else + typedef ino_t InodeRefType; +#endif + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN +#endif + +#include "emu.h" + +#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..41bad0ba --- /dev/null +++ b/lib/common/BoxPortsAndFiles.h.in @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------- +// +// 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" + +// 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_DEFAULT_CONFIG "@sysconfdir_expanded@/boxbackup/bbackupd.conf" +#define BOX_FILE_RAIDFILE_DEFAULT_CONFIG "@sysconfdir_expanded@/boxbackup/raidfile.conf" +#define BOX_FILE_BBSTORED_DEFAULT_CONFIG "@sysconfdir_expanded@/boxbackup/bbstored.conf" +#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" +#endif + +#endif // BOXPORTSANDFILES__H + diff --git a/lib/common/BoxTime.cpp b/lib/common/BoxTime.cpp new file mode 100644 index 00000000..d05c0a6c --- /dev/null +++ b/lib/common/BoxTime.cpp @@ -0,0 +1,96 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTime.cpp +// Purpose: Time for the box +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_SYS_TIME_H + #include <sys/time.h> +#endif + +#ifdef HAVE_TIME_H + #include <time.h> +#endif + +#include <errno.h> +#include <string.h> + +#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 timeNow = (tv.tv_sec * MICRO_SEC_IN_SEC_LL) + + tv.tv_usec; + return timeNow; + } + #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(6) << micros; + } + } + else + { + buf << strerror(errno); + } + + return buf.str(); +} + diff --git a/lib/common/BoxTime.h b/lib/common/BoxTime.h new file mode 100644 index 00000000..6681bbbd --- /dev/null +++ b/lib/common/BoxTime.h @@ -0,0 +1,46 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTime.h +// Purpose: How time is represented +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#ifndef BOXTIME__H +#define BOXTIME__H + +// Time is presented as an unsigned 64 bit integer, in microseconds +typedef uint64_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 MILLI_SEC_IN_NANO_SEC (1000) +#define MILLI_SEC_IN_NANO_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 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_NANO_SEC_LL; +} +inline uint64_t BoxTimeToMicroSeconds(box_time_t Time) +{ + return Time; +} + +std::string FormatTime(box_time_t time, bool includeDate, + bool showMicros = false); + +#endif // BOXTIME__H diff --git a/lib/common/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 <sys/types.h> +#include <time.h> +#include <stdio.h> + +#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 <string> +#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 <time.h> +#else +#include <sys/time.h> +#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..b58253f3 --- /dev/null +++ b/lib/common/BufferedStream.cpp @@ -0,0 +1,207 @@ +// -------------------------------------------------------------------------- +// +// 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 <string.h> + +#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) +{ + 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 mrSource.StreamDataLeft(); +} + +// -------------------------------------------------------------------------- +// +// 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..079c482a --- /dev/null +++ b/lib/common/BufferedStream.h @@ -0,0 +1,43 @@ +// -------------------------------------------------------------------------- +// +// 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); + virtual pos_type GetPosition() const; + virtual void Seek(IOStream::pos_type Offset, int SeekType); + virtual void Close(); + + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +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..797be00d --- /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 <string.h> + +#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 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..7a1c8c17 --- /dev/null +++ b/lib/common/BufferedWriteStream.h @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------- +// +// 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); + 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..90e2e7bc --- /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 <string.h> + +#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) +{ + 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..d73af8db --- /dev/null +++ b/lib/common/CollectInBufferStream.h @@ -0,0 +1,60 @@ +// -------------------------------------------------------------------------- +// +// 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(); +private: + // No copying + CollectInBufferStream(const CollectInBufferStream &); + CollectInBufferStream(const IOStream &); +public: + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes); + virtual 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<char*> 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..b2819886 --- /dev/null +++ b/lib/common/CommonException.txt @@ -0,0 +1,47 @@ + +# 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. diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp new file mode 100644 index 00000000..f49f3c6e --- /dev/null +++ b/lib/common/Configuration.cpp @@ -0,0 +1,920 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Configuration.cpp +// Purpose: Reading configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <sstream> + +#include "Configuration.h" +#include "CommonException.h" +#include "Guards.h" +#include "FdGetLine.h" + +#include "MemLeakFindOn.h" + +#include <cstring> + +// 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}; + +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> Configuration::LoadAndVerify( + const std::string& rFilename, + const ConfigurationVerify *pVerify, + std::string &rErrorMsg) +{ + // Just to make sure + rErrorMsg.erase(); + + // Open the file + FileHandleGuard<O_RDONLY> file(rFilename); + + // GetLine object + FdGetLine getline(file); + + // Object to create + std::auto_ptr<Configuration> apConfig( + new Configuration(std::string("<root>"))); + + try + { + // Load + LoadInto(*apConfig, getline, rErrorMsg, true); + + if(!rErrorMsg.empty()) + { + // An error occured, return now + BOX_ERROR("Error in Configuration::LoadInto: " << + rErrorMsg); + return std::auto_ptr<Configuration>(0); + } + + // Verify? + if(pVerify) + { + if(!apConfig->Verify(*pVerify, std::string(), rErrorMsg)) + { + BOX_ERROR("Error verifying configuration: " << + rErrorMsg); + return std::auto_ptr<Configuration>(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<std::string, Configuration>(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<std::string, std::string>::const_iterator i(mKeys.find(rKeyName)); + + if(i == mKeys.end()) + { + BOX_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<std::string, std::string>::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; + } +} + + +// -------------------------------------------------------------------------- +// +// 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<std::string, std::string>::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<std::string, std::string>::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<std::string> Configuration::GetKeyNames() const +{ + std::map<std::string, std::string>::const_iterator i(mKeys.begin()); + + std::vector<std::string> 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<std::pair<std::string, Configuration> >::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<std::pair<std::string, Configuration> >::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<std::string> Configuration::GetSubConfigurationNames() const +{ + std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin()); + + std::vector<std::string> 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<std::string, std::string>::const_iterator i = mKeys.begin(); + i != mKeys.end(); ++i) + { + // Is the name in the list? + const ConfigurationVerifyKey *scan = rVerify.mpKeys; + bool found = false; + while(scan) + { + if(scan->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..4828b315 --- /dev/null +++ b/lib/common/Configuration.h @@ -0,0 +1,147 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Configuration +// Purpose: Reading configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- + +#ifndef CONFIGURATION__H +#define CONFIGURATION__H + +#include <map> +#include <list> +#include <vector> +#include <string> +#include <memory> + +// For defining tests +enum +{ + ConfigTest_LastEntry = 1, + ConfigTest_Exists = 2, + ConfigTest_IsInt = 4, + ConfigTest_IsUint32 = 8, + ConfigTest_MultiValueAllowed = 16, + ConfigTest_IsBool = 32 +}; + +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 +}; + +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<Configuration> LoadAndVerify( + const std::string& rFilename, + const ConfigurationVerify *pVerify, + std::string &rErrorMsg); + + static std::auto_ptr<Configuration> 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; + uint32_t GetKeyValueUint32(const std::string& rKeyName) const; + bool GetKeyValueBool(const std::string& rKeyName) const; + std::vector<std::string> 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<std::string> GetSubConfigurationNames() const; + + void AddKeyValue(const std::string& rKey, const std::string& rValue); + void AddSubConfig(const std::string& rName, const Configuration& rSubConfig); + + bool Verify(const ConfigurationVerify &rVerify, std::string &rErrorMsg) + { + return Verify(rVerify, std::string(), rErrorMsg); + } + +private: + std::string mName; + // Order of keys not preserved + std::map<std::string, std::string> mKeys; + // Order of sub blocks preserved + typedef std::list<std::pair<std::string, Configuration> > SubConfigListType; + SubConfigListType mSubConfigurations; + + 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 <string> + +namespace BoxConvert +{ + // -------------------------------------------------------------------------- + // + // Function + // Name: BoxConvert::Convert<to_type, from_type>(to_type &, from_type) + // Purpose: Convert from types to types + // Created: 9/4/04 + // + // -------------------------------------------------------------------------- + template<typename to_type, typename from_type> + 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<int32_t, const std::string &>(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 32); + } + template<> + inline int16_t Convert<int16_t, const std::string &>(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 16); + } + template<> + inline int8_t Convert<int8_t, const std::string &>(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 8); + } + template<> + inline int32_t Convert<int32_t, const char *>(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 32); + } + template<> + inline int16_t Convert<int16_t, const char *>(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 16); + } + template<> + inline int8_t Convert<int8_t, const char *>(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<std::string, int32_t>(int32_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + template<> + inline std::string Convert<std::string, int16_t>(int16_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + template<> + inline std::string Convert<std::string, int8_t>(int8_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + + // Specialise for bool -> string + template<> + inline std::string Convert<std::string, bool>(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 <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> + +#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/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 <stdio.h> + +#ifdef WIN32 + #include "emu.h" +#else + #include <syslog.h> +#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..72891cd1 --- /dev/null +++ b/lib/common/DebugMemLeakFinder.cpp @@ -0,0 +1,552 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFinder.cpp +// Purpose: Memory leak finder +// Created: 12/1/04 +// +// -------------------------------------------------------------------------- + + +#ifndef BOX_RELEASE_BUILD + +#include "Box.h" + +#undef malloc +#undef realloc +#undef free + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include <map> +#include <stdio.h> +#include <string.h> +#include <set> +#include <cstdlib> // for std::atexit + +#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<void *, MallocBlockInfo> sMallocBlocks; + static std::map<void *, ObjectInfo> sObjectBlocks; + static bool sTrackingDataDestroyed = false; + + static class DestructionWatchdog + { + public: + ~DestructionWatchdog() + { + sTrackingDataDestroyed = true; + } + } + sWatchdog; + + static bool sTrackMallocInSection = false; + static std::set<void *> sSectionMallocBlocks; + static bool sTrackObjectsInSection = false; + static std::map<void *, ObjectInfo> sSectionObjectBlocks; + + static std::set<void *> sNotLeaks; + + void *sNotLeaksPre[1024]; + size_t sNotLeaksPreNum = 0; +} + +void memleakfinder_init() +{ + ASSERT(!memleakfinder_initialised); + + { + // allocates a permanent buffer on Solaris. + // not a leak? + std::ostringstream oss; + } + + memleakfinder_initialised = true; +} + +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) 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_realloc(void *ptr, size_t size) +{ + InternalAllocGuard guard; + + if(!memleakfinder_global_enable || !memleakfinder_initialised) + { + return std::realloc(ptr, size); + } + + // Check it's been allocated + std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); + if(ptr && i == sMallocBlocks.end()) + { + BOX_WARNING("Block " << ptr << " realloc()ated, but not " + "in list. Error? Or allocated in startup static " + "objects?"); + } + + void *b = std::realloc(ptr, size); + + if(ptr && 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<void *>::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<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); + if(i != sMallocBlocks.end()) + { + sMallocBlocks.erase(i); + } + else + { + BOX_WARNING("Block " << ptr << " freed, but not " + "known. Error? Or allocated in startup " + "static allocation?"); + } + + if(sTrackMallocInSection) + { + std::set<void *>::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_global_enable && memleakfinder_initialised) + { + sNotLeaks.insert(ptr); + } + else + { + if ( sNotLeaksPreNum < + sizeof(sNotLeaksPre)/sizeof(*sNotLeaksPre) ) + sNotLeaksPre[sNotLeaksPreNum++] = ptr; + } +/* { + std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); + if(i != sMallocBlocks.end()) sMallocBlocks.erase(i); + } + { + std::set<void *>::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + } + { + std::map<void *, ObjectInfo>::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<void *>::iterator s(sSectionMallocBlocks.begin()); + for(; s != sSectionMallocBlocks.end(); ++s) + { + std::map<void *, MallocBlockInfo>::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<void *, ObjectInfo>::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<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) + { + if(is_leak(i->first)) ++n; + } + + for(std::map<void *, ObjectInfo>::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) + { + const ObjectInfo& rInfo = i->second; + if(is_leak(i->first)) ++n; + } + + return n; +} + +void memleakfinder_reportleaks_file(FILE *file) +{ + InternalAllocGuard guard; + + ASSERT(!sTrackingDataDestroyed); + + for(std::map<void *, MallocBlockInfo>::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<void *, ObjectInfo>::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 char *filename, const char *markertext) +{ + ::strncpy(atexit_filename, filename, sizeof(atexit_filename)-1); + ::strncpy(atexit_markertext, markertext, sizeof(atexit_markertext)-1); + atexit_filename[sizeof(atexit_filename)-1] = 0; + atexit_markertext[sizeof(atexit_markertext)-1] = 0; + if(!atexit_registered) + { + 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) + { + 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<void *, ObjectInfo>::iterator i(sObjectBlocks.find(block)); + if(i != sObjectBlocks.end()) + { + sObjectBlocks.erase(i); + } + + if(sTrackObjectsInSection) + { + std::map<void *, ObjectInfo>::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) +{ + 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_RELEASE_BUILD 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 <stdio.h> +#include <stdarg.h> + +#ifdef WIN32 + #include "emu.h" +#else + #include <syslog.h> +#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/EventWatchFilesystemObject.cpp b/lib/common/EventWatchFilesystemObject.cpp new file mode 100644 index 00000000..43533fc8 --- /dev/null +++ b/lib/common/EventWatchFilesystemObject.cpp @@ -0,0 +1,112 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: EventWatchFilesystemObject.cpp +// Purpose: WaitForEvent compatible object for watching directories +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <errno.h> +#include <fcntl.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include "EventWatchFilesystemObject.h" +#include "autogen_CommonException.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + + +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::EventWatchFilesystemObject +// (const char *) +// Purpose: Constructor -- opens the file object +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename) +#ifdef HAVE_KQUEUE + : mDescriptor(::open(Filename, O_RDONLY /*O_EVTONLY*/, 0)) +#endif +{ +#ifdef HAVE_KQUEUE + if(mDescriptor == -1) + { + BOX_LOG_SYS_ERROR("EventWatchFilesystemObject: " + "Failed to open file '" << Filename << "'"); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +#else + THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform) +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::~EventWatchFilesystemObject() +// Purpose: Destructor +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +EventWatchFilesystemObject::~EventWatchFilesystemObject() +{ + if(mDescriptor != -1) + { + ::close(mDescriptor); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::EventWatchFilesystemObject +// (const EventWatchFilesystemObject &) +// Purpose: Copy constructor +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +EventWatchFilesystemObject::EventWatchFilesystemObject( + const EventWatchFilesystemObject &rToCopy) + : mDescriptor(::dup(rToCopy.mDescriptor)) +{ + if(mDescriptor == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } +} + + +#ifdef HAVE_KQUEUE +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::FillInKEvent(struct kevent &, int) +// Purpose: For WaitForEvent +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +void EventWatchFilesystemObject::FillInKEvent(struct kevent &rEvent, + int Flags) const +{ + EV_SET(&rEvent, mDescriptor, EVFILT_VNODE, EV_CLEAR, + NOTE_DELETE | NOTE_WRITE, 0, (void*)this); +} +#else +void EventWatchFilesystemObject::FillInPoll(int &fd, short &events, + int Flags) const +{ + THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform) +} +#endif + diff --git a/lib/common/EventWatchFilesystemObject.h b/lib/common/EventWatchFilesystemObject.h new file mode 100644 index 00000000..f9175a49 --- /dev/null +++ b/lib/common/EventWatchFilesystemObject.h @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: EventWatchFilesystemObject.h +// Purpose: WaitForEvent compatible object for watching directories +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- + +#ifndef EVENTWATCHFILESYSTEMOBJECT__H +#define EVENTWATCHFILESYSTEMOBJECT__H + +#ifdef HAVE_KQUEUE + #include <sys/event.h> +#endif + + +// -------------------------------------------------------------------------- +// +// Class +// Name: EventWatchFilesystemObject +// Purpose: WaitForEvent compatible object for watching files and directories +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +class EventWatchFilesystemObject +{ +public: + EventWatchFilesystemObject(const char *Filename); + ~EventWatchFilesystemObject(); + EventWatchFilesystemObject(const EventWatchFilesystemObject &rToCopy); +private: + // Assignment not allowed + EventWatchFilesystemObject &operator=(const EventWatchFilesystemObject &); +public: + +#ifdef HAVE_KQUEUE + void FillInKEvent(struct kevent &rEvent, int Flags = 0) const; +#else + void FillInPoll(int &fd, short &events, int Flags = 0) const; +#endif + +private: + int mDescriptor; +}; + +#endif // EventWatchFilesystemObject__H + diff --git a/lib/common/ExcludeList.cpp b/lib/common/ExcludeList.cpp new file mode 100644 index 00000000..edbf1a6a --- /dev/null +++ b/lib/common/ExcludeList.cpp @@ -0,0 +1,481 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ExcludeList.cpp +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_REGEX_SUPPORT + #ifdef HAVE_PCREPOSIX_H + #include <pcreposix.h> + #else + #include <regex.h> + #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); + } + + for (std::string::iterator i = output.begin(); i != output.end(); i++) + { + *i = tolower(*i); + } + + 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<std::string> ens; + SplitString(rEntries, Configuration::MultiValueSeparator, ens); + + // Add to set of excluded strings + for(std::vector<std::string>::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_WARNING("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<std::string> ens; + SplitString(rEntries, Configuration::MultiValueSeparator, ens); + + // Create and add new regular expressions + for(std::vector<std::string>::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; + + // Convert any forward slashes in the string + // to appropriately escaped backslashes + + #ifdef WIN32 + entry = ReplaceSlashesRegex(entry); + #endif + + // Compile + int errcode = ::regcomp(pregex, entry.c_str(), + REG_EXTENDED | REG_NOSUB); + + if (errcode != 0) + { + char buf[1024]; + regerror(errcode, pregex, buf, sizeof(buf)); + BOX_ERROR("Invalid regular expression: " << + entry << ": " << buf); + THROW_EXCEPTION(CommonException, BadRegularExpression) + } + + // 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 + 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<regex_t *>::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<std::string>::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<std::string>::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 <string> +#include <set> +#include <vector> + +// 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<std::string> mDefinite; +#ifdef HAVE_REGEX_SUPPORT + std::vector<regex_t *> mRegex; + std::vector<std::string> 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..9b53288b --- /dev/null +++ b/lib/common/FdGetLine.cpp @@ -0,0 +1,228 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FdGetLine.cpp +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <sys/types.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include "FdGetLine.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: FdGetLine::FdGetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +FdGetLine::FdGetLine(int fd) + : mFileHandle(fd), + mLineNumber(0), + mBufferBegin(0), + mBytesInBuffer(0), + mPendingEOF(false), + mEOF(false) +{ + 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)} + + // EOF? + if(mEOF) {THROW_EXCEPTION(CommonException, GetLineEOF)} + + std::string r; + + 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 + r += c; + } + + // Implicit line ending at EOF + if(mBufferBegin >= mBytesInBuffer && mPendingEOF) + { + foundLineEnd = true; + } + } + + // Check size + if(r.size() > FDGETLINE_MAX_LINE_SIZE) + { + THROW_EXCEPTION(CommonException, GetLineTooLarge) + } + + // Read more in? + if(!foundLineEnd && mBufferBegin >= mBytesInBuffer && !mPendingEOF) + { +#ifdef WIN32 + int bytes; + + if (mFileHandle == _fileno(stdin)) + { + bytes = console_read(mBuffer, sizeof(mBuffer)); + } + else + { + bytes = ::read(mFileHandle, mBuffer, + sizeof(mBuffer)); + } +#else // !WIN32 + int bytes = ::read(mFileHandle, mBuffer, sizeof(mBuffer)); +#endif // WIN32 + + // Error? + if(bytes == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + // Adjust buffer info + mBytesInBuffer = bytes; + mBufferBegin = 0; + + // EOF / closed? + if(bytes == 0) + { + mPendingEOF = true; + } + } + + // 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) + { + return r; + } + else + { + // Check for comment char, but char before must be whitespace + int end = 0; + int size = r.size(); + while(end < size) + { + if(r[end] == '#' && (end == 0 || (iw(r[end-1])))) + { + break; + } + end++; + } + + // Remove whitespace + int begin = 0; + while(begin < size && iw(r[begin])) + { + begin++; + } + if(!iw(r[end])) end--; + while(end > begin && iw(r[end])) + { + end--; + } + + // Return a sub string + return r.substr(begin, end - begin + 1); + } +} + + +// -------------------------------------------------------------------------- +// +// 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..df43c3c9 --- /dev/null +++ b/lib/common/FdGetLine.h @@ -0,0 +1,65 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FdGetLine.h +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#ifndef FDGETLINE__H +#define FDGETLINE__H + +#include <string> + +#ifdef BOX_RELEASE_BUILD + #define FDGETLINE_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 FDGETLINE_BUFFER_SIZE 5 +#else + #define FDGETLINE_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 FDGETLINE_MAX_LINE_SIZE (1024*256) + +// -------------------------------------------------------------------------- +// +// Class +// Name: FdGetLine +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +class FdGetLine +{ +public: + FdGetLine(int fd); + ~FdGetLine(); +private: + FdGetLine(const FdGetLine &rToCopy); + +public: + std::string GetLine(bool Preprocess = false); + bool IsEOF() {return mEOF;} + int GetLineNumber() {return mLineNumber;} + + // Call to detach, setting file pointer correctly to last bit read. + // Only works for lseek-able file descriptors. + void DetachFile(); + +private: + char mBuffer[FDGETLINE_BUFFER_SIZE]; + int mFileHandle; + int mLineNumber; + int mBufferBegin; + int mBytesInBuffer; + bool mPendingEOF; + bool mEOF; +}; + +#endif // FDGETLINE__H + diff --git a/lib/common/FileModificationTime.cpp b/lib/common/FileModificationTime.cpp new file mode 100644 index 00000000..1109b15f --- /dev/null +++ b/lib/common/FileModificationTime.cpp @@ -0,0 +1,64 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileModificationTime.cpp +// Purpose: Function for getting file modification time. +// Created: 2010/02/15 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <sys/stat.h> + +#include "BoxTime.h" +#include "FileModificationTime.h" + +#include "MemLeakFindOn.h" + +box_time_t FileModificationTime(EMU_STRUCT_STAT &st) +{ +#ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC + box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL); +#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)); +#endif + + return datamodified; +} + +box_time_t FileAttrModificationTime(EMU_STRUCT_STAT &st) +{ + box_time_t statusmodified = +#ifdef HAVE_STRUCT_STAT_ST_MTIMESPEC + (((int64_t)st.st_ctimespec.tv_nsec) / (NANO_SEC_IN_USEC_LL)) + + (((int64_t)st.st_ctimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#elif defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + (((int64_t)st.st_ctim.tv_nsec) / (NANO_SEC_IN_USEC_LL)) + + (((int64_t)st.st_ctim.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC + (((int64_t)st.st_ctimensec) / (NANO_SEC_IN_USEC_LL)) + + (((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL)); +#else // no nanoseconds anywhere + (((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL)); +#endif + + return statusmodified; +} + +box_time_t FileModificationTimeMaxModAndAttr(EMU_STRUCT_STAT &st) +{ +#ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC + box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL); + 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..e6e6c172 --- /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 <sys/stat.h> + +#include "BoxTime.h" + +box_time_t FileModificationTime(EMU_STRUCT_STAT &st); +box_time_t FileAttrModificationTime(EMU_STRUCT_STAT &st); +box_time_t FileModificationTimeMaxModAndAttr(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..5be8237c --- /dev/null +++ b/lib/common/FileStream.cpp @@ -0,0 +1,447 @@ +// -------------------------------------------------------------------------- +// +// 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 <errno.h> + +#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 + BOX_LOG_WIN_WARNING_NUMBER("Failed to open file: " << + mFileName, winerrno); + #else + BOX_LOG_SYS_WARNING("Failed to open file: " << + mFileName); + #endif + + if(errno == EACCES) + { + THROW_EXCEPTION(CommonException, AccessDenied) + } + else + { + THROW_EXCEPTION(CommonException, OSFileOpenError) + } + } +} + + +// -------------------------------------------------------------------------- +// +// 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 + { + BOX_LOG_WIN_ERROR("Failed to read from file: " << mFileName); + r = -1; + } +#else + int r = ::read(mOSFileHandle, pBuffer, NBytes); + if(r == -1) + { + BOX_LOG_SYS_ERROR("Failed to read from file: " << mFileName); + } +#endif + + if(r == -1) + { + THROW_EXCEPTION(CommonException, OSFileReadError) + } + + if(r == 0) + { + mIsEOF = true; + } + + 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) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + 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) +{ + 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)) + { + // DWORD err = GetLastError(); + THROW_EXCEPTION(CommonException, OSFileWriteError) + } +#else + if(::write(mOSFileHandle, pBuffer, NBytes) != NBytes) + { + BOX_LOG_SYS_ERROR("Failed to write to file: " << mFileName); + THROW_EXCEPTION(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 = 0; + + conv.LowPart = SetFilePointer(this->mOSFileHandle, 0, &conv.HighPart, FILE_CURRENT); + + return (IOStream::pos_type)conv.QuadPart; +#else // ! WIN32 + off_t p = ::lseek(mOSFileHandle, 0, SEEK_CUR); + if(p == -1) + { + THROW_EXCEPTION(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_EXCEPTION(CommonException, OSFileError) + } +#else // ! WIN32 + if(::lseek(mOSFileHandle, Offset, ConvertSeekTypeToOSWhence(SeekType)) == -1) + { + THROW_EXCEPTION(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) +#else + if(::close(mOSFileHandle) != 0) +#endif + { + THROW_EXCEPTION(CommonException, OSFileCloseError) + } + + 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 mIsEOF; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::CompareWith(IOStream&, int) +// Purpose: Compare bytes in this file with other stream's data +// Created: 2009/01/03 +// +// -------------------------------------------------------------------------- +bool FileStream::CompareWith(IOStream& rOther, int Timeout) +{ + // Size + IOStream::pos_type mySize = BytesLeftToRead(); + IOStream::pos_type otherSize = 0; + + // Test the contents + char buf1[2048]; + char buf2[2048]; + while(StreamDataLeft() && rOther.StreamDataLeft()) + { + int readSize = rOther.Read(buf1, sizeof(buf1), Timeout); + otherSize += readSize; + + if(Read(buf2, readSize) != readSize || + ::memcmp(buf1, buf2, readSize) != 0) + { + return false; + } + } + + // Check read all the data from the server and file -- can't be + // equal if local and remote aren't the same length. Can't use + // StreamDataLeft() test on local file, because if it's the same + // size, it won't know it's EOF yet. + + if(rOther.StreamDataLeft() || otherSize != mySize) + { + return false; + } + + return true; +} diff --git a/lib/common/FileStream.h b/lib/common/FileStream.h new file mode 100644 index 00000000..9101a968 --- /dev/null +++ b/lib/common/FileStream.h @@ -0,0 +1,66 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileStream.h +// Purpose: FileStream interface to files +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef FILESTREAM__H +#define FILESTREAM__H + +#include "IOStream.h" + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/stat.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#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); + 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); + +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/Guards.h b/lib/common/Guards.h new file mode 100644 index 00000000..cd2e4628 --- /dev/null +++ b/lib/common/Guards.h @@ -0,0 +1,121 @@ +// -------------------------------------------------------------------------- +// +// 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 <unistd.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <new> + +#include "CommonException.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +template <int flags = O_RDONLY | O_BINARY, int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)> +class FileHandleGuard +{ +public: + FileHandleGuard(const std::string& rFilename) + : mOSFileHandle(::open(rFilename.c_str(), flags, mode)) + { + if(mOSFileHandle < 0) + { + BOX_LOG_SYS_ERROR("FileHandleGuard: failed to open " + "file '" << rFilename << "'"); + THROW_EXCEPTION(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<typename type> +class MemoryBlockGuard +{ +public: + MemoryBlockGuard(int BlockSize) + : mpBlock(::malloc(BlockSize)) + { + if(mpBlock == 0) + { + throw std::bad_alloc(); + } + } + + ~MemoryBlockGuard() + { + free(mpBlock); + } + + operator type() const + { + return (type)mpBlock; + } + + type GetPtr() const + { + return (type)mpBlock; + } + + void Resize(int NewSize) + { + void *ptrn = ::realloc(mpBlock, NewSize); + if(ptrn == 0) + { + throw std::bad_alloc(); + } + mpBlock = ptrn; + } + +private: + void *mpBlock; +}; + +#include "MemLeakFindOff.h" + +#endif // GUARDS__H + diff --git a/lib/common/IOStream.cpp b/lib/common/IOStream.cpp new file mode 100644 index 00000000..fc9d0bc3 --- /dev/null +++ b/lib/common/IOStream.cpp @@ -0,0 +1,251 @@ +// -------------------------------------------------------------------------- +// +// 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() +{ +} + + +// -------------------------------------------------------------------------- +// +// 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<char*> 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); + } +} + +void IOStream::Write(const char *pBuffer) +{ + Write(pBuffer, strlen(pBuffer)); +} diff --git a/lib/common/IOStream.h b/lib/common/IOStream.h new file mode 100644 index 00000000..0b1cedd3 --- /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) = 0; + virtual void Write(const char *pBuffer); + virtual void WriteAllBuffered(); + 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); +}; + + +#endif // IOSTREAM__H + + diff --git a/lib/common/IOStreamGetLine.cpp b/lib/common/IOStreamGetLine.cpp new file mode 100644 index 00000000..27a77c29 --- /dev/null +++ b/lib/common/IOStreamGetLine.cpp @@ -0,0 +1,227 @@ +// -------------------------------------------------------------------------- +// +// 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" + +// utility whitespace function +inline bool iw(int c) +{ + return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::IOStreamGetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +IOStreamGetLine::IOStreamGetLine(IOStream &Stream) + : mrStream(Stream), + mLineNumber(0), + mBufferBegin(0), + mBytesInBuffer(0), + mPendingEOF(false), + mEOF(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// 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) +{ + // EOF? + if(mEOF) {THROW_EXCEPTION(CommonException, GetLineEOF)} + + // Initialise string to stored into + std::string r(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 + r += c; + } + + // Implicit line ending at EOF + if(mBufferBegin >= mBytesInBuffer && mPendingEOF) + { + foundLineEnd = true; + } + } + + // Check size + if(r.size() > IOSTREAMGETLINE_MAX_LINE_SIZE) + { + THROW_EXCEPTION(CommonException, GetLineTooLarge) + } + + // Read more in? + if(!foundLineEnd && mBufferBegin >= mBytesInBuffer && !mPendingEOF) + { + int bytes = mrStream.Read(mBuffer, sizeof(mBuffer), Timeout); + + // Adjust buffer info + mBytesInBuffer = bytes; + mBufferBegin = 0; + + // EOF / closed? + if(!mrStream.StreamDataLeft()) + { + mPendingEOF = true; + } + + // No data returned? + if(bytes == 0 && mrStream.StreamDataLeft()) + { + // store string away + mPendingString = r; + // 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) + { + rOutput = r; + return true; + } + else + { + // Check for comment char, but char before must be whitespace + int end = 0; + int size = r.size(); + while(end < size) + { + if(r[end] == '#' && (end == 0 || (iw(r[end-1])))) + { + break; + } + end++; + } + + // Remove whitespace + int begin = 0; + while(begin < size && iw(r[begin])) + { + begin++; + } + if(!iw(r[end])) end--; + while(end > begin && iw(r[end])) + { + end--; + } + + // Return a sub string + rOutput = r.substr(begin, end - begin + 1); + return true; + } +} + + +// -------------------------------------------------------------------------- +// +// 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..9a5d1818 --- /dev/null +++ b/lib/common/IOStreamGetLine.h @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStreamGetLine.h +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#ifndef IOSTREAMGETLINE__H +#define IOSTREAMGETLINE__H + +#include <string> + +#include "IOStream.h" + +#ifdef BOX_RELEASE_BUILD + #define IOSTREAMGETLINE_BUFFER_SIZE 1024 +#else + #define IOSTREAMGETLINE_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 IOSTREAMGETLINE_MAX_LINE_SIZE (1024*256) + +// -------------------------------------------------------------------------- +// +// Class +// Name: IOStreamGetLine +// Purpose: Line based stream reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +class IOStreamGetLine +{ +public: + IOStreamGetLine(IOStream &Stream); + ~IOStreamGetLine(); +private: + IOStreamGetLine(const IOStreamGetLine &rToCopy); + +public: + bool GetLine(std::string &rOutput, bool Preprocess = false, int Timeout = IOStream::TimeOutInfinite); + bool IsEOF() {return mEOF;} + int GetLineNumber() {return mLineNumber;} + + // Call to detach, setting file pointer correctly to last bit read. + // Only works for lseek-able file descriptors. + void DetachFile(); + + // 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;} + +private: + char mBuffer[IOSTREAMGETLINE_BUFFER_SIZE]; + IOStream &mrStream; + int mLineNumber; + int mBufferBegin; + int mBytesInBuffer; + bool mPendingEOF; + bool mEOF; + std::string mPendingString; +}; + +#endif // IOSTREAMGETLINE__H + diff --git a/lib/common/InvisibleTempFileStream.cpp b/lib/common/InvisibleTempFileStream.cpp new file mode 100644 index 00000000..abfcb5f6 --- /dev/null +++ b/lib/common/InvisibleTempFileStream.cpp @@ -0,0 +1,39 @@ +// -------------------------------------------------------------------------- +// +// 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 char *Filename, int flags, int mode) +#ifdef WIN32 + : FileStream(Filename, flags | O_TEMPORARY, mode) +#else + : FileStream(Filename, flags, mode) +#endif +{ + #ifndef WIN32 + if(unlink(Filename) != 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..a77d05e2 --- /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 char *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..296443ea --- /dev/null +++ b/lib/common/Logging.cpp @@ -0,0 +1,518 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Logging.cpp +// Purpose: Generic logging core routines implementation +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <errno.h> +#include <time.h> +#include <string.h> // for stderror + +// c.f. http://bugs.debian.org/512510 +#include <cstdio> + +#ifdef HAVE_SYSLOG_H + #include <syslog.h> +#endif +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include <cstring> +#include <iomanip> + +#include "BoxTime.h" +#include "Logging.h" + +bool Logging::sLogToSyslog = false; +bool Logging::sLogToConsole = false; +bool Logging::sContextSet = false; + +bool HideExceptionMessageGuard::sHiddenState = false; + +std::vector<Logger*> Logging::sLoggers; +std::string Logging::sContext; +Console* Logging::spConsole = NULL; +Syslog* Logging::spSyslog = NULL; +Log::Level Logging::sGlobalLevel = Log::EVERYTHING; +Logging Logging::sGlobalLogging; //automatic initialisation +std::string Logging::sProgramName; + +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<Logger*>::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<Logger*>::iterator i = sLoggers.begin(); + i != sLoggers.end(); i++) + { + if (*i == pOldLogger) + { + sLoggers.erase(i); + return; + } + } +} + +void Logging::Log(Log::Level level, const std::string& rFile, + int line, const std::string& rMessage) +{ + if (level > sGlobalLevel) + { + return; + } + + std::string newMessage; + + if (sContextSet) + { + newMessage += "[" + sContext + "] "; + } + + newMessage += rMessage; + + for (std::vector<Logger*>::iterator i = sLoggers.begin(); + i != sLoggers.end(); i++) + { + bool result = (*i)->Log(level, rFile, line, newMessage); + if (!result) + { + return; + } + } +} + +void Logging::LogToSyslog(Log::Level level, const std::string& rFile, + int line, const std::string& rMessage) +{ + if (!sLogToSyslog) + { + return; + } + + if (level > sGlobalLevel) + { + return; + } + + std::string newMessage; + + if (sContextSet) + { + newMessage += "[" + sContext + "] "; + } + + newMessage += rMessage; + + spSyslog->Log(level, rFile, line, newMessage); +} + +void Logging::SetContext(std::string context) +{ + sContext = context; + sContextSet = true; +} + +Log::Level Logging::GetNamedLevel(const std::string& rName) +{ + if (rName == "nothing") { return Log::NOTHING; } + else if (rName == "fatal") { return Log::FATAL; } + else if (rName == "error") { return Log::ERROR; } + else if (rName == "warning") { return Log::WARNING; } + else if (rName == "notice") { return Log::NOTICE; } + else if (rName == "info") { return Log::INFO; } + else if (rName == "trace") { return Log::TRACE; } + else if (rName == "everything") { return Log::EVERYTHING; } + else + { + BOX_ERROR("Unknown verbosity level: " << rName); + return Log::INVALID; + } +} + +void Logging::ClearContext() +{ + sContextSet = false; +} + +void Logging::SetProgramName(const std::string& rProgramName) +{ + sProgramName = rProgramName; + + for (std::vector<Logger*>::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 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& rFile, + int line, std::string& rMessage) +{ + if (level > GetLevel()) + { + return true; + } + + FILE* target = stdout; + + if (level <= Log::WARNING) + { + target = stderr; + } + + 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 << rMessage; + + #ifdef WIN32 + std::string output = buf.str(); + ConvertUtf8ToConsole(output.c_str(), output); + fprintf(target, "%s\n", output.c_str()); + #else + fprintf(target, "%s\n", buf.str().c_str()); + #endif + + return true; +} + +bool Syslog::Log(Log::Level level, const std::string& rFile, + int line, std::string& rMessage) +{ + 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 += rMessage; + + syslog(syslogLevel, "%s", msg.c_str()); + + return true; +} + +Syslog::Syslog() : mFacility(LOG_LOCAL6) +{ + ::openlog("Box Backup", LOG_PID, mFacility); +} + +Syslog::~Syslog() +{ + ::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& rFile, + int line, std::string& rMessage) +{ + if (Level > GetLevel()) + { + return true; + } + + /* avoid infinite loop if this throws an exception */ + Logging::Remove(this); + + std::ostringstream buf; + buf << FormatTime(GetCurrentBoxTime(), true, false); + buf << " "; + + if (Level <= Log::FATAL) + { + buf << "[FATAL] "; + } + else if (Level <= Log::ERROR) + { + buf << "[ERROR] "; + } + else if (Level <= Log::WARNING) + { + buf << "[WARNING] "; + } + else if (Level <= Log::NOTICE) + { + buf << "[NOTICE] "; + } + else if (Level <= Log::INFO) + { + buf << "[INFO] "; + } + else if (Level <= Log::TRACE) + { + buf << "[TRACE] "; + } + + buf << rMessage << "\n"; + std::string output = buf.str(); + + #ifdef WIN32 + ConvertUtf8ToConsole(output.c_str(), output); + #endif + + mLogFile.Write(output.c_str(), output.length()); + + Logging::Add(this); + return true; +} + +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(); +} diff --git a/lib/common/Logging.h b/lib/common/Logging.h new file mode 100644 index 00000000..15400711 --- /dev/null +++ b/lib/common/Logging.h @@ -0,0 +1,346 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Logging.h +// Purpose: Generic logging core routines declarations and macros +// Created: 2006/12/16 +// +// -------------------------------------------------------------------------- + +#ifndef LOGGING__H +#define LOGGING__H + +#include <cerrno> +#include <cstring> +#include <iomanip> +#include <sstream> +#include <vector> + +#include "FileStream.h" + +#define BOX_LOG(level, stuff) \ +{ \ + std::ostringstream _box_log_line; \ + _box_log_line << stuff; \ + Logging::Log(level, __FILE__, __LINE__, _box_log_line.str()); \ +} + +#define BOX_SYSLOG(level, stuff) \ +{ \ + std::ostringstream _box_log_line; \ + _box_log_line << stuff; \ + Logging::LogToSyslog(level, __FILE__, __LINE__, _box_log_line.str()); \ +} + +#define BOX_FATAL(stuff) BOX_LOG(Log::FATAL, stuff) +#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) \ + if (Logging::IsEnabled(Log::TRACE)) \ + { BOX_LOG(Log::TRACE, stuff) } + +#define BOX_SYS_ERROR(stuff) \ + stuff << ": " << std::strerror(errno) << " (" << errno << ")" + +#define BOX_LOG_SYS_WARNING(stuff) \ + BOX_WARNING(BOX_SYS_ERROR(stuff)) +#define BOX_LOG_SYS_ERROR(stuff) \ + BOX_ERROR(BOX_SYS_ERROR(stuff)) +#define BOX_LOG_SYS_FATAL(stuff) \ + BOX_FATAL(BOX_SYS_ERROR(stuff)) + +#define LOG_AND_THROW_ERROR(message, filename, exception, subtype) \ + BOX_LOG_SYS_ERROR(message << ": " << filename); \ + THROW_EXCEPTION_MESSAGE(exception, subtype, \ + BOX_SYS_ERROR(message << ": " << filename)); + +inline std::string GetNativeErrorMessage() +{ +#ifdef WIN32 + return GetErrorMessage(GetLastError()); +#else + std::ostringstream _box_log_line; + _box_log_line << std::strerror(errno) << " (" << errno << ")"; + return _box_log_line.str(); +#endif +} + +#ifdef WIN32 + #define BOX_LOG_WIN_ERROR(stuff) \ + BOX_ERROR(stuff << ": " << GetErrorMessage(GetLastError())) + #define BOX_LOG_WIN_WARNING(stuff) \ + BOX_WARNING(stuff << ": " << GetErrorMessage(GetLastError())) + #define BOX_LOG_WIN_ERROR_NUMBER(stuff, number) \ + BOX_ERROR(stuff << ": " << GetErrorMessage(number)) + #define BOX_LOG_WIN_WARNING_NUMBER(stuff, number) \ + BOX_WARNING(stuff << ": " << GetErrorMessage(number)) + #define BOX_LOG_NATIVE_ERROR(stuff) BOX_LOG_WIN_ERROR(stuff) + #define BOX_LOG_NATIVE_WARNING(stuff) BOX_LOG_WIN_WARNING(stuff) +#else + #define BOX_LOG_NATIVE_ERROR(stuff) BOX_LOG_SYS_ERROR(stuff) + #define BOX_LOG_NATIVE_WARNING(stuff) BOX_LOG_SYS_WARNING(stuff) +#endif + +#define BOX_LOG_SOCKET_ERROR(_type, _name, _port, stuff) \ + BOX_LOG_NATIVE_ERROR(stuff << " (type " << _type << ", name " << \ + _name << ", port " << _port << ")") + +#define BOX_FORMAT_HEX32(number) \ + std::hex << \ + std::showbase << \ + std::internal << \ + std::setw(10) << \ + std::setfill('0') << \ + (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) << \ + timespec.tv_usec + +#undef ERROR + +namespace Log +{ + enum Level + { + NOTHING = 1, + FATAL, + ERROR, + WARNING, + NOTICE, + INFO, + TRACE, + EVERYTHING, + INVALID = -1 + }; +} + +// -------------------------------------------------------------------------- +// +// 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& rFile, + int line, std::string& rMessage) = 0; + + void Filter(Log::Level level) + { + mCurrentLevel = level; + } + + virtual const char* GetType() = 0; + Log::Level GetLevel() { return mCurrentLevel; } + + virtual void SetProgramName(const std::string& rProgramName) = 0; +}; + +// -------------------------------------------------------------------------- +// +// 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& rFile, + int line, std::string& rMessage); + 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); +}; + +// -------------------------------------------------------------------------- +// +// 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& rFile, + int line, std::string& rMessage); + virtual const char* GetType() { return "Syslog"; } + virtual void SetProgramName(const std::string& rProgramName); + virtual void SetFacility(int facility); + static int GetNamedFacility(const std::string& rFacility); +}; + +// -------------------------------------------------------------------------- +// +// 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<Logger*> sLoggers; + static bool sLogToSyslog, sLogToConsole; + static std::string sContext; + static bool sContextSet; + static Console* spConsole; + static Syslog* spSyslog; + static Log::Level sGlobalLevel; + static Logging sGlobalLogging; + static std::string sProgramName; + + 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& rFile, + int line, const std::string& rMessage); + static void LogToSyslog(Log::Level level, const std::string& rFile, + int line, const std::string& rMessage); + static void SetContext(std::string context); + static void ClearContext(); + static void SetGlobalLevel(Log::Level level) { sGlobalLevel = level; } + static Log::Level GetGlobalLevel() { return sGlobalLevel; } + static Log::Level GetNamedLevel(const std::string& rName); + static bool IsEnabled(Log::Level level) + { + return (int)sGlobalLevel >= (int)level; + } + static void SetProgramName(const std::string& rProgramName); + static std::string GetProgramName() { return sProgramName; } + static void SetFacility(int facility); + + class Guard + { + private: + Log::Level mOldLevel; + + public: + Guard(Log::Level newLevel) + { + mOldLevel = Logging::GetGlobalLevel(); + Logging::SetGlobalLevel(newLevel); + } + ~Guard() + { + Logging::SetGlobalLevel(mOldLevel); + } + }; + + class Tagger + { + private: + std::string mOldTag; + + public: + Tagger(const std::string& rTempTag) + { + mOldTag = Logging::GetProgramName(); + Logging::SetProgramName(mOldTag + " " + rTempTag); + } + ~Tagger() + { + Logging::SetProgramName(mOldTag); + } + }; +}; + +class FileLogger : public Logger +{ + private: + FileStream mLogFile; + FileLogger(const FileLogger& forbidden) + : mLogFile("") { /* do not call */ } + + public: + FileLogger(const std::string& rFileName, Log::Level Level) + : Logger(Level), + mLogFile(rFileName, O_WRONLY | O_CREAT | O_APPEND) + { } + + virtual bool Log(Log::Level Level, const std::string& rFile, + int Line, std::string& rMessage); + + virtual const char* GetType() { return "FileLogger"; } + virtual void SetProgramName(const std::string& rProgramName) { } +}; + +class HideExceptionMessageGuard +{ + public: + HideExceptionMessageGuard() + { + mOldHiddenState = sHiddenState; + sHiddenState = true; + } + ~HideExceptionMessageGuard() + { + sHiddenState = mOldHiddenState; + } + static bool ExceptionsHidden() { return sHiddenState; } + + private: + static bool sHiddenState; + bool mOldHiddenState; +}; + +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..d91bc2f9 --- /dev/null +++ b/lib/common/MainHelper.h @@ -0,0 +1,43 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MainHelper.h +// Purpose: Helper stuff for main() programs +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- + +#ifndef MAINHELPER__H +#define MAINHELPER__H + +#include <stdio.h> + +#include "BoxException.h" + +#define MAINHELPER_START \ + if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ + { printf(BOX_VERSION "\n"); return 0; } \ + MEMLEAKFINDER_INIT \ + MEMLEAKFINDER_START \ + try { +#define MAINHELPER_END \ + } catch(BoxException &e) { \ + printf("Exception: %s (%d/%d)\n", e.what(), e.GetType(), e.GetSubType()); \ + return 1; \ + } catch(std::exception &e) { \ + printf("Exception: %s\n", e.what()); \ + return 1; \ + } catch(...) { \ + printf("Exception: <UNKNOWN>\n"); \ + 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..538a7ef8 --- /dev/null +++ b/lib/common/MemBlockStream.cpp @@ -0,0 +1,235 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemBlockStream.cpp +// Purpose: Stream out data from any memory block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <string.h> + +#include "MemBlockStream.h" +#include "CommonException.h" +#include "StreamableMemBlock.h" +#include "CollectInBufferStream.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// 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 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) +{ + 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..f78ff8e6 --- /dev/null +++ b/lib/common/MemBlockStream.h @@ -0,0 +1,52 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemBlockStream.h +// Purpose: Stream out data from any memory block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#ifndef MEMBLOCKSTREAM__H +#define MEMBLOCKSTREAM__H + +#include "IOStream.h" + +class StreamableMemBlock; +class CollectInBufferStream; + +// -------------------------------------------------------------------------- +// +// 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(const void *pBuffer, int Size); + 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); + virtual pos_type GetPosition() const; + virtual void Seek(pos_type Offset, int SeekType); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + 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..c20fe25a --- /dev/null +++ b/lib/common/MemLeakFindOn.h @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------- +// +// 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 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..ca207bd5 --- /dev/null +++ b/lib/common/MemLeakFinder.h @@ -0,0 +1,63 @@ +// -------------------------------------------------------------------------- +// +// 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 <cstdlib> +#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_realloc(void *ptr, size_t size); + void memleakfinder_free(void *ptr); +} + +void memleakfinder_init(); + +int memleakfinder_numleaks(); + +void memleakfinder_reportleaks(); + +void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext); + +void memleakfinder_setup_exit_report(const char *filename, const char *markertext); + +void memleakfinder_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 +#ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING + #define malloc(X) memleakfinder_malloc(X, __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..f96f80b5 --- /dev/null +++ b/lib/common/NamedLock.cpp @@ -0,0 +1,170 @@ +// -------------------------------------------------------------------------- +// +// 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 <fcntl.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#ifdef HAVE_FLOCK + #include <sys/file.h> +#endif + +#include "NamedLock.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::NamedLock() +// Purpose: Constructor +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +NamedLock::NamedLock() + : mFileDescriptor(-1) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::~NamedLock() +// Purpose: Destructor (automatically unlocks if locked) +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +NamedLock::~NamedLock() +{ + if(mFileDescriptor != -1) + { + 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 + if(mFileDescriptor != -1) + { + THROW_EXCEPTION(CommonException, NamedLockAlreadyLockingSomething) + } + + // See if the lock can be got +#if HAVE_DECL_O_EXLOCK + int fd = ::open(rFilename.c_str(), + O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC | O_EXLOCK, mode); + if(fd != -1) + { + // Got a lock, lovely + mFileDescriptor = fd; + return true; + } + + // Failed. Why? + if(errno != EWOULDBLOCK) + { + // Not the expected error + THROW_EXCEPTION(CommonException, OSFileError) + } + + return false; +#else + int fd = ::open(rFilename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); + if(fd == -1) + { + BOX_WARNING("Failed to open lockfile: " << rFilename); + THROW_EXCEPTION(CommonException, OSFileError) + } + +#ifdef HAVE_FLOCK + if(::flock(fd, LOCK_EX | LOCK_NB) != 0) + { + ::close(fd); + if(errno == EWOULDBLOCK) + { + return false; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError) + } + } +#elif HAVE_DECL_F_SETLK + struct flock desc; + desc.l_type = F_WRLCK; + desc.l_whence = SEEK_SET; + desc.l_start = 0; + desc.l_len = 0; + if(::fcntl(fd, F_SETLK, &desc) != 0) + { + ::close(fd); + if(errno == EAGAIN) + { + return false; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError) + } + } +#endif + + // Success + mFileDescriptor = fd; + + return true; +#endif +} + +// -------------------------------------------------------------------------- +// +// 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? + if(mFileDescriptor == -1) + { + THROW_EXCEPTION(CommonException, NamedLockNotHeld) + } + + // Close the file + if(::close(mFileDescriptor) != 0) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + // Mark as unlocked + mFileDescriptor = -1; +} + + + + diff --git a/lib/common/NamedLock.h b/lib/common/NamedLock.h new file mode 100644 index 00000000..534115db --- /dev/null +++ b/lib/common/NamedLock.h @@ -0,0 +1,41 @@ +// -------------------------------------------------------------------------- +// +// 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); + bool GotLock() {return mFileDescriptor != -1;} + void ReleaseLock(); + + +private: + int mFileDescriptor; +}; + +#endif // NAMEDLOCK__H + diff --git a/lib/common/PartialReadStream.cpp b/lib/common/PartialReadStream.cpp new file mode 100644 index 00000000..f2f79715 --- /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) +{ + 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..1b46b0bd --- /dev/null +++ b/lib/common/PartialReadStream.h @@ -0,0 +1,46 @@ +// -------------------------------------------------------------------------- +// +// 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); + 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 <string> + +// -------------------------------------------------------------------------- +// +// 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 <string> + +// -------------------------------------------------------------------------- +// +// 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/ReadGatherStream.cpp b/lib/common/ReadGatherStream.cpp new file mode 100644 index 00000000..f50e6664 --- /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) +{ + 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..613ede3e --- /dev/null +++ b/lib/common/ReadGatherStream.h @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------- +// +// 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 <vector> + +// -------------------------------------------------------------------------- +// +// 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); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + virtual pos_type GetPosition() const; + +private: + bool mDeleteComponentStreamsOnDestruction; + std::vector<IOStream *> mComponents; + + typedef struct + { + pos_type mLength; + pos_type mSeekTo; + int mComponent; + bool mSeek; + } Block; + + std::vector<Block> 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..54c99c95 --- /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 <string.h> + +#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) +{ + 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..b23b542c --- /dev/null +++ b/lib/common/ReadLoggingStream.h @@ -0,0 +1,58 @@ +// -------------------------------------------------------------------------- +// +// 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); + 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..36e9a4d3 --- /dev/null +++ b/lib/common/SelfFlushingStream.h @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: SelfFlushingStream.h +// Purpose: A stream wrapper that always flushes the underlying +// stream, to ensure protocol safety. +// Created: 2008/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef SELFFLUSHINGSTREAM__H +#define SELFFLUSHINGSTREAM__H + +#include "IOStream.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: SelfFlushingStream +// Purpose: A stream wrapper that always flushes the underlying +// stream, to ensure protocol safety. +// Created: 2008/08/20 +// +// -------------------------------------------------------------------------- +class SelfFlushingStream : public IOStream +{ +public: + SelfFlushingStream(IOStream &rSource) + : mrSource(rSource) { } + + SelfFlushingStream(const SelfFlushingStream &rToCopy) + : mrSource(rToCopy.mrSource) { } + + ~SelfFlushingStream() + { + Flush(); + } + +private: + // no copying from IOStream allowed + SelfFlushingStream(const IOStream& rToCopy); + +public: + virtual int Read(void *pBuffer, int NBytes, + int Timeout = IOStream::TimeOutInfinite) + { + return mrSource.Read(pBuffer, NBytes, Timeout); + } + virtual pos_type BytesLeftToRead() + { + return mrSource.BytesLeftToRead(); + } + virtual void Write(const void *pBuffer, int NBytes) + { + mrSource.Write(pBuffer, NBytes); + } + virtual bool StreamDataLeft() + { + return mrSource.StreamDataLeft(); + } + virtual bool StreamClosed() + { + return mrSource.StreamClosed(); + } + +private: + IOStream &mrSource; +}; + +#endif // SELFFLUSHINGSTREAM__H + diff --git a/lib/common/StreamableMemBlock.cpp b/lib/common/StreamableMemBlock.cpp new file mode 100644 index 00000000..cf431022 --- /dev/null +++ b/lib/common/StreamableMemBlock.cpp @@ -0,0 +1,364 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: StreamableMemBlock.cpp +// Purpose: Memory blocks which can be loaded and saved from streams +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <new> +#include <cstdlib> +#include <string.h> + +#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 */)) + { + 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 */)) + { + 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 */)) + { + 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..250c0aea --- /dev/null +++ b/lib/common/StreamableMemBlock.h @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: StreamableMemBlock.h +// Purpose: Memory blocks which can be loaded and saved from streams +// 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/TemporaryDirectory.h b/lib/common/TemporaryDirectory.h new file mode 100644 index 00000000..9d52ecd9 --- /dev/null +++ b/lib/common/TemporaryDirectory.h @@ -0,0 +1,46 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: TemporaryDirectory.h +// Purpose: Location of temporary directory +// Created: 2003/10/13 +// +// -------------------------------------------------------------------------- + +#ifndef TEMPORARYDIRECTORY__H +#define TEMPORARYDIRECTORY__H + +#include <string> + +#ifdef WIN32 + #include <windows.h> +#endif + +// Prefix name with Box to avoid clashing with OS API names +std::string BoxGetTemporaryDirectoryName() +{ +#ifdef WIN32 + // http://msdn.microsoft.com/library/default.asp? + // url=/library/en-us/fileio/fs/creating_and_using_a_temporary_file.asp + + DWORD dwRetVal; + char lpPathBuffer[1024]; + DWORD dwBufSize = sizeof(lpPathBuffer); + + // Get the temp path. + dwRetVal = GetTempPath(dwBufSize, // length of the buffer + lpPathBuffer); // buffer for path + if (dwRetVal > dwBufSize) + { + THROW_EXCEPTION(CommonException, TempDirPathTooLong) + } + + return std::string(lpPathBuffer); +#elif defined TEMP_DIRECTORY_NAME + return std::string(TEMP_DIRECTORY_NAME); +#else + #error non-static temporary directory names not supported yet +#endif +} + +#endif // TEMPORARYDIRECTORY__H diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp new file mode 100644 index 00000000..56638058 --- /dev/null +++ b/lib/common/Test.cpp @@ -0,0 +1,486 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Test.cpp +// Purpose: Useful stuff for tests +// Created: 2008/04/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include "Test.h" + +bool TestFileExists(const char *Filename) +{ + EMU_STRUCT_STAT st; + return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0; +} + +bool TestFileNotEmpty(const char *Filename) +{ + EMU_STRUCT_STAT st; + return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0 && + st.st_size > 0; +} + +bool TestDirExists(const char *Filename) +{ + EMU_STRUCT_STAT st; + return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR; +} + +// -1 if doesn't exist +int TestGetFileSize(const char *Filename) +{ + EMU_STRUCT_STAT st; + if(EMU_STAT(Filename, &st) == 0) + { + return st.st_size; + } + return -1; +} + +std::string ConvertPaths(const std::string& rOriginal) +{ +#ifdef WIN32 + // convert UNIX paths to native + + std::string converted; + for (size_t i = 0; i < rOriginal.size(); i++) + { + if (rOriginal[i] == '/') + { + converted += '\\'; + } + else + { + converted += rOriginal[i]; + } + } + return converted; + +#else // !WIN32 + return rOriginal; +#endif +} + +int RunCommand(const std::string& rCommandLine) +{ + return ::system(ConvertPaths(rCommandLine).c_str()); +} + +#ifdef WIN32 +#include <windows.h> +#endif + +bool ServerIsAlive(int pid) +{ + #ifdef WIN32 + + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, + false, pid); + if (hProcess == NULL) + { + if (GetLastError() != ERROR_INVALID_PARAMETER) + { + BOX_ERROR("Failed to open process " << pid << + ": " << + GetErrorMessage(GetLastError())); + } + return false; + } + + DWORD exitCode; + BOOL result = GetExitCodeProcess(hProcess, &exitCode); + CloseHandle(hProcess); + + if (result == 0) + { + BOX_ERROR("Failed to get exit code for process " << + pid << ": " << + GetErrorMessage(GetLastError())) + return false; + } + + if (exitCode == STILL_ACTIVE) + { + return true; + } + + return false; + + #else // !WIN32 + + if(pid == 0) return false; + return ::kill(pid, 0) != -1; + + #endif // WIN32 +} + +int ReadPidFile(const char *pidFile) +{ + if(!TestFileNotEmpty(pidFile)) + { + TEST_FAIL_WITH_MESSAGE("Server didn't save PID file " + "(perhaps one was already running?)"); + return -1; + } + + int pid = -1; + + FILE *f = fopen(pidFile, "r"); + if(f == NULL || fscanf(f, "%d", &pid) != 1) + { + TEST_FAIL_WITH_MESSAGE("Couldn't read PID file"); + return -1; + } + fclose(f); + + return pid; +} + +int LaunchServer(const std::string& rCommandLine, const char *pidFile) +{ + ::fprintf(stdout, "Starting server: %s\n", rCommandLine.c_str()); + +#ifdef WIN32 + + PROCESS_INFORMATION procInfo; + + STARTUPINFO startInfo; + startInfo.cb = sizeof(startInfo); + startInfo.lpReserved = NULL; + startInfo.lpDesktop = NULL; + startInfo.lpTitle = NULL; + startInfo.dwFlags = 0; + startInfo.cbReserved2 = 0; + startInfo.lpReserved2 = NULL; + + std::string cmd = ConvertPaths(rCommandLine); + CHAR* tempCmd = strdup(cmd.c_str()); + + DWORD result = CreateProcess + ( + NULL, // lpApplicationName, naughty! + tempCmd, // lpCommandLine + NULL, // lpProcessAttributes + NULL, // lpThreadAttributes + false, // bInheritHandles + 0, // dwCreationFlags + NULL, // lpEnvironment + NULL, // lpCurrentDirectory + &startInfo, // lpStartupInfo + &procInfo // lpProcessInformation + ); + + free(tempCmd); + + if (result == 0) + { + DWORD err = GetLastError(); + printf("Launch failed: %s: error %d\n", rCommandLine.c_str(), + (int)err); + TEST_FAIL_WITH_MESSAGE("Couldn't start server"); + return -1; + } + + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + + return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId); + +#else // !WIN32 + + if(RunCommand(rCommandLine) != 0) + { + TEST_FAIL_WITH_MESSAGE("Couldn't start server"); + return -1; + } + + return WaitForServerStartup(pidFile, 0); + +#endif // WIN32 +} + +int WaitForServerStartup(const char *pidFile, int pidIfKnown) +{ + #ifdef WIN32 + if (pidFile == NULL) + { + return pidIfKnown; + } + #else + // on other platforms there is no other way to get + // the PID, so a NULL pidFile doesn't make sense. + ASSERT(pidFile != NULL); + #endif + + // time for it to start up + if (Logging::GetGlobalLevel() >= Log::TRACE) + { + BOX_TRACE("Waiting for server to start"); + } + else + { + ::fprintf(stdout, "Waiting for server to start: "); + } + + for (int i = 0; i < 15; i++) + { + if (TestFileNotEmpty(pidFile)) + { + break; + } + + if (pidIfKnown && !ServerIsAlive(pidIfKnown)) + { + break; + } + + if (Logging::GetGlobalLevel() < Log::TRACE) + { + ::fprintf(stdout, "."); + ::fflush(stdout); + } + + ::sleep(1); + } + + // on Win32 we can check whether the process is alive + // without even checking the PID file + + if (pidIfKnown && !ServerIsAlive(pidIfKnown)) + { + if (Logging::GetGlobalLevel() >= Log::TRACE) + { + BOX_ERROR("server died!"); + } + else + { + ::fprintf(stdout, " server died!\n"); + } + + TEST_FAIL_WITH_MESSAGE("Server died!"); + return -1; + } + + if (!TestFileNotEmpty(pidFile)) + { + if (Logging::GetGlobalLevel() >= Log::TRACE) + { + BOX_ERROR("timed out!"); + } + else + { + ::fprintf(stdout, " timed out!\n"); + } + + TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); + return -1; + } + + if (Logging::GetGlobalLevel() >= Log::TRACE) + { + BOX_TRACE("Server started"); + } + else + { + ::fprintf(stdout, " done.\n"); + } + + // wait a second for the pid to be written to the file + ::sleep(1); + + // read pid file + int pid = ReadPidFile(pidFile); + + // On Win32 we can check whether the PID in the pidFile matches + // the one returned by the system, which it always should. + + if (pidIfKnown && pid != pidIfKnown) + { + BOX_ERROR("Server wrote wrong pid to file (" << pidFile << + "): expected " << pidIfKnown << " but found " << + pid); + TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file"); + return -1; + } + + return pid; +} + +void TestRemoteProcessMemLeaksFunc(const char *filename, + const char* file, int line) +{ +#ifdef BOX_MEMORY_LEAK_TESTING + // Does the file exist? + if(!TestFileExists(filename)) + { + if (failures == 0) + { + first_fail_file = file; + first_fail_line = line; + } + ++failures; + printf("FAILURE: MemLeak report not available (file %s) " + "at %s:%d\n", filename, file, line); + } + else + { + // Is it empty? + if(TestGetFileSize(filename) > 0) + { + if (failures == 0) + { + first_fail_file = file; + first_fail_line = line; + } + ++failures; + printf("FAILURE: Memory leaks found in other process " + "(file %s) at %s:%d\n==========\n", + filename, file, line); + FILE *f = fopen(filename, "r"); + char linebuf[512]; + while(::fgets(linebuf, sizeof(linebuf), f) != 0) + { + printf("%s", linebuf); + } + fclose(f); + printf("==========\n"); + } + + // Delete it + ::unlink(filename); + } +#endif +} + +void force_sync() +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "force-sync") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); +} + +void wait_for_sync_start() +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "wait-for-sync") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); +} + +void wait_for_sync_end() +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "wait-for-end") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); +} + +void sync_and_wait() +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "sync-and-wait") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); +} + +void terminate_bbackupd(int pid) +{ + TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf " + "terminate") == 0); + TestRemoteProcessMemLeaks("bbackupctl.memleaks"); + + for (int i = 0; i < 20; i++) + { + if (!ServerIsAlive(pid)) break; + fprintf(stdout, "."); + fflush(stdout); + sleep(1); + } + + TEST_THAT(!ServerIsAlive(pid)); + TestRemoteProcessMemLeaks("bbackupd.memleaks"); +} + + +// Wait a given number of seconds for something to complete +void wait_for_operation(int seconds, const char* message) +{ + if (Logging::GetGlobalLevel() >= Log::TRACE) + { + BOX_TRACE("Waiting " << seconds << " seconds for " << message); + } + else + { + printf("Waiting for %s: ", message); + fflush(stdout); + } + + for(int l = 0; l < seconds; ++l) + { + sleep(1); + if (Logging::GetGlobalLevel() < Log::TRACE) + { + printf("."); + fflush(stdout); + } + } + + if (Logging::GetGlobalLevel() >= Log::TRACE) + { + BOX_TRACE("Finished waiting for " << message); + } + else + { + printf(" done.\n"); + fflush(stdout); + } +} + +void safe_sleep(int seconds) +{ + BOX_TRACE("sleeping for " << seconds << " seconds"); + +#ifdef WIN32 + Sleep(seconds * 1000); +#else + struct timespec ts; + memset(&ts, 0, sizeof(ts)); + ts.tv_sec = seconds; + ts.tv_nsec = 0; + 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 = (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 / 1000000000) << " secs remaining, " + "sleeping again"); + } +#endif +} + diff --git a/lib/common/Test.h b/lib/common/Test.h new file mode 100644 index 00000000..08ba4542 --- /dev/null +++ b/lib/common/Test.h @@ -0,0 +1,167 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Test.h +// Purpose: Useful stuff for tests +// Created: 2003/07/11 +// +// -------------------------------------------------------------------------- + +#ifndef TEST__H +#define TEST__H + +#include <cstring> + +#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); +#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); +#endif + +extern int failures; +extern int first_fail_line; +extern std::string first_fail_file; +extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; + +#define TEST_FAIL_WITH_MESSAGE(msg) \ +{ \ + if (failures == 0) \ + { \ + first_fail_file = __FILE__; \ + first_fail_line = __LINE__; \ + } \ + failures++; \ + BOX_ERROR("**** TEST FAILURE: " << msg << " at " << __FILE__ << \ + ":" << __LINE__); \ +} + +#define TEST_ABORT_WITH_MESSAGE(msg) {TEST_FAIL_WITH_MESSAGE(msg); return 1;} + +#define TEST_THAT(condition) {if(!(condition)) TEST_FAIL_WITH_MESSAGE("Condition [" #condition "] failed")} +#define TEST_THAT_ABORTONFAIL(condition) {if(!(condition)) TEST_ABORT_WITH_MESSAGE("Condition [" #condition "] failed")} + +// NOTE: The 0- bit is to allow this to work with stuff which has negative constants for flags (eg ConnectionException) +#define TEST_CHECK_THROWS(statement, excepttype, subtype) \ + { \ + bool didthrow = false; \ + HideExceptionMessageGuard hide; \ + try \ + { \ + statement; \ + } \ + catch(excepttype &e) \ + { \ + if(e.GetSubType() != ((unsigned int)excepttype::subtype) \ + && e.GetSubType() != (unsigned int)(0-excepttype::subtype)) \ + { \ + throw; \ + } \ + didthrow = true; \ + } \ + catch(...) \ + { \ + throw; \ + } \ + if(!didthrow) \ + { \ + TEST_FAIL_WITH_MESSAGE("Didn't throw exception " #excepttype "(" #subtype ")") \ + } \ + } + +// utility macro for comparing two strings in a line +#define TEST_EQUAL(_expected, _found) \ +{ \ + std::ostringstream _oss1; \ + _oss1 << _expected; \ + std::string _exp_str = _oss1.str(); \ + \ + std::ostringstream _oss2; \ + _oss2 << _found; \ + std::string _found_str = _oss2.str(); \ + \ + if(_exp_str != _found_str) \ + { \ + BOX_WARNING("Expected <" << _exp_str << "> but found <" << \ + _found_str << ">"); \ + \ + std::ostringstream _oss3; \ + _oss3 << #_found << " != " << #_expected; \ + \ + TEST_FAIL_WITH_MESSAGE(_oss3.str().c_str()); \ + } \ +} + +// 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 macro for testing a line +#define TEST_LINE(_condition, _line) \ + TEST_THAT(_condition); \ + if (!(_condition)) \ + { \ + printf("Test failed on <%s>\n", _line.c_str()); \ + } + +bool TestFileExists(const char *Filename); +bool TestDirExists(const char *Filename); + +// -1 if doesn't exist +int TestGetFileSize(const char *Filename); +std::string ConvertPaths(const std::string& rOriginal); +int RunCommand(const std::string& rCommandLine); +bool ServerIsAlive(int pid); +int ReadPidFile(const char *pidFile); +int LaunchServer(const std::string& rCommandLine, const char *pidFile); +int WaitForServerStartup(const char *pidFile, int pidIfKnown); + +#define TestRemoteProcessMemLeaks(filename) \ + TestRemoteProcessMemLeaksFunc(filename, __FILE__, __LINE__) + +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); + +#endif // TEST__H diff --git a/lib/common/Timer.cpp b/lib/common/Timer.cpp new file mode 100644 index 00000000..137ad45f --- /dev/null +++ b/lib/common/Timer.cpp @@ -0,0 +1,626 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Timer.cpp +// Purpose: Generic timers which execute arbitrary code when +// they expire. +// Created: 5/11/2006 +// +// -------------------------------------------------------------------------- + +#ifdef WIN32 + #define _WIN32_WINNT 0x0500 +#endif + +#include "Box.h" + +#include <signal.h> +#include <cstring> + +#include "Timer.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +std::vector<Timer*>* Timers::spTimers = NULL; +bool Timers::sRescheduleNeeded = false; + +#define TIMER_ID "timer " << mName << " (" << this << ") " +#define TIMER_ID_OF(t) "timer " << (t).GetName() << " (" << &(t) << ") " + +typedef void (*sighandler_t)(int); + +// -------------------------------------------------------------------------- +// +// 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) + { + BOX_ERROR("Failed to install signal handler"); + THROW_EXCEPTION(CommonException, Internal); + } + ASSERT(oldact.sa_handler == 0); + #endif // WIN32 && !PLATFORM_CYGWIN + + spTimers = new std::vector<Timer*>; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: static void Timers::Cleanup() +// Purpose: Clean up timers, stop signal handler +// Created: 6/11/2006 +// +// -------------------------------------------------------------------------- +void Timers::Cleanup() +{ + ASSERT(spTimers); + if (!spTimers) + { + BOX_ERROR("Tried to clean up timers when not initialised!"); + return; + } + + #if defined WIN32 && ! defined PLATFORM_CYGWIN + // no cleanup needed + #else + struct itimerval timeout; + memset(&timeout, 0, sizeof(timeout)); + + int result = ::setitimer(ITIMER_REAL, &timeout, NULL); + ASSERT(result == 0); + + struct sigaction newact, oldact; + newact.sa_handler = SIG_DFL; + newact.sa_flags = SA_RESTART; + sigemptyset(&(newact.sa_mask)); + if (::sigaction(SIGALRM, &newact, &oldact) != 0) + { + BOX_ERROR("Failed to remove signal handler"); + THROW_EXCEPTION(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); + ASSERT(&rTimer); + 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) +{ + ASSERT(spTimers); + ASSERT(&rTimer); + + bool restart = true; + while (restart) + { + restart = false; + + for (std::vector<Timer*>::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(); + } +} + +#define FORMAT_MICROSECONDS(t) \ + (int)(t / 1000000) << "." << \ + (int)(t % 1000000) << " seconds" + +// -------------------------------------------------------------------------- +// +// 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, Internal) + } + + #ifndef WIN32 + struct sigaction oldact; + if (::sigaction(SIGALRM, NULL, &oldact) != 0) + { + BOX_ERROR("Failed to check signal handler"); + THROW_EXCEPTION(CommonException, Internal) + } + + ASSERT(oldact.sa_handler == Timers::SignalHandler); + + if (oldact.sa_handler != Timers::SignalHandler) + { + BOX_ERROR("Signal handler was " << + (void *)oldact.sa_handler << + ", expected " << + (void *)Timers::SignalHandler); + THROW_EXCEPTION(CommonException, Internal) + } + #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(); + + // 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; + + for (std::vector<Timer*>::iterator i = spTimers->begin(); + i != spTimers->end(); i++) + { + Timer& rTimer = **i; + int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow; + + if (timeToExpiry <= 0) + { + /* + BOX_TRACE("timer " << *i << " has expired, " + "triggering it"); + */ + BOX_TRACE(TIMER_ID_OF(**i) "has expired, " + "triggering " << + FORMAT_MICROSECONDS(-timeToExpiry) << + " late"); + rTimer.OnExpire(); + spTimers->erase(i); + restart = true; + break; + } + else + { + /* + BOX_TRACE("timer " << *i << " has not " + "expired, triggering in " << + FORMAT_MICROSECONDS(timeToExpiry) << + " seconds"); + */ + } + } + } + + // Now the only remaining timers should all be in the future. + // Scan to find the next one to fire (earliest deadline). + + int64_t timeToNextEvent = 0; + std::string nameOfNextEvent; + + for (std::vector<Timer*>::iterator i = spTimers->begin(); + i != spTimers->end(); i++) + { + Timer& rTimer = **i; + int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow; + + ASSERT(timeToExpiry > 0) + if (timeToExpiry <= 0) + { + timeToExpiry = 1; + } + + if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry) + { + timeToNextEvent = timeToExpiry; + nameOfNextEvent = rTimer.GetName(); + } + } + + ASSERT(timeToNextEvent >= 0); + + if (timeToNextEvent == 0) + { + BOX_TRACE("timer: no more events, going to sleep."); + } + else + { + BOX_TRACE("timer: next event: " << nameOfNextEvent << + " expires in " << FORMAT_MICROSECONDS(timeToNextEvent)); + } + + struct itimerval timeout; + memset(&timeout, 0, sizeof(timeout)); + + timeout.it_value.tv_sec = BoxTimeToSeconds(timeToNextEvent); + timeout.it_value.tv_usec = (int) + (BoxTimeToMicroSeconds(timeToNextEvent) + % MICRO_SEC_IN_SEC); + + if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0) + { + BOX_ERROR("Failed to initialise system timer\n"); + THROW_EXCEPTION(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) +{ + // ASSERT(spTimers); + Timers::RequestReschedule(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Timer(size_t timeoutSecs, +// const std::string& rName) +// Purpose: Standard timer constructor, takes a timeout in +// seconds from now, and an optional name for +// logging purposes. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer::Timer(size_t timeoutSecs, const std::string& rName) +: mExpires(GetCurrentBoxTime() + SecondsToBoxTime(timeoutSecs)), + mExpired(false), + mName(rName) +#ifdef WIN32 +, mTimerHandle(INVALID_HANDLE_VALUE) +#endif +{ + #ifndef BOX_RELEASE_BUILD + if (timeoutSecs == 0) + { + BOX_TRACE(TIMER_ID "initialised for " << timeoutSecs << + " secs, will not fire"); + } + else + { + BOX_TRACE(TIMER_ID "initialised for " << timeoutSecs << + " secs, to fire at " << FormatTime(mExpires, false, true)); + } + #endif + + if (timeoutSecs == 0) + { + mExpires = 0; + } + else + { + Timers::Add(*this); + Start(timeoutSecs * MICRO_SEC_IN_SEC_LL); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Start() +// Purpose: This internal function initialises an OS TimerQueue +// timer on Windows, while on Unixes there is only a +// single global timer, managed by the Timers class, +// so this method does nothing. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::Start() +{ +#ifdef WIN32 + box_time_t timeNow = GetCurrentBoxTime(); + int64_t timeToExpiry = mExpires - timeNow; + + if (timeToExpiry <= 0) + { + BOX_WARNING(TIMER_ID << "fudging expiry from -" << + FORMAT_MICROSECONDS(-timeToExpiry)) + timeToExpiry = 1; + } + + Start(timeToExpiry); +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Start(int64_t delayInMicros) +// Purpose: This internal function initialises an OS TimerQueue +// timer on Windows, with a specified delay already +// calculated to save us doing it again. Like +// Timer::Start(), on Unixes it does nothing. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::Start(int64_t delayInMicros) +{ +#ifdef WIN32 + // only call me once! + ASSERT(mTimerHandle == INVALID_HANDLE_VALUE); + + int64_t delayInMillis = delayInMicros / 1000; + + // Windows XP always seems to fire timers up to 20 ms late, + // at least on my test laptop. Not critical in practice, but our + // tests are precise enough that they will fail if we don't + // correct for it. + delayInMillis -= 20; + + // Set a system timer to call our timer routine + if (CreateTimerQueueTimer(&mTimerHandle, NULL, TimerRoutine, + (PVOID)this, delayInMillis, 0, WT_EXECUTEINTIMERTHREAD) + == FALSE) + { + BOX_ERROR(TIMER_ID "failed to create timer: " << + GetErrorMessage(GetLastError())); + mTimerHandle = INVALID_HANDLE_VALUE; + } +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Stop() +// Purpose: This internal function deletes the associated OS +// TimerQueue timer on Windows, and on Unixes does +// nothing. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::Stop() +{ +#ifdef WIN32 + if (mTimerHandle != INVALID_HANDLE_VALUE) + { + if (DeleteTimerQueueTimer(NULL, mTimerHandle, + INVALID_HANDLE_VALUE) == FALSE) + { + BOX_ERROR(TIMER_ID "failed to delete timer: " << + GetErrorMessage(GetLastError())); + } + mTimerHandle = INVALID_HANDLE_VALUE; + } +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::~Timer() +// Purpose: Destructor for Timer objects. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer::~Timer() +{ + #ifndef BOX_RELEASE_BUILD + BOX_TRACE(TIMER_ID "destroyed"); + #endif + + Timers::Remove(*this); + Stop(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::Timer(Timer& rToCopy) +// Purpose: Copy constructor for Timer objects. Creates a new +// timer that will trigger at the same time as the +// original. The original will usually be discarded. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer::Timer(const Timer& rToCopy) +: mExpires(rToCopy.mExpires), + mExpired(rToCopy.mExpired), + mName(rToCopy.mName) +#ifdef WIN32 +, mTimerHandle(INVALID_HANDLE_VALUE) +#endif +{ + #ifndef BOX_RELEASE_BUILD + if (mExpired) + { + BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " + "already expired, will not fire"); + } + else if (mExpires == 0) + { + BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " + "no expiry, will not fire"); + } + else + { + BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " + "to fire at " << + (int)(mExpires / 1000000) << "." << + (int)(mExpires % 1000000)); + } + #endif + + if (!mExpired && mExpires != 0) + { + Timers::Add(*this); + Start(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::operator=(const Timer& rToCopy) +// Purpose: Assignment operator for Timer objects. Works +// exactly the same as the copy constructor, except +// that if the receiving timer is already running, +// it is stopped first. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +Timer& Timer::operator=(const Timer& rToCopy) +{ + #ifndef BOX_RELEASE_BUILD + if (rToCopy.mExpired) + { + BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " + "already expired, will not fire"); + } + else if (rToCopy.mExpires == 0) + { + BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " + "no expiry, will not fire"); + } + else + { + BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " + "to fire at " << + (int)(rToCopy.mExpires / 1000000) << "." << + (int)(rToCopy.mExpires % 1000000)); + } + #endif + + Timers::Remove(*this); + Stop(); + + mExpires = rToCopy.mExpires; + mExpired = rToCopy.mExpired; + mName = rToCopy.mName; + + if (!mExpired && mExpires != 0) + { + Timers::Add(*this); + Start(); + } + + return *this; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: Timer::OnExpire() +// Purpose: Method called by Timers::Reschedule (on Unixes) +// on next poll after timer expires, or from +// Timer::TimerRoutine (on Windows) from a separate +// thread managed by the OS. Marks the timer as +// expired for future reference. +// Created: 27/07/2008 +// +// -------------------------------------------------------------------------- + +void Timer::OnExpire() +{ + #ifndef 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..42b2e00f --- /dev/null +++ b/lib/common/Timer.h @@ -0,0 +1,89 @@ +// -------------------------------------------------------------------------- +// +// 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 <sys/time.h> +#endif + +#include <vector> + +#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<Timer*>* spTimers; + static void Reschedule(); + + static bool sRescheduleNeeded; + static void SignalHandler(int iUnused); + + public: + static void Init(); + static void Cleanup(); + static void Add (Timer& rTimer); + static void Remove(Timer& rTimer); + static void RequestReschedule(); + static void RescheduleIfNeeded(); +}; + +class Timer +{ +public: + Timer(size_t timeoutSecs, 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; } + +private: + box_time_t mExpires; + bool mExpired; + std::string mName; + + void Start(); + void Start(int64_t delayInMicros); + void Stop(); + + #ifdef WIN32 + HANDLE mTimerHandle; + static VOID CALLBACK TimerRoutine(PVOID lpParam, + BOOLEAN TimerOrWaitFired); + #endif +}; + +#include "MemLeakFindOff.h" + +#endif // TIMER__H diff --git a/lib/common/UnixUser.cpp b/lib/common/UnixUser.cpp new file mode 100644 index 00000000..f81b474c --- /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 <pwd.h> +#endif + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#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 char *Username) + : mUID(0), + mGID(0), + mRevertOnDestruction(false) +{ + // Get password info + struct passwd *pwd = ::getpwnam(Username); + 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..c895eb2a --- /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 char *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..6f21330d --- /dev/null +++ b/lib/common/Utils.cpp @@ -0,0 +1,315 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Utils.cpp +// Purpose: Utility function +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include <cstdlib> + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION + #include <execinfo.h> + #include <stdlib.h> +#endif + +#ifdef HAVE_CXXABI_H + #include <cxxabi.h> +#endif + +#include "Utils.h" +#include "CommonException.h" +#include "Logging.h" + +#include "MemLeakFindOn.h" + +std::string GetBoxBackupVersion() +{ + return BOX_VERSION; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: SplitString(const std::string &, char, std::vector<std::string> &) +// Purpose: Splits a string at a given character +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void SplitString(const std::string &String, char SplitOn, std::vector<std::string> &rOutput) +{ + // Split it up. + std::string::size_type b = 0; + std::string::size_type e = 0; + while(e = String.find_first_of(SplitOn, b), e != String.npos) + { + // Get this string + unsigned int len = e - b; + if(len >= 1) + { + rOutput.push_back(String.substr(b, len)); + } + b = e + 1; + } + // Last string + if(b < String.size()) + { + rOutput.push_back(String.substr(b)); + } +/*#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*/ +} + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION +void DumpStackBacktrace() +{ + void *array[10]; + size_t size = backtrace (array, 10); + char **strings = backtrace_symbols (array, size); + + BOX_TRACE("Obtained " << size << " stack frames."); + + for(size_t i = 0; i < size; i++) + { + // Demangling code copied from + // cctbx_sources/boost_adaptbx/meta_ext.cpp, BSD license + + std::string mangled_frame = strings[i]; + std::string output_frame = strings[i]; // default + + #ifdef HAVE_CXXABI_H + int start = mangled_frame.find('('); + int end = mangled_frame.find('+', start); + std::string mangled_func = mangled_frame.substr(start + 1, + end - start - 1); + + int status; + +#include "MemLeakFindOff.h" + char* result = abi::__cxa_demangle(mangled_func.c_str(), + NULL, NULL, &status); +#include "MemLeakFindOn.h" + + if (result == NULL) + { + if (status == 0) + { + BOX_WARNING("Demangle failed but no error: " << + mangled_func); + } + else if (status == -1) + { + BOX_WARNING("Demangle failed with " + "memory allocation error: " << + mangled_func); + } + else if (status == -2) + { + // Probably non-C++ name, don't demangle + /* + BOX_WARNING("Demangle failed with " + "with invalid name: " << + mangled_func); + */ + } + else if (status == -3) + { + BOX_WARNING("Demangle failed with " + "with invalid argument: " << + mangled_func); + } + else + { + BOX_WARNING("Demangle failed with " + "with unknown error " << status << + ": " << mangled_func); + } + } + else + { + output_frame = mangled_frame.substr(0, start + 1) + + result + mangled_frame.substr(end); +#include "MemLeakFindOff.h" + std::free(result); +#include "MemLeakFindOn.h" + } + #endif // HAVE_CXXABI_H + + BOX_TRACE("Stack frame " << i << ": " << output_frame); + } + +#include "MemLeakFindOff.h" + std::free (strings); +#include "MemLeakFindOn.h" +} +#endif + + + +// -------------------------------------------------------------------------- +// +// 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..8d98a520 --- /dev/null +++ b/lib/common/Utils.h @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Utils.h +// Purpose: Utility function +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef UTILS__H +#define UTILS__H + +#include <string> +#include <vector> + +#include "MemLeakFindOn.h" + +std::string GetBoxBackupVersion(); + +void SplitString(const std::string &String, char SplitOn, std::vector<std::string> &rOutput); + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION + void DumpStackBacktrace(); +#endif + +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); + +#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 <unistd.h> +#endif + +#include <errno.h> +#include <string.h> + +#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 <cstdlib> + +#ifdef HAVE_KQUEUE + #include <sys/event.h> + #include <sys/time.h> +#else + #include <vector> + #ifndef WIN32 + #include <poll.h> + #endif +#endif + +#include <cstdlib> + +#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<typename T> + 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<typename T> + 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<ItemInfo>::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<ItemInfo> 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..9d87d76a --- /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 <string.h> + +#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) +{ + 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 false; +} + +// -------------------------------------------------------------------------- +// +// 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..0119045b --- /dev/null +++ b/lib/common/ZeroStream.h @@ -0,0 +1,39 @@ +// -------------------------------------------------------------------------- +// +// 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); + 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..76b9b02b --- /dev/null +++ b/lib/common/makeexception.pl.in @@ -0,0 +1,283 @@ +#!@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(<EXCEPTION_DESC>) +{ + 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) + { + } + + ${class}Exception(const ${class}Exception &rToCopy) + : mSubType(rToCopy.mSubType), mMessage(rToCopy.mMessage) + { + } + + ~${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; + } + +private: + unsigned int mSubType; + std::string mMessage; +}; + +#endif // $guardname +__E + +# ----------------------------------------------------------------------------------------------------------- + +print CPP <<__E; + +// Auto-generated file -- do not edit + +#include "Box.h" +#include "autogen_${class}Exception.h" + +#include "MemLeakFindOn.h" + +#ifdef EXCEPTION_CODENAMES_EXTENDED + #ifdef EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION +static const char *whats[] = { +__E + +my $last_seen = -1; +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + for(my $s = $last_seen + 1; $s < $e; $s++) + { + print CPP "\t\"UNUSED\",\n" + } + my $ext = ($exception_desc[$e] ne '')?" ($exception_desc[$e])":''; + print CPP "\t\"${class} ".$exception[$e].$ext.'"'.(($e==$#exception)?'':',')."\n"; + $last_seen = $e; + } +} + +print CPP <<__E; +}; + #else +static const char *whats[] = { +__E + +$last_seen = -1; +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + for(my $s = $last_seen + 1; $s < $e; $s++) + { + print CPP "\t\"UNUSED\",\n" + } + print CPP "\t\"${class} ".$exception[$e].'"'.(($e==$#exception)?'':',')."\n"; + $last_seen = $e; + } +} + +print CPP <<__E; +}; + #endif +#endif + +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() +{ +#ifdef EXCEPTION_CODENAMES_EXTENDED + if(mSubType < 0 || mSubType > (sizeof(whats) / sizeof(whats[0]))) + { + return "${class}"; + } + return whats[mSubType]; +#else + return "${class}"; +#endif +} + +__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(<CURRENT>) + { + 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(<CURRENT>) + { + 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; + + |