diff --git a/CMakeLists.txt b/CMakeLists.txt index edf5959..1ee65a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}.${ENCFS_PATCH}") set (ENCFS_SOVERSION "1.9") set (ENCFS_NAME "Encrypted Filesystem") +option(IWYU "Build with IWYU analyais." OFF) + set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") @@ -62,10 +64,6 @@ else () include_directories (${TINYXML_INCLUDE_DIR}) endif () -find_package (RLog REQUIRED) -add_definitions (-DRLOG_COMPONENT="encfs") -include_directories (${RLOG_INCLUDE_DIR}) - find_program (POD2MAN pod2man) include (FindGettext) @@ -95,6 +93,14 @@ check_function_exists_glibc (lchmod HAVE_LCHMOD) set (CMAKE_THREAD_PREFER_PTHREAD) find_package (Threads REQUIRED) +# Logging. +add_definitions (-DELPP_THREAD_SAFE -DELPP_DISABLE_DEFAULT_CRASH_HANDLING) +check_include_file_cxx (syslog.h HAVE_SYSLOG_H) +if (HAVE_SYSLOG_H) + message ("-- Enabled syslog logging support") + add_definitions(-DELPP_SYSLOG) +endif (HAVE_SYSLOG_H) + # Packaging config. set (CPACK_PACKAGE_NAME "encfs") set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR}) @@ -124,6 +130,7 @@ set(SOURCE_FILES encfs/Context.cpp encfs/DirNode.cpp encfs/encfs.cpp + encfs/Error.cpp encfs/FileIO.cpp encfs/FileNode.cpp encfs/FileUtils.cpp @@ -148,11 +155,21 @@ target_link_libraries(encfs ${FUSE_LIBRARIES} ${OPENSSL_LIBRARIES} ${TINYXML_LIBRARIES} - ${RLOG_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) install (TARGETS encfs DESTINATION lib) +if (IWYU) + if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.2) + find_program(iwyu_path NAMES include-what-you-use iwyu) + if (iwyu_path) + message ("-- Enabled IWYU") + set_property(TARGET encfs PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${iwyu_path}) + endif() + endif() +endif() + + # Set RPATH to library install path. set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") diff --git a/INSTALL.md b/INSTALL.md index e3a7f3c..85cc8f3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -41,13 +41,16 @@ Dependencies EncFS depends on a number of libraries: - openssl fuse tinyxml2 gettext libintl librlog + * fuse : the userspace filesystem layer + * openssl : used for cryptographic primitives + * tinyxml2 : for reading and writing XML configuration files + * gettext : internationalization support + * libintl : internationalization support Compiling on Debian and Ubuntu ============================== -We use separate [Drone.io](https://drone.io/) and [CircleCi](https://circleci.com/) builds to automatically -test every commit. See the README.md file for current build status. +See the automated build static in README.md for current build status on Ubuntu systems. -The build configuration files (.drone.yml and circle.yml) therefore -always contains up-to-date instructions to build EncFS on Ubuntu distributions. +The build configuration files (.drone.yml and circle.yml) always contains up-to-date +instructions to build EncFS on Ubuntu distributions. diff --git a/ci/config.sh b/ci/config.sh index 96c4ee3..c6c414a 100755 --- a/ci/config.sh +++ b/ci/config.sh @@ -2,4 +2,4 @@ set -x set -e mkdir build cd build -cmake -DCMAKE_INSTALL_PREFIX:PATH=/tmp/encfs -DCMAKE_BUILD_TYPE=Debug .. +cmake -DCMAKE_INSTALL_PREFIX:PATH=/tmp/encfs -DCMAKE_BUILD_TYPE=Debug -DMINIGLOG=ON .. diff --git a/circle.yml b/circle.yml index cefc163..c570296 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,7 @@ machine: dependencies: pre: - - sudo apt-get install cmake libfuse-dev librlog-dev libgettextpo-dev + - sudo apt-get install cmake libfuse-dev libgettextpo-dev - bash ./ci/install-gcc.sh test: diff --git a/cmake/FindRLog.cmake b/cmake/FindRLog.cmake deleted file mode 100644 index 87e1173..0000000 --- a/cmake/FindRLog.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# FindRLog -# -------- -# -# Find RLog -# -# Find the RLog logging library. This module defines -# -# :: -# -# RLOG_INCLUDE_DIR, where to find rlog.h, etc. -# RLOG_LIBRARIES, the libraries needed to use RLog. -# RLOG_FOUND, If false, do not try to use RLog. - -find_path(RLOG_INCLUDE_DIR rlog/rlog.h) - -set(RLOG_NAMES ${RLOG_NAMES} rlog librlog) -find_library(RLOG_LIBRARY NAMES ${RLOG_NAMES} ) - -# handle the QUIETLY and REQUIRED arguments and set RLOG_FOUND to TRUE if -# all listed variables are TRUE -include (FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(RLOG DEFAULT_MSG RLOG_LIBRARY RLOG_INCLUDE_DIR) - -if(RLOG_FOUND) - set(RLOG_LIBRARIES ${RLOG_LIBRARY}) -endif() - -mark_as_advanced(RLOG_LIBRARY RLOG_INCLUDE_DIR ) diff --git a/drone-config/worker/Dockerfile b/drone-config/worker/Dockerfile index 6dbf9a4..96fd1a0 100644 --- a/drone-config/worker/Dockerfile +++ b/drone-config/worker/Dockerfile @@ -32,7 +32,6 @@ RUN apt-get -y upgrade \ git \ libfuse-dev \ libssl-dev \ - librlog-dev \ gettext \ libgettextpo-dev \ && apt-get clean diff --git a/encfs/BlockFileIO.cpp b/encfs/BlockFileIO.cpp index 73da4dd..032217d 100644 --- a/encfs/BlockFileIO.cpp +++ b/encfs/BlockFileIO.cpp @@ -20,16 +20,15 @@ #include "BlockFileIO.h" -#include -#include -#include -#include +#include // for memset, memcpy, NULL -#include "FSConfig.h" -#include "FileIO.h" -#include "FileUtils.h" -#include "MemoryPool.h" -#include "i18n.h" +#include "Error.h" +#include "FSConfig.h" // for FSConfigPtr +#include "FileIO.h" // for IORequest, FileIO +#include "FileUtils.h" // for EncFS_Opts +#include "MemoryPool.h" // for MemBlock, release, allocation + +namespace encfs { template inline Type min(Type A, Type B) { @@ -43,7 +42,7 @@ static void clearCache(IORequest &req, int blockSize) { BlockFileIO::BlockFileIO(int blockSize, const FSConfigPtr &cfg) : _blockSize(blockSize), _allowHoles(cfg->config->allowHoles) { - rAssert(_blockSize > 1); + CHECK(_blockSize > 1); _cache.data = new unsigned char[_blockSize]; _noCache = cfg->opts->noCache; } @@ -60,9 +59,8 @@ BlockFileIO::~BlockFileIO() { * returned data as neccessary. */ ssize_t BlockFileIO::cacheReadOneBlock(const IORequest &req) const { - - rAssert(req.dataLen <= _blockSize); - rAssert(req.offset % _blockSize == 0); + CHECK(req.dataLen <= _blockSize); + CHECK(req.offset % _blockSize == 0); /* we can satisfy the request even if _cache.dataLen is too short, because * we always request a full block during reads. This just means we are @@ -115,7 +113,7 @@ bool BlockFileIO::cacheWriteOneBlock(const IORequest &req) { * lower layer. */ ssize_t BlockFileIO::read(const IORequest &req) const { - rAssert(_blockSize != 0); + CHECK(_blockSize != 0); int partialOffset = req.offset % _blockSize; off_t blockNum = req.offset / _blockSize; @@ -149,11 +147,10 @@ ssize_t BlockFileIO::read(const IORequest &req) const { } ssize_t readSize = cacheReadOneBlock(blockReq); - if (unlikely(readSize <= partialOffset)) - break; // didn't get enough bytes + if (readSize <= partialOffset) break; // didn't get enough bytes int cpySize = min((size_t)(readSize - partialOffset), size); - rAssert(cpySize <= readSize); + CHECK(cpySize <= readSize); // if we read to a temporary buffer, then move the data if (blockReq.data != out) @@ -165,7 +162,7 @@ ssize_t BlockFileIO::read(const IORequest &req) const { ++blockNum; partialOffset = 0; - if (unlikely(readSize < _blockSize)) break; + if (readSize < _blockSize) break; } if (mb.data) MemoryPool::release(mb); @@ -175,11 +172,10 @@ ssize_t BlockFileIO::read(const IORequest &req) const { } bool BlockFileIO::write(const IORequest &req) { - rAssert(_blockSize != 0); + CHECK(_blockSize != 0); off_t fileSize = getSize(); - if (fileSize < 0) - return false; + if (fileSize < 0) return false; // where write request begins off_t blockNum = req.offset / _blockSize; @@ -301,7 +297,7 @@ void BlockFileIO::padFile(off_t oldSize, off_t newSize, bool forceWrite) { cacheWriteOneBlock(req); } } else - rDebug("optimization: not padding last block"); + VLOG(1) << "optimization: not padding last block"; } else { mb = MemoryPool::allocate(_blockSize); req.data = mb.data; @@ -315,7 +311,7 @@ void BlockFileIO::padFile(off_t oldSize, off_t newSize, bool forceWrite) { // 1. req.dataLen == 0, iff oldSize was already a multiple of blocksize if (req.dataLen != 0) { - rDebug("padding block %" PRIi64, oldLastBlock); + VLOG(1) << "padding block " << oldLastBlock; memset(mb.data, 0, _blockSize); cacheReadOneBlock(req); req.dataLen = _blockSize; // expand to full block size @@ -326,7 +322,7 @@ void BlockFileIO::padFile(off_t oldSize, off_t newSize, bool forceWrite) { // 2, pad zero blocks unless holes are allowed if (!_allowHoles) { for (; oldLastBlock != newLastBlock; ++oldLastBlock) { - rDebug("padding block %" PRIi64, oldLastBlock); + VLOG(1) << "padding block " << oldLastBlock; req.offset = oldLastBlock * _blockSize; req.dataLen = _blockSize; memset(mb.data, 0, req.dataLen); @@ -386,8 +382,8 @@ int BlockFileIO::truncateBase(off_t size, FileIO *base) { if ((rdSz < 0) || (!wrRes)) { // rwarning - unlikely to ever occur.. - rWarning(_("truncate failure: read %i bytes, partial block of %i"), - (int)rdSz, partialBlock); + RLOG(WARNING) << "truncate failure: read " << rdSz + << " bytes, partial block of " << partialBlock; } MemoryPool::release(mb); @@ -399,3 +395,5 @@ int BlockFileIO::truncateBase(off_t size, FileIO *base) { return res; } + +} // namespace encfs diff --git a/encfs/BlockFileIO.h b/encfs/BlockFileIO.h index cbd6a7f..8eb4520 100644 --- a/encfs/BlockFileIO.h +++ b/encfs/BlockFileIO.h @@ -26,6 +26,8 @@ #include "FSConfig.h" #include "FileIO.h" +namespace encfs { + /* Implements block scatter / gather interface. Requires derived classes to implement readOneBlock() / writeOneBlock() at a minimum. @@ -65,4 +67,6 @@ class BlockFileIO : public FileIO { mutable IORequest _cache; }; +} // namespace encfs + #endif diff --git a/encfs/BlockNameIO.cpp b/encfs/BlockNameIO.cpp index 2f11a1a..0fdff9a 100644 --- a/encfs/BlockNameIO.cpp +++ b/encfs/BlockNameIO.cpp @@ -20,42 +20,37 @@ #include "BlockNameIO.h" -#include -#include #include +#include #include "Cipher.h" #include "CipherKey.h" +#include "Error.h" +#include "Interface.h" #include "NameIO.h" #include "base64.h" +#include "internal/easylogging++.h" #include "intl/gettext.h" -namespace rlog { -class RLogChannel; -} // namespace rlog +namespace encfs { -using namespace rlog; -using namespace rel; - -static RLogChannel *Info = DEF_CHANNEL("info/nameio", Log_Info); - -static shared_ptr NewBlockNameIO(const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key) { +static std::shared_ptr NewBlockNameIO( + const Interface &iface, const std::shared_ptr &cipher, + const CipherKey &key) { int blockSize = 8; if (cipher) blockSize = cipher->cipherBlockSize(); - return shared_ptr( + return std::shared_ptr( new BlockNameIO(iface, cipher, key, blockSize, false)); } -static shared_ptr NewBlockNameIO32(const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key) { +static std::shared_ptr NewBlockNameIO32( + const Interface &iface, const std::shared_ptr &cipher, + const CipherKey &key) { int blockSize = 8; if (cipher) blockSize = cipher->cipherBlockSize(); - return shared_ptr( + return std::shared_ptr( new BlockNameIO(iface, cipher, key, blockSize, true)); } @@ -99,9 +94,10 @@ Interface BlockNameIO::CurrentInterface(bool caseInsensitive) { return Interface("nameio/block", 4, 0, 2); } -BlockNameIO::BlockNameIO(const rel::Interface &iface, - const shared_ptr &cipher, const CipherKey &key, - int blockSize, bool caseInsensitiveEncoding) +BlockNameIO::BlockNameIO(const Interface &iface, + const std::shared_ptr &cipher, + const CipherKey &key, int blockSize, + bool caseInsensitiveEncoding) : _interface(iface.current()), _bs(blockSize), _cipher(cipher), @@ -191,8 +187,8 @@ int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, // don't bother trying to decode files which are too small if (decodedStreamLen < _bs) { - rDebug("Rejecting filename '%s'", encodedName); - throw ERROR("Filename too small to decode"); + VLOG(1) << "Rejecting filename " << encodedName; + throw Error("Filename too small to decode"); } BUFFER_INIT(tmpBuf, 32, (unsigned int)length); @@ -222,8 +218,9 @@ int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, // might happen if there is an error decoding.. if (padding > _bs || finalSize < 0) { - rDebug("padding, _bx, finalSize = %i, %i, %i", padding, _bs, finalSize); - throw ERROR("invalid padding size"); + VLOG(1) << "padding, _bx, finalSize = " << padding << ", " << _bs << ", " + << finalSize; + throw Error("invalid padding size"); } // copy out the result.. @@ -238,12 +235,14 @@ int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, BUFFER_RESET(tmpBuf); if (mac2 != mac) { - rDebug("checksum mismatch: expected %u, got %u", mac, mac2); - rDebug("on decode of %i bytes", finalSize); - throw ERROR("checksum mismatch in filename decode"); + VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2 + << " on decode of " << finalSize << " bytes"; + throw Error("checksum mismatch in filename decode"); } return finalSize; } bool BlockNameIO::Enabled() { return true; } + +} // namespace encfs diff --git a/encfs/BlockNameIO.h b/encfs/BlockNameIO.h index 062ecea..4ea0594 100644 --- a/encfs/BlockNameIO.h +++ b/encfs/BlockNameIO.h @@ -21,13 +21,14 @@ #ifndef _BlockNameIO_incl_ #define _BlockNameIO_incl_ -#include #include +#include // for uint64_t -#include "CipherKey.h" -#include "Interface.h" -#include "NameIO.h" -#include "shared_ptr.h" +#include "CipherKey.h" // for CipherKey +#include "Interface.h" // for Interface +#include "NameIO.h" // for NameIO + +namespace encfs { class Cipher; @@ -38,14 +39,14 @@ class Cipher; */ class BlockNameIO : public NameIO { public: - static rel::Interface CurrentInterface(bool caseInsensitive = false); + static Interface CurrentInterface(bool caseInsensitive = false); - BlockNameIO(const rel::Interface &iface, const shared_ptr &cipher, + BlockNameIO(const Interface &iface, const std::shared_ptr &cipher, const CipherKey &key, int blockSize, bool caseInsensitiveEncoding = false); virtual ~BlockNameIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual int maxEncodedNameLen(int plaintextNameLen) const; virtual int maxDecodedNameLen(int encodedNameLen) const; @@ -62,9 +63,11 @@ class BlockNameIO : public NameIO { private: int _interface; int _bs; - shared_ptr _cipher; + std::shared_ptr _cipher; CipherKey _key; bool _caseInsensitive; }; +} // namespace encfs + #endif diff --git a/encfs/Cipher.cpp b/encfs/Cipher.cpp index c8e9101..fa84671 100644 --- a/encfs/Cipher.cpp +++ b/encfs/Cipher.cpp @@ -18,10 +18,10 @@ * along with this program. If not, see . */ -#include #include #include #include +#include #include #include @@ -36,7 +36,8 @@ #include "base64.h" using namespace std; -using namespace rel; + +namespace encfs { #define REF_MODULE(TYPE) \ if (!TYPE::Enabled()) cerr << "referenceModule: should never happen\n"; @@ -110,9 +111,8 @@ bool Cipher::Register(const char *name, const char *description, gCipherMap->insert(make_pair(string(name), ca)); return true; } - -shared_ptr Cipher::New(const string &name, int keyLen) { - shared_ptr result; +std::shared_ptr Cipher::New(const string &name, int keyLen) { + std::shared_ptr result; if (gCipherMap) { CipherMap_t::const_iterator it = gCipherMap->find(name); @@ -125,9 +125,8 @@ shared_ptr Cipher::New(const string &name, int keyLen) { return result; } - -shared_ptr Cipher::New(const Interface &iface, int keyLen) { - shared_ptr result; +std::shared_ptr Cipher::New(const Interface &iface, int keyLen) { + std::shared_ptr result; if (gCipherMap) { CipherMap_t::const_iterator it; CipherMap_t::const_iterator mapEnd = gCipherMap->end(); @@ -199,3 +198,5 @@ string Cipher::encodeAsString(const CipherKey &key, return string((const char *)b64Key); } + +} // namespace encfs diff --git a/encfs/Cipher.h b/encfs/Cipher.h index c21ea69..89ed4ec 100644 --- a/encfs/Cipher.h +++ b/encfs/Cipher.h @@ -22,9 +22,9 @@ #define _Cipher_incl_ #include -#include #include #include +#include #include #include "CipherKey.h" @@ -32,6 +32,8 @@ #include "Range.h" #include "encfs.h" +namespace encfs { + /* Mostly pure virtual interface defining operations on a cipher. @@ -42,13 +44,13 @@ class Cipher { public: // if no key length was indicated when cipher was registered, then keyLen // <= 0 will be used. - typedef shared_ptr(*CipherConstructor)(const rel::Interface &iface, - int keyLenBits); + typedef std::shared_ptr (*CipherConstructor)(const Interface &iface, + int keyLenBits); struct CipherAlgorithm { std::string name; std::string description; - rel::Interface iface; + Interface iface; Range keyLength; Range blockSize; }; @@ -56,21 +58,22 @@ class Cipher { typedef std::list AlgorithmList; static AlgorithmList GetAlgorithmList(bool includeHidden = false); - static shared_ptr New(const rel::Interface &iface, int keyLen = -1); - static shared_ptr New(const std::string &cipherName, int keyLen = -1); + static std::shared_ptr New(const Interface &iface, int keyLen = -1); + static std::shared_ptr New(const std::string &cipherName, + int keyLen = -1); static bool Register(const char *cipherName, const char *description, - const rel::Interface &iface, - CipherConstructor constructor, bool hidden = false); + const Interface &iface, CipherConstructor constructor, + bool hidden = false); static bool Register(const char *cipherName, const char *description, - const rel::Interface &iface, const Range &keyLength, + const Interface &iface, const Range &keyLength, const Range &blockSize, CipherConstructor constructor, bool hidden = false); Cipher(); virtual ~Cipher(); - virtual rel::Interface interface() const = 0; + virtual Interface interface() const = 0; // create a new key based on a password // if iterationCount == 0, then iteration count will be determined @@ -150,4 +153,6 @@ class Cipher { const CipherKey &key) const = 0; }; +} // namespace encfs + #endif diff --git a/encfs/CipherFileIO.cpp b/encfs/CipherFileIO.cpp index 2de0eaa..1a16dac 100644 --- a/encfs/CipherFileIO.cpp +++ b/encfs/CipherFileIO.cpp @@ -20,40 +20,34 @@ #include "CipherFileIO.h" +#include "internal/easylogging++.h" +#include #include #include +#include #include -#include -#include #include #include -#include #include "BlockFileIO.h" #include "Cipher.h" #include "CipherKey.h" +#include "Error.h" #include "FileIO.h" +namespace encfs { + /* - Version 2:0 adds support for a per-file initialization vector with a fixed 8 byte header. The headers are enabled globally within a filesystem at the filesystem configuration level. When headers are disabled, 2:0 is compatible with version 1:0. */ -static rel::Interface CipherFileIO_iface("FileIO/Cipher", 2, 0, 1); +static Interface CipherFileIO_iface("FileIO/Cipher", 2, 0, 1); const int HEADER_SIZE = 8; // 64 bit initialization vector.. -static bool checkSize(int fsBlockSize, int cipherBlockSize) { - int blockBoundary = fsBlockSize % cipherBlockSize; - if (blockBoundary != 0) { - rError("CipherFileIO: blocks should be multiple of cipher block size"); - return true; - } else - return false; -} - -CipherFileIO::CipherFileIO(const shared_ptr &_base, +CipherFileIO::CipherFileIO(const std::shared_ptr &_base, const FSConfigPtr &cfg) : BlockFileIO(cfg->config->blockSize, cfg), base(_base), @@ -65,16 +59,13 @@ CipherFileIO::CipherFileIO(const shared_ptr &_base, cipher = cfg->cipher; key = cfg->key; - static bool warnOnce = false; - - if (!warnOnce) - warnOnce = checkSize(fsConfig->config->blockSize, - fsConfig->cipher->cipherBlockSize()); + CHECK_EQ(fsConfig->config->blockSize % fsConfig->cipher->cipherBlockSize(), 0) + << "FS block size must be multiple of cipher block size"; } CipherFileIO::~CipherFileIO() {} -rel::Interface CipherFileIO::interface() const { return CipherFileIO_iface; } +Interface CipherFileIO::interface() const { return CipherFileIO_iface; } int CipherFileIO::open(int flags) { int res = base->open(flags); @@ -91,17 +82,16 @@ void CipherFileIO::setFileName(const char *fileName) { const char *CipherFileIO::getFileName() const { return base->getFileName(); } bool CipherFileIO::setIV(uint64_t iv) { - rDebug("in setIV, current IV = %" PRIu64 ", new IV = %" PRIu64 - ", fileIV = %" PRIu64, - externalIV, iv, fileIV); + VLOG(1) << "in setIV, current IV = " << externalIV << ", new IV = " << iv + << ", fileIV = " << fileIV; if (externalIV == 0) { // we're just being told about which IV to use. since we haven't // initialized the fileIV, there is no need to just yet.. externalIV = iv; - if (fileIV != 0) - rWarning("fileIV initialized before externalIV! (%" PRIu64 ", %" PRIu64 - ")", - fileIV, externalIV); + if (fileIV != 0) { + RLOG(WARNING) << "fileIV initialized before externalIV: " << fileIV + << ", " << externalIV; + } } else if (haveHeader) { // we have an old IV, and now a new IV, so we need to update the fileIV // on disk. @@ -115,7 +105,7 @@ bool CipherFileIO::setIV(uint64_t iv) { externalIV = iv; return base->setIV(iv); } else { - rDebug("writeHeader failed to re-open for write"); + VLOG(1) << "writeHeader failed to re-open for write"; return false; } } @@ -185,7 +175,7 @@ void CipherFileIO::initHeader() { // create one. off_t rawSize = base->getSize(); if (rawSize >= HEADER_SIZE) { - rDebug("reading existing header, rawSize = %" PRIi64, rawSize); + VLOG(1) << "reading existing header, rawSize = " << rawSize; // has a header.. read it unsigned char buf[8] = {0}; @@ -202,18 +192,18 @@ void CipherFileIO::initHeader() { rAssert(fileIV != 0); // 0 is never used.. } else { - rDebug("creating new file IV header"); + VLOG(1) << "creating new file IV header"; unsigned char buf[8] = {0}; do { if (!cipher->randomize(buf, 8, false)) - throw ERROR("Unable to generate a random file IV"); + throw Error("Unable to generate a random file IV"); fileIV = 0; for (int i = 0; i < 8; ++i) fileIV = (fileIV << 8) | (uint64_t)buf[i]; if (fileIV == 0) - rWarning("Unexpected result: randomize returned 8 null bytes!"); + RLOG(WARNING) << "Unexpected result: randomize returned 8 null bytes!"; } while (fileIV == 0); // don't accept 0 as an option.. if (base->isWritable()) { @@ -225,10 +215,11 @@ void CipherFileIO::initHeader() { req.dataLen = 8; base->write(req); - } else - rDebug("base not writable, IV not written.."); + } else { + VLOG(1) << "base not writable, IV not written.."; + } } - rDebug("initHeader finished, fileIV = %" PRIu64, fileIV); + VLOG(1) << "initHeader finished, fileIV = " << fileIV; } bool CipherFileIO::writeHeader() { @@ -236,13 +227,15 @@ bool CipherFileIO::writeHeader() { // open for write.. int newFlags = lastFlags | O_RDWR; if (base->open(newFlags) < 0) { - rDebug("writeHeader failed to re-open for write"); + VLOG(1) << "writeHeader failed to re-open for write"; return false; } } - if (fileIV == 0) rError("Internal error: fileIV == 0 in writeHeader!!!"); - rDebug("writing fileIV %" PRIu64, fileIV); + if (fileIV == 0) { + RLOG(ERROR) << "Internal error: fileIV == 0 in writeHeader!!!"; + } + VLOG(1) << "writing fileIV " << fileIV; unsigned char buf[8] = {0}; for (int i = 0; i < 8; ++i) { @@ -282,7 +275,7 @@ void CipherFileIO::generateReverseHeader(unsigned char *headerBuf) { ino_t ino = stbuf.st_ino; rAssert(ino != 0); - rDebug("generating reverse file IV header from ino=%lu", (unsigned long)ino); + VLOG(1) << "generating reverse file IV header from ino=" << ino; // Serialize the inode number into inoBuf unsigned char inoBuf[sizeof(ino_t)]; @@ -305,7 +298,7 @@ void CipherFileIO::generateReverseHeader(unsigned char *headerBuf) { fileIV = (fileIV << 8) | (uint64_t)headerBuf[i]; } - rDebug("fileIV=%" PRIx64, fileIV); + VLOG(1) << "fileIV=" << fileIV; // Encrypt externally-visible header cipher->streamEncode(headerBuf, HEADER_SIZE, externalIV, key); @@ -335,19 +328,20 @@ ssize_t CipherFileIO::readOneBlock(const IORequest &req) const { const_cast(this)->initHeader(); if (readSize != bs) { - rDebug("streamRead(data, %d, IV)", (int)readSize); + VLOG(1) << "streamRead(data, " << readSize << ", IV)"; ok = streamRead(tmpReq.data, (int)readSize, blockNum ^ fileIV); } else { ok = blockRead(tmpReq.data, (int)readSize, blockNum ^ fileIV); } if (!ok) { - rDebug("decodeBlock failed for block %" PRIi64 ", size %i", blockNum, - (int)readSize); + VLOG(1) << "decodeBlock failed for block " << blockNum << ", size " + << readSize; readSize = -1; } - } else - rDebug("readSize zero for offset %" PRIi64, req.offset); + } else { + VLOG(1) << "readSize zero for offset " << req.offset; + } return readSize; } @@ -355,7 +349,8 @@ ssize_t CipherFileIO::readOneBlock(const IORequest &req) const { bool CipherFileIO::writeOneBlock(const IORequest &req) { if (haveHeader && fsConfig->reverseEncryption) { - rDebug("writing to a reverse mount with per-file IVs is not implemented"); + VLOG(1) + << "writing to a reverse mount with per-file IVs is not implemented"; return false; } @@ -379,8 +374,8 @@ bool CipherFileIO::writeOneBlock(const IORequest &req) { } else ok = base->write(req); } else { - rDebug("encodeBlock failed for block %" PRIi64 ", size %i", blockNum, - req.dataLen); + VLOG(1) << "encodeBlock failed for block " << blockNum << ", size " + << req.dataLen; ok = false; } return ok; @@ -388,7 +383,7 @@ bool CipherFileIO::writeOneBlock(const IORequest &req) { bool CipherFileIO::blockWrite(unsigned char *buf, int size, uint64_t _iv64) const { - rDebug("Called blockWrite"); + VLOG(1) << "Called blockWrite"; if (!fsConfig->reverseEncryption) return cipher->blockEncode(buf, size, _iv64, key); else @@ -397,7 +392,7 @@ bool CipherFileIO::blockWrite(unsigned char *buf, int size, bool CipherFileIO::streamWrite(unsigned char *buf, int size, uint64_t _iv64) const { - rDebug("Called streamWrite"); + VLOG(1) << "Called streamWrite"; if (!fsConfig->reverseEncryption) return cipher->streamEncode(buf, size, _iv64, key); else @@ -439,7 +434,7 @@ int CipherFileIO::truncate(off_t size) { // open for write.. int newFlags = lastFlags | O_RDWR; if (base->open(newFlags) < 0) - rDebug("writeHeader failed to re-open for write"); + VLOG(1) << "writeHeader failed to re-open for write"; } initHeader(); } @@ -461,13 +456,13 @@ ssize_t CipherFileIO::read(const IORequest &origReq) const { /* if reverse mode is not active with uniqueIV, * the read request is handled by the base class */ if (!(fsConfig->reverseEncryption && haveHeader)) { - rDebug("relaying request to base class: offset=%d, dataLen=%d", - origReq.offset, origReq.dataLen); + VLOG(1) << "relaying request to base class: offset=" << origReq.offset + << ", dataLen=" << origReq.dataLen; return BlockFileIO::read(origReq); } - rDebug("handling reverse unique IV read: offset=%d, dataLen=%d", - origReq.offset, origReq.dataLen); + VLOG(1) << "handling reverse unique IV read: offset=" << origReq.offset + << ", dataLen=" << origReq.dataLen; // generate the file IV header // this is needed in any case - without IV the file cannot be decoded @@ -489,7 +484,7 @@ ssize_t CipherFileIO::read(const IORequest &origReq) const { headerBytes = -req.offset; if (req.dataLen < headerBytes) headerBytes = req.dataLen; // only up to the number of bytes requested - rDebug("Adding %d header bytes", headerBytes); + VLOG(1) << "Adding " << headerBytes << " header bytes"; // copy the header bytes into the data int headerOffset = HEADER_SIZE - headerBytes; @@ -509,14 +504,16 @@ ssize_t CipherFileIO::read(const IORequest &origReq) const { // read the payload ssize_t readBytes = BlockFileIO::read(req); - rDebug("read %ld bytes from backing file", (long)readBytes); + VLOG(1) << "read " << readBytes << " bytes from backing file"; if (readBytes < 0) return readBytes; // Return error code else { ssize_t sum = headerBytes + readBytes; - rDebug("returning sum=%ld", (long)sum); + VLOG(1) << "returning sum=" << sum; return sum; } } bool CipherFileIO::isWritable() const { return base->isWritable(); } + +} // namespace encfs diff --git a/encfs/CipherFileIO.h b/encfs/CipherFileIO.h index e73e6ff..a1bfd84 100644 --- a/encfs/CipherFileIO.h +++ b/encfs/CipherFileIO.h @@ -22,9 +22,9 @@ #define _CipherFileIO_incl_ #include +#include #include #include -#include #include "BlockFileIO.h" #include "CipherKey.h" @@ -32,6 +32,8 @@ #include "FileUtils.h" #include "Interface.h" +namespace encfs { + class Cipher; class FileIO; struct IORequest; @@ -43,10 +45,10 @@ struct IORequest; */ class CipherFileIO : public BlockFileIO { public: - CipherFileIO(const shared_ptr &base, const FSConfigPtr &cfg); + CipherFileIO(const std::shared_ptr &base, const FSConfigPtr &cfg); virtual ~CipherFileIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual void setFileName(const char *fileName); virtual const char *getFileName() const; @@ -75,7 +77,7 @@ class CipherFileIO : public BlockFileIO { ssize_t read(const IORequest &req) const; - shared_ptr base; + std::shared_ptr base; FSConfigPtr fsConfig; @@ -86,8 +88,10 @@ class CipherFileIO : public BlockFileIO { uint64_t fileIV; int lastFlags; - shared_ptr cipher; + std::shared_ptr cipher; CipherKey key; }; +} // namespace encfs + #endif diff --git a/encfs/CipherKey.cpp b/encfs/CipherKey.cpp index ba2f779..bf3f168 100644 --- a/encfs/CipherKey.cpp +++ b/encfs/CipherKey.cpp @@ -20,6 +20,10 @@ #include "CipherKey.h" +namespace encfs { + AbstractCipherKey::AbstractCipherKey() {} AbstractCipherKey::~AbstractCipherKey() {} + +} // namespace encfs diff --git a/encfs/CipherKey.h b/encfs/CipherKey.h index 7054fd8..42ddb8a 100644 --- a/encfs/CipherKey.h +++ b/encfs/CipherKey.h @@ -23,7 +23,7 @@ #include -#include "shared_ptr.h" +namespace encfs { class AbstractCipherKey { public: @@ -31,6 +31,8 @@ class AbstractCipherKey { virtual ~AbstractCipherKey(); }; -typedef shared_ptr CipherKey; +typedef std::shared_ptr CipherKey; + +} // namespace encfs #endif diff --git a/encfs/ConfigReader.cpp b/encfs/ConfigReader.cpp index ee298c8..e622ecf 100644 --- a/encfs/ConfigReader.cpp +++ b/encfs/ConfigReader.cpp @@ -20,17 +20,18 @@ #include "ConfigReader.h" +#include #include -#include #include #include -#include #include #include "ConfigVar.h" +#include "Error.h" using namespace std; -using namespace rlog; + +namespace encfs { ConfigReader::ConfigReader() {} @@ -54,8 +55,8 @@ bool ConfigReader::load(const char *fileName) { close(fd); if (res != size) { - rWarning("Partial read of config file, expecting %i bytes, got %i", size, - res); + RLOG(WARNING) << "Partial read of config file, expecting " << size + << " bytes, got " << res; delete[] buf; return false; } @@ -78,7 +79,7 @@ bool ConfigReader::loadFromVar(ConfigVar &in) { in >> key >> value; if (key.length() == 0) { - rError("Invalid key encoding in buffer"); + RLOG(ERROR) << "Invalid key encoding in buffer"; return false; } ConfigVar newVar(value); @@ -97,11 +98,11 @@ bool ConfigReader::save(const char *fileName) const { int retVal = ::write(fd, out.buffer(), out.size()); close(fd); if (retVal != out.size()) { - rError("Error writing to config file %s", fileName); + RLOG(ERROR) << "Error writing to config file " << fileName; return false; } } else { - rError("Unable to open or create file %s", fileName); + RLOG(ERROR) << "Unable to open or create file " << fileName; return false; } @@ -135,3 +136,5 @@ ConfigVar ConfigReader::operator[](const std::string &varName) const { ConfigVar &ConfigReader::operator[](const std::string &varName) { return vars[varName]; } + +} // namespace encfs diff --git a/encfs/ConfigReader.h b/encfs/ConfigReader.h index b400013..994e3e3 100644 --- a/encfs/ConfigReader.h +++ b/encfs/ConfigReader.h @@ -21,11 +21,13 @@ #ifndef _ConfigReader_incl_ #define _ConfigReader_incl_ -#include #include +#include #include "ConfigVar.h" +namespace encfs { + /* handles Configuration load / store for Encfs filesystems. @@ -61,4 +63,6 @@ class ConfigReader { std::map vars; }; +} // namespace encfs + #endif diff --git a/encfs/ConfigVar.cpp b/encfs/ConfigVar.cpp index 4c38941..a4fadc8 100644 --- a/encfs/ConfigVar.cpp +++ b/encfs/ConfigVar.cpp @@ -20,10 +20,12 @@ #include "ConfigVar.h" -#include +#include "internal/easylogging++.h" #include -using namespace rlog; +#include "Error.h" + +namespace encfs { #ifndef MIN inline int MIN(int a, int b) { return (a < b) ? a : b; } @@ -192,9 +194,12 @@ const ConfigVar &operator>>(const ConfigVar &src, std::string &result) { } if (readLen != length) { - rDebug("string encoded as size %i bytes, read %i", length, readLen); + VLOG(1) << "string encoded as size " << length << " bytes, read " + << readLen; } rAssert(readLen == length); return src; } + +} // namespace encfs diff --git a/encfs/ConfigVar.h b/encfs/ConfigVar.h index aad8a87..d1b73b0 100644 --- a/encfs/ConfigVar.h +++ b/encfs/ConfigVar.h @@ -24,7 +24,7 @@ #include #include -#include "shared_ptr.h" +namespace encfs { class ConfigVar { struct ConfigVarData { @@ -32,7 +32,7 @@ class ConfigVar { int offset; }; - shared_ptr pd; + std::shared_ptr pd; public: ConfigVar(); @@ -77,4 +77,6 @@ const ConfigVar &operator>>(const ConfigVar &, bool &); const ConfigVar &operator>>(const ConfigVar &, int &); const ConfigVar &operator>>(const ConfigVar &, std::string &str); +} // namespace encfs + #endif diff --git a/encfs/Context.cpp b/encfs/Context.cpp index 90a1973..348b2a9 100644 --- a/encfs/Context.cpp +++ b/encfs/Context.cpp @@ -18,15 +18,15 @@ * along with this program. If not, see . */ -#include +#include "internal/easylogging++.h" #include #include "Context.h" #include "DirNode.h" +#include "Error.h" #include "Mutex.h" -using namespace rel; -using namespace rlog; +namespace encfs { EncFS_Context::EncFS_Context() { pthread_cond_init(&wakeupCond, 0); @@ -44,9 +44,8 @@ EncFS_Context::~EncFS_Context() { // release all entries from map openFiles.clear(); } - -shared_ptr EncFS_Context::getRoot(int *errCode) { - shared_ptr ret; +std::shared_ptr EncFS_Context::getRoot(int *errCode) { + std::shared_ptr ret; do { { Lock lock(contextMutex); @@ -66,7 +65,7 @@ shared_ptr EncFS_Context::getRoot(int *errCode) { return ret; } -void EncFS_Context::setRoot(const shared_ptr &r) { +void EncFS_Context::setRoot(const std::shared_ptr &r) { Lock lock(contextMutex); root = r; @@ -89,8 +88,7 @@ int EncFS_Context::openFileCount() const { return openFiles.size(); } - -shared_ptr EncFS_Context::lookupNode(const char *path) { +std::shared_ptr EncFS_Context::lookupNode(const char *path) { Lock lock(contextMutex); FileMap::iterator it = openFiles.find(std::string(path)); @@ -99,7 +97,7 @@ shared_ptr EncFS_Context::lookupNode(const char *path) { // first return (*it->second.begin())->node; } else { - return shared_ptr(); + return std::shared_ptr(); } } @@ -113,14 +111,13 @@ void EncFS_Context::renameNode(const char *from, const char *to) { openFiles[std::string(to)] = val; } } - -shared_ptr EncFS_Context::getNode(void *pl) { +std::shared_ptr EncFS_Context::getNode(void *pl) { Placeholder *ph = (Placeholder *)pl; return ph->node; } void *EncFS_Context::putNode(const char *path, - const shared_ptr &node) { + const std::shared_ptr &node) { Lock lock(contextMutex); Placeholder *pl = new Placeholder(node); openFiles[std::string(path)].insert(pl); @@ -151,3 +148,5 @@ void EncFS_Context::eraseNode(const char *path, void *pl) { delete ph; } + +} // namespace encfs diff --git a/encfs/Context.h b/encfs/Context.h index 1d74883..1ddbfaa 100644 --- a/encfs/Context.h +++ b/encfs/Context.h @@ -21,15 +21,16 @@ #ifndef _Context_incl_ #define _Context_incl_ -#include #include +#include #include #include #include #include "encfs.h" -#include "shared_ptr.h" + +namespace encfs { class DirNode; class FileNode; @@ -41,24 +42,24 @@ class EncFS_Context { EncFS_Context(); ~EncFS_Context(); - shared_ptr getNode(void *ptr); - shared_ptr lookupNode(const char *path); + std::shared_ptr getNode(void *ptr); + std::shared_ptr lookupNode(const char *path); int getAndResetUsageCounter(); int openFileCount() const; - void *putNode(const char *path, const shared_ptr &node); + void *putNode(const char *path, const std::shared_ptr &node); void eraseNode(const char *path, void *placeholder); void renameNode(const char *oldName, const char *newName); - void setRoot(const shared_ptr &root); - shared_ptr getRoot(int *err); + void setRoot(const std::shared_ptr &root); + std::shared_ptr getRoot(int *err); bool isMounted(); - shared_ptr args; - shared_ptr opts; + std::shared_ptr args; + std::shared_ptr opts; bool publicFilesystem; // root path to cipher dir @@ -77,13 +78,13 @@ class EncFS_Context { * A FileNode may be opened many times, but only one FileNode instance per * file is kept. Rather then doing reference counting in FileNode, we * store a unique Placeholder for each open() until the corresponding - * release() is called. shared_ptr then does our reference counting for + * release() is called. std::shared_ptr then does our reference counting for * us. */ struct Placeholder { - shared_ptr node; + std::shared_ptr node; - Placeholder(const shared_ptr &ptr) : node(ptr) {} + Placeholder(const std::shared_ptr &ptr) : node(ptr) {} }; typedef std::unordered_map > FileMap; @@ -92,9 +93,11 @@ class EncFS_Context { FileMap openFiles; int usageCount; - shared_ptr root; + std::shared_ptr root; }; int remountFS(EncFS_Context *ctx); +} // namespace encfs + #endif diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp index 6ae343c..c0111c7 100644 --- a/encfs/DirNode.cpp +++ b/encfs/DirNode.cpp @@ -18,13 +18,13 @@ * along with this program. If not, see . */ +#include +#include #include #include #include #include #include -#include -#include #include "DirNode.h" #include "FSConfig.h" @@ -35,30 +35,24 @@ #include #endif -#include -#include +#include "internal/easylogging++.h" #include #include "Context.h" +#include "Error.h" #include "Mutex.h" -namespace rlog { -class RLogChannel; -} // namespace rlog - using namespace std; -using namespace rel; -using namespace rlog; -static RLogChannel *Info = DEF_CHANNEL("info/DirNode", Log_Info); +namespace encfs { class DirDeleter { public: void operator()(DIR *d) { ::closedir(d); } }; -DirTraverse::DirTraverse(const shared_ptr &_dirPtr, uint64_t _iv, - const shared_ptr &_naming) +DirTraverse::DirTraverse(const std::shared_ptr &_dirPtr, uint64_t _iv, + const std::shared_ptr &_naming) : dir(_dirPtr), iv(_iv), naming(_naming) {} DirTraverse::DirTraverse(const DirTraverse &src) @@ -78,7 +72,7 @@ DirTraverse::~DirTraverse() { naming.reset(); } -static bool _nextName(struct dirent *&de, const shared_ptr &dir, +static bool _nextName(struct dirent *&de, const std::shared_ptr &dir, int *fileType, ino_t *inode) { de = ::readdir(dir.get()); @@ -105,9 +99,9 @@ std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) { try { uint64_t localIv = iv; return naming->decodePath(de->d_name, &localIv); - } catch (rlog::Error &ex) { + } catch (encfs::Error &ex) { // .. .problem decoding, ignore it and continue on to next name.. - rDebug("error decoding filename: %s", de->d_name); + VLOG(1) << "error decoding filename: " << de->d_name; } } @@ -122,7 +116,7 @@ std::string DirTraverse::nextInvalid() { uint64_t localIv = iv; naming->decodePath(de->d_name, &localIv); continue; - } catch (rlog::Error &ex) { + } catch (encfs::Error &ex) { return string(de->d_name); } } @@ -145,11 +139,11 @@ struct RenameEl { class RenameOp { private: DirNode *dn; - shared_ptr > renameList; + std::shared_ptr > renameList; list::const_iterator last; public: - RenameOp(DirNode *_dn, const shared_ptr > &_renameList) + RenameOp(DirNode *_dn, const std::shared_ptr > &_renameList) : dn(_dn), renameList(_renameList) { last = renameList->begin(); } @@ -181,8 +175,7 @@ bool RenameOp::apply() { try { while (last != renameList->end()) { // backing store rename. - rDebug("renaming %s -> %s", last->oldCName.c_str(), - last->newCName.c_str()); + VLOG(1) << "renaming " << last->oldCName << " -> " << last->newCName; struct stat st; bool preserve_mtime = ::stat(last->oldCName.c_str(), &st) == 0; @@ -192,8 +185,8 @@ bool RenameOp::apply() { // rename on disk.. if (::rename(last->oldCName.c_str(), last->newCName.c_str()) == -1) { - rWarning("Error renaming %s: %s", last->oldCName.c_str(), - strerror(errno)); + RLOG(WARNING) << "Error renaming " << last->oldCName << ": " + << strerror(errno); dn->renameNode(last->newPName.c_str(), last->oldPName.c_str(), false); return false; } @@ -209,17 +202,17 @@ bool RenameOp::apply() { } return true; - } catch (rlog::Error &err) { - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(WARNING) << err.what(); return false; } } void RenameOp::undo() { - rDebug("in undoRename"); + VLOG(1) << "in undoRename"; if (last == renameList->begin()) { - rDebug("nothing to undo"); + VLOG(1) << "nothing to undo"; return; // nothing to undo } @@ -231,20 +224,19 @@ void RenameOp::undo() { while (it != renameList->begin()) { --it; - rDebug("undo: renaming %s -> %s", it->newCName.c_str(), - it->oldCName.c_str()); + VLOG(1) << "undo: renaming " << it->newCName << " -> " << it->oldCName; ::rename(it->newCName.c_str(), it->oldCName.c_str()); try { dn->renameNode(it->newPName.c_str(), it->oldPName.c_str(), false); - } catch (rlog::Error &err) { - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(WARNING) << err.what(); // continue on anyway... } ++undoCount; }; - rWarning("Undo rename count: %i", undoCount); + RLOG(WARNING) << "Undo rename count: " << undoCount; } DirNode::DirNode(EncFS_Context *_ctx, const string &sourceDir, @@ -254,7 +246,7 @@ DirNode::DirNode(EncFS_Context *_ctx, const string &sourceDir, Lock _lock(mutex); ctx = _ctx; - rootDir = sourceDir; // .. and fsConfig->opts->mountPoint have trailing slash + rootDir = sourceDir; // .. and fsConfig->opts->mountPoint have trailing slash fsConfig = _config; naming = fsConfig->nameCoding; @@ -273,7 +265,7 @@ string DirNode::rootDirectory() { return string(rootDir, 0, rootDir.length() - 1); } -bool DirNode::touchesMountpoint( const char *realPath ) const { +bool DirNode::touchesMountpoint(const char *realPath) const { const string &mountPoint = fsConfig->opts->mountPoint; // compare mountPoint up to the leading slash. // examples: @@ -336,10 +328,8 @@ string DirNode::plainPath(const char *cipherPath_) { // Default. return naming->decodePath(cipherPath_); - } catch (rlog::Error &err) { - rError("decode err: %s", err.message()); - err.log(_RLWarningChannel); - + } catch (encfs::Error &err) { + RLOG(ERROR) << "decode err: " << err.what(); return string(); } } @@ -354,33 +344,29 @@ string DirNode::relativeCipherPath(const char *plaintextPath) { } return naming->encodePath(plaintextPath); - } catch (rlog::Error &err) { - rError("encode err: %s", err.message()); - err.log(_RLWarningChannel); - + } catch (encfs::Error &err) { + RLOG(ERROR) << "encode err: " << err.what(); return string(); } } DirTraverse DirNode::openDir(const char *plaintextPath) { string cyName = rootDir + naming->encodePath(plaintextPath); - // rDebug("openDir on %s", cyName.c_str() ); DIR *dir = ::opendir(cyName.c_str()); if (dir == NULL) { - rDebug("opendir error %s", strerror(errno)); - return DirTraverse(shared_ptr(), 0, shared_ptr()); + VLOG(1) << "opendir error " << strerror(errno); + return DirTraverse(shared_ptr(), 0, std::shared_ptr()); } else { - shared_ptr dp(dir, DirDeleter()); + std::shared_ptr dp(dir, DirDeleter()); uint64_t iv = 0; // if we're using chained IV mode, then compute the IV at this // directory level.. try { if (naming->getChainedNameIV()) naming->encodePath(plaintextPath, &iv); - } catch (rlog::Error &err) { - rError("encode err: %s", err.message()); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "encode err: " << err.what(); } return DirTraverse(dp, iv, naming); } @@ -401,9 +387,9 @@ bool DirNode::genRenameList(list &renameList, const char *fromP, if (fromIV == toIV) return true; // generate the real destination path, where we expect to find the files.. - rDebug("opendir %s", sourcePath.c_str()); - shared_ptr dir = - shared_ptr(opendir(sourcePath.c_str()), DirDeleter()); + VLOG(1) << "opendir " << sourcePath; + std::shared_ptr dir = + std::shared_ptr(opendir(sourcePath.c_str()), DirDeleter()); if (!dir) return false; struct dirent *de = NULL; @@ -421,7 +407,7 @@ bool DirNode::genRenameList(list &renameList, const char *fromP, try { plainName = naming->decodePath(de->d_name, &localIV); - } catch (rlog::Error &ex) { + } catch (encfs::Error &ex) { // if filename can't be decoded, then ignore it.. continue; } @@ -463,15 +449,15 @@ bool DirNode::genRenameList(list &renameList, const char *fromP, } } - rDebug("adding file %s to rename list", oldFull.c_str()); + VLOG(1) << "adding file " << oldFull << " to rename list"; renameList.push_back(ren); - } catch (rlog::Error &err) { + } catch (encfs::Error &err) { // We can't convert this name, because we don't have a valid IV for // it (or perhaps a valid key).. It will be inaccessible.. - rWarning("Aborting rename: error on file: %s", - fromCPart.append(1, '/').append(de->d_name).c_str()); - err.log(_RLDebugChannel); + RLOG(WARNING) << "Aborting rename: error on file: " + << fromCPart.append(1, '/').append(de->d_name); + RLOG(WARNING) << err.what(); // abort.. Err on the side of safety and disallow rename, rather // then loosing files.. @@ -489,16 +475,16 @@ bool DirNode::genRenameList(list &renameList, const char *fromP, will have changed.. Returns a list of renamed items on success, a null list on failure. -*/ -shared_ptr DirNode::newRenameOp(const char *fromP, const char *toP) { +*/ std::shared_ptr DirNode::newRenameOp(const char *fromP, + const char *toP) { // Do the rename in two stages to avoid chasing our tail // Undo everything if we encounter an error! - shared_ptr > renameList(new list); + std::shared_ptr > renameList(new list); if (!genRenameList(*renameList.get(), fromP, toP)) { - rWarning("Error during generation of recursive rename list"); - return shared_ptr(); + RLOG(WARNING) << "Error during generation of recursive rename list"; + return std::shared_ptr(); } else - return shared_ptr(new RenameOp(this, renameList)); + return std::shared_ptr(new RenameOp(this, renameList)); } int DirNode::mkdir(const char *plaintextPath, mode_t mode, uid_t uid, @@ -506,7 +492,7 @@ int DirNode::mkdir(const char *plaintextPath, mode_t mode, uid_t uid, string cyName = rootDir + naming->encodePath(plaintextPath); rAssert(!cyName.empty()); - rLog(Info, "mkdir on %s", cyName.c_str()); + VLOG(1) << "mkdir on " << cyName; // if uid or gid are set, then that should be the directory owner int olduid = -1; @@ -521,8 +507,8 @@ int DirNode::mkdir(const char *plaintextPath, mode_t mode, uid_t uid, if (res == -1) { int eno = errno; - rWarning("mkdir error on %s mode %i: %s", cyName.c_str(), mode, - strerror(eno)); + RLOG(WARNING) << "mkdir error on " << cyName << " mode " << mode << ": " + << strerror(eno); res = -eno; } else res = 0; @@ -538,22 +524,22 @@ int DirNode::rename(const char *fromPlaintext, const char *toPlaintext) { rAssert(!fromCName.empty()); rAssert(!toCName.empty()); - rLog(Info, "rename %s -> %s", fromCName.c_str(), toCName.c_str()); + VLOG(1) << "rename " << fromCName << " -> " << toCName; - shared_ptr toNode = findOrCreate(toPlaintext); + std::shared_ptr toNode = findOrCreate(toPlaintext); - shared_ptr renameOp; + std::shared_ptr renameOp; if (hasDirectoryNameDependency() && isDirectory(fromCName.c_str())) { - rLog(Info, "recursive rename begin"); + VLOG(1) << "recursive rename begin"; renameOp = newRenameOp(fromPlaintext, toPlaintext); if (!renameOp || !renameOp->apply()) { if (renameOp) renameOp->undo(); - rWarning("rename aborted"); + RLOG(WARNING) << "rename aborted"; return -EACCES; } - rLog(Info, "recursive rename end"); + VLOG(1) << "recursive rename end"; } int res = 0; @@ -576,14 +562,14 @@ int DirNode::rename(const char *fromPlaintext, const char *toPlaintext) { ut.modtime = st.st_mtime; ::utime(toCName.c_str(), &ut); } - } catch (rlog::Error &err) { + } catch (encfs::Error &err) { // exception from renameNode, just show the error and continue.. - err.log(_RLWarningChannel); + RLOG(WARNING) << err.what(); res = -EIO; } if (res != 0) { - rLog(Info, "rename failed: %s", strerror(errno)); + VLOG(1) << "rename failed: " << strerror(errno); res = -errno; } @@ -599,11 +585,11 @@ int DirNode::link(const char *from, const char *to) { rAssert(!fromCName.empty()); rAssert(!toCName.empty()); - rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str()); + VLOG(1) << "link " << fromCName << " -> " << toCName; int res = -EPERM; if (fsConfig->config->externalIVChaining) { - rLog(Info, "hard links not supported with external IV chaining!"); + VLOG(1) << "hard links not supported with external IV chaining!"; } else { res = ::link(fromCName.c_str(), toCName.c_str()); if (res == -1) @@ -618,36 +604,34 @@ int DirNode::link(const char *from, const char *to) { /* The node is keyed by filename, so a rename means the internal node names must be changed. -*/ -shared_ptr DirNode::renameNode(const char *from, const char *to) { +*/ std::shared_ptr DirNode::renameNode(const char *from, + const char *to) { return renameNode(from, to, true); } - -shared_ptr DirNode::renameNode(const char *from, const char *to, - bool forwardMode) { - shared_ptr node = findOrCreate(from); +std::shared_ptr DirNode::renameNode(const char *from, const char *to, + bool forwardMode) { + std::shared_ptr node = findOrCreate(from); if (node) { uint64_t newIV = 0; string cname = rootDir + naming->encodePath(to, &newIV); - rLog(Info, "renaming internal node %s -> %s", node->cipherName(), - cname.c_str()); + VLOG(1) << "renaming internal node " << node->cipherName() << " -> " + << cname; if (node->setName(to, cname.c_str(), newIV, forwardMode)) { if (ctx) ctx->renameNode(from, to); } else { // rename error! - put it back - rError("renameNode failed"); - throw ERROR("Internal node name change failed!"); + RLOG(ERROR) << "renameNode failed"; + throw Error("Internal node name change failed!"); } } return node; } - -shared_ptr DirNode::findOrCreate(const char *plainName) { - shared_ptr node; +std::shared_ptr DirNode::findOrCreate(const char *plainName) { + std::shared_ptr node; if (ctx) node = ctx->lookupNode(plainName); if (!node) { @@ -658,18 +642,17 @@ shared_ptr DirNode::findOrCreate(const char *plainName) { if (fsConfig->config->externalIVChaining) node->setName(0, 0, iv); - rLog(Info, "created FileNode for %s", node->cipherName()); + VLOG(1) << "created FileNode for " << node->cipherName(); } return node; } - -shared_ptr DirNode::lookupNode(const char *plainName, - const char *requestor) { +std::shared_ptr DirNode::lookupNode(const char *plainName, + const char *requestor) { (void)requestor; Lock _lock(mutex); - shared_ptr node = findOrCreate(plainName); + std::shared_ptr node = findOrCreate(plainName); return node; } @@ -678,25 +661,24 @@ shared_ptr DirNode::lookupNode(const char *plainName, Similar to lookupNode, except that we also call open() and only return a node on sucess.. This is done in one step to avoid any race conditions with the stored state of the file. -*/ -shared_ptr DirNode::openNode(const char *plainName, - const char *requestor, int flags, - int *result) { +*/ std::shared_ptr DirNode::openNode(const char *plainName, + const char *requestor, int flags, + int *result) { (void)requestor; rAssert(result != NULL); Lock _lock(mutex); - shared_ptr node = findOrCreate(plainName); + std::shared_ptr node = findOrCreate(plainName); if (node && (*result = node->open(flags)) >= 0) return node; else - return shared_ptr(); + return std::shared_ptr(); } int DirNode::unlink(const char *plaintextName) { string cyName = naming->encodePath(plaintextName); - rLog(Info, "unlink %s", cyName.c_str()); + VLOG(1) << "unlink " << cyName; Lock _lock(mutex); @@ -705,19 +687,20 @@ int DirNode::unlink(const char *plaintextName) { // If FUSE is running with "hard_remove" option where it doesn't // hide open files for us, then we can't allow an unlink of an open // file.. - rWarning( - "Refusing to unlink open file: %s, hard_remove option " - "is probably in effect", - cyName.c_str()); + RLOG(WARNING) << "Refusing to unlink open file: " << cyName + << ", hard_remove option " + "is probably in effect"; res = -EBUSY; } else { string fullName = rootDir + cyName; res = ::unlink(fullName.c_str()); if (res == -1) { res = -errno; - rDebug("unlink error: %s", strerror(errno)); + VLOG(1) << "unlink error: " << strerror(errno); } } return res; } + +} // namespace encfs diff --git a/encfs/DirNode.h b/encfs/DirNode.h index 5257fc9..a88082d 100644 --- a/encfs/DirNode.h +++ b/encfs/DirNode.h @@ -23,13 +23,13 @@ #include #include -#include -#include -#include #include #include #include +#include +#include #include +#include #include #include "CipherKey.h" @@ -37,6 +37,8 @@ #include "FileNode.h" #include "NameIO.h" +namespace encfs { + class Cipher; class EncFS_Context; class FileNode; @@ -46,8 +48,8 @@ struct RenameEl; class DirTraverse { public: - DirTraverse(const shared_ptr &dirPtr, uint64_t iv, - const shared_ptr &naming); + DirTraverse(const std::shared_ptr &dirPtr, uint64_t iv, + const std::shared_ptr &naming); DirTraverse(const DirTraverse &src); ~DirTraverse(); @@ -68,11 +70,11 @@ class DirTraverse { std::string nextInvalid(); private: - shared_ptr dir; // struct DIR + std::shared_ptr dir; // struct DIR // initialization vector to use. Not very general purpose, but makes it // more efficient to support filename IV chaining.. uint64_t iv; - shared_ptr naming; + std::shared_ptr naming; }; inline bool DirTraverse::valid() const { return dir.get() != 0; } @@ -90,16 +92,16 @@ class DirNode { bool touchesMountpoint(const char *realPath) const; // find files - shared_ptr lookupNode(const char *plaintextName, - const char *requestor); + std::shared_ptr lookupNode(const char *plaintextName, + const char *requestor); /* Combined lookupNode + node->open() call. If the open fails, then the node is not retained. If the open succeeds, then the node is returned. */ - shared_ptr openNode(const char *plaintextName, - const char *requestor, int flags, - int *openResult); + std::shared_ptr openNode(const char *plaintextName, + const char *requestor, int flags, + int *openResult); std::string cipherPath(const char *plaintextPath); std::string cipherPathWithoutRoot(const char *plaintextPath); @@ -141,9 +143,9 @@ class DirNode { this call has no effect. Returns the FileNode if it was found. */ - shared_ptr renameNode(const char *from, const char *to); - shared_ptr renameNode(const char *from, const char *to, - bool forwardMode); + std::shared_ptr renameNode(const char *from, const char *to); + std::shared_ptr renameNode(const char *from, const char *to, + bool forwardMode); /* when directory IV chaining is enabled, a directory can't be renamed @@ -151,7 +153,7 @@ class DirNode { called after renaming the directory, passing in the plaintext from and to paths. */ - shared_ptr newRenameOp(const char *from, const char *to); + std::shared_ptr newRenameOp(const char *from, const char *to); private: friend class RenameOp; @@ -159,7 +161,7 @@ class DirNode { bool genRenameList(std::list &list, const char *fromP, const char *toP); - shared_ptr findOrCreate(const char *plainName); + std::shared_ptr findOrCreate(const char *plainName); pthread_mutex_t mutex; @@ -169,7 +171,9 @@ class DirNode { std::string rootDir; FSConfigPtr fsConfig; - shared_ptr naming; + std::shared_ptr naming; }; +} // namespace encfs + #endif diff --git a/encfs/Error.cpp b/encfs/Error.cpp new file mode 100644 index 0000000..4f277e3 --- /dev/null +++ b/encfs/Error.cpp @@ -0,0 +1,9 @@ +#include "Error.h" + +namespace encfs { + +el::base::DispatchAction rlogAction = el::base::DispatchAction::NormalLog; + +Error::Error(const char *msg) : runtime_error(msg) {} + +} // namespace encfs diff --git a/encfs/Error.h b/encfs/Error.h new file mode 100644 index 0000000..56415ca --- /dev/null +++ b/encfs/Error.h @@ -0,0 +1,44 @@ +#ifndef _Error_incl_ +#define _Error_incl_ + +// Provides compatibility with RLog's rAssert, which throws an Error exception. + +#include "internal/easylogging++.h" +#include + +namespace encfs { + +class Error : public std::runtime_error { + public: + Error(const char *msg); +}; + +#define STR(X) #X + +#define rAssert(cond) \ + do { \ + if ((cond) == false) { \ + RLOG(ERROR) << "Assert failed: " << STR(cond); \ + throw encfs::Error(STR(cond)); \ + } \ + } while (0) + +inline void initLogging() { + el::Configurations defaultConf; + defaultConf.setToDefault(); + defaultConf.set(el::Level::Verbose, el::ConfigurationType::Format, + std::string("%datetime %level [%fbase:%line] %msg")); + el::Loggers::reconfigureLogger("default", defaultConf); + el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); +} + +// This can be changed to change log action between normal and syslog logging. +// Not thread-safe, so any change must occur outside of threading context. +extern el::base::DispatchAction rlogAction; + +#define RLOG(LEVEL, ...) \ + C##LEVEL(el::base::Writer, rlogAction, ELPP_CURR_FILE_LOGGER_ID) + +} // namespace encfs + +#endif diff --git a/encfs/FSConfig.h b/encfs/FSConfig.h index 922dfc1..39e2859 100644 --- a/encfs/FSConfig.h +++ b/encfs/FSConfig.h @@ -21,13 +21,15 @@ #ifndef _FSConfig_incl_ #define _FSConfig_incl_ +#include #include #include -#include "encfs.h" -#include "Interface.h" #include "CipherKey.h" -#include "shared_ptr.h" +#include "Interface.h" +#include "encfs.h" + +namespace encfs { enum ConfigType { Config_None = 0, @@ -52,10 +54,10 @@ struct EncFSConfig { int subVersion; // interface of cipher - rel::Interface cipherIface; + Interface cipherIface; // interface used for file name coding - rel::Interface nameIface; - + Interface nameIface; + int keySize; // reported in bits int blockSize; // reported in bytes @@ -93,7 +95,7 @@ struct EncFSConfig { const std::string &rootDir); CipherKey getNewUserKey(); - shared_ptr getCipher() const; + std::shared_ptr getCipher() const; // deprecated void assignKeyData(const std::string &in); @@ -112,12 +114,12 @@ std::ostream &operator<<(std::ostream &os, const EncFSConfig &cfg); std::istream &operator>>(std::istream &os, EncFSConfig &cfg); struct FSConfig { - shared_ptr config; - shared_ptr opts; + std::shared_ptr config; + std::shared_ptr opts; - shared_ptr cipher; + std::shared_ptr cipher; CipherKey key; - shared_ptr nameCoding; + std::shared_ptr nameCoding; bool forceDecode; // force decode on MAC block failures bool reverseEncryption; // reverse encryption operation @@ -128,6 +130,8 @@ struct FSConfig { : forceDecode(false), reverseEncryption(false), idleTracking(false) {} }; -typedef shared_ptr FSConfigPtr; +typedef std::shared_ptr FSConfigPtr; + +} // namespace encfs #endif diff --git a/encfs/FileIO.cpp b/encfs/FileIO.cpp index 1eb6bae..ab9b363 100644 --- a/encfs/FileIO.cpp +++ b/encfs/FileIO.cpp @@ -20,6 +20,8 @@ #include "FileIO.h" +namespace encfs { + FileIO::FileIO() {} FileIO::~FileIO() {} @@ -30,3 +32,5 @@ bool FileIO::setIV(uint64_t iv) { (void)iv; return true; } + +} // namespace encfs diff --git a/encfs/FileIO.h b/encfs/FileIO.h index 3031612..474ff20 100644 --- a/encfs/FileIO.h +++ b/encfs/FileIO.h @@ -28,6 +28,8 @@ #include "Interface.h" #include "encfs.h" +namespace encfs { + struct IORequest { off_t offset; @@ -45,7 +47,7 @@ class FileIO { FileIO(); virtual ~FileIO(); - virtual rel::Interface interface() const = 0; + virtual Interface interface() const = 0; // default implementation returns 1, meaning this is not block oriented. virtual int blockSize() const; @@ -78,4 +80,6 @@ class FileIO { FileIO &operator=(const FileIO &); }; +} // namespace encfs + #endif diff --git a/encfs/FileNode.cpp b/encfs/FileNode.cpp index 1fc6f1a..a7ac1d9 100644 --- a/encfs/FileNode.cpp +++ b/encfs/FileNode.cpp @@ -28,10 +28,10 @@ #include #endif -#include #include #include "CipherFileIO.h" +#include "Error.h" #include "FileIO.h" #include "FileNode.h" #include "FileUtils.h" @@ -39,13 +39,9 @@ #include "Mutex.h" #include "RawFileIO.h" -namespace rlog { -class RLogChannel; -} // namespace rlog - using namespace std; -using namespace rel; -using namespace rlog; + +namespace encfs { /* TODO: locking at the FileNode level is inefficient, since this precludes @@ -56,8 +52,6 @@ using namespace rlog; sent to the IO subsystem! */ -static RLogChannel *Info = DEF_CHANNEL("info/FileNode", Log_Info); - FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, const char *plaintextName_, const char *cipherName_) { pthread_mutex_init(&mutex, 0); @@ -71,11 +65,11 @@ FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, this->fsConfig = cfg; // chain RawFileIO & CipherFileIO - shared_ptr rawIO(new RawFileIO(_cname)); - io = shared_ptr(new CipherFileIO(rawIO, fsConfig)); + std::shared_ptr rawIO(new RawFileIO(_cname)); + io = std::shared_ptr(new CipherFileIO(rawIO, fsConfig)); if (cfg->config->blockMACBytes || cfg->config->blockMACRandBytes) - io = shared_ptr(new MACFileIO(io, fsConfig)); + io = std::shared_ptr(new MACFileIO(io, fsConfig)); } FileNode::~FileNode() { @@ -95,7 +89,7 @@ const char *FileNode::plaintextName() const { return _pname.c_str(); } string FileNode::plaintextParent() const { return parentDirectory(_pname); } -static bool setIV(const shared_ptr &io, uint64_t iv) { +static bool setIV(const std::shared_ptr &io, uint64_t iv) { struct stat stbuf; if ((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) return io->setIV(iv); @@ -106,7 +100,7 @@ static bool setIV(const shared_ptr &io, uint64_t iv) { bool FileNode::setName(const char *plaintextName_, const char *cipherName_, uint64_t iv, bool setIVFirst) { // Lock _lock( mutex ); - rDebug("calling setIV on %s", cipherName_); + VLOG(1) << "calling setIV on " << cipherName_; if (setIVFirst) { if (fsConfig->config->externalIVChaining && !setIV(io, iv)) return false; @@ -145,14 +139,14 @@ int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) { if (uid != 0) { olduid = setfsuid(uid); if (olduid == -1) { - rInfo("setfsuid error: %s", strerror(errno)); + RLOG(INFO) << "setfsuid error: " << strerror(errno); return -EPERM; } } if (gid != 0) { oldgid = setfsgid(gid); if (oldgid == -1) { - rInfo("setfsgid error: %s", strerror(errno)); + RLOG(INFO) << "setfsgid error: " << strerror(errno); return -EPERM; } } @@ -175,7 +169,7 @@ int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) { if (res == -1) { int eno = errno; - rDebug("mknod error: %s", strerror(eno)); + VLOG(1) << "mknod error: " << strerror(eno); res = -eno; } @@ -215,8 +209,7 @@ ssize_t FileNode::read(off_t offset, unsigned char *data, ssize_t size) const { } bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) { - rLog(Info, "FileNode::write offset %" PRIi64 ", data size %i", offset, - (int)size); + RLOG(INFO) << "FileNode::write offset " << offset << ", data size " << size; IORequest req; req.offset = offset; @@ -258,3 +251,5 @@ int FileNode::sync(bool datasync) { } else return fh; } + +} // namespace encfs diff --git a/encfs/FileNode.h b/encfs/FileNode.h index de2bb37..6662bf1 100644 --- a/encfs/FileNode.h +++ b/encfs/FileNode.h @@ -22,17 +22,19 @@ #define _FileNode_incl_ #include +#include #include #include -#include -#include #include +#include #include "CipherKey.h" -#include "FileUtils.h" #include "FSConfig.h" +#include "FileUtils.h" #include "encfs.h" +namespace encfs { + class Cipher; class DirNode; class FileIO; @@ -85,7 +87,7 @@ class FileNode { FSConfigPtr fsConfig; - shared_ptr io; + std::shared_ptr io; std::string _pname; // plaintext name std::string _cname; // encrypted name DirNode *parent; @@ -95,4 +97,6 @@ class FileNode { FileNode &operator=(const FileNode &src); }; +} // namespace encfs + #endif diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp index d23de29..e9b979c 100644 --- a/encfs/FileUtils.cpp +++ b/encfs/FileUtils.cpp @@ -24,22 +24,21 @@ #endif #define _BSD_SOURCE // pick up setenv on RH7.3 -#include -#include -#include -#include -#include -#include -#include +#include "internal/easylogging++.h" #include #include #include #include #include -#include +#include #include +#include #include +#include +#include +#include #include +#include #include #include "BlockNameIO.h" @@ -49,6 +48,7 @@ #include "ConfigVar.h" #include "Context.h" #include "DirNode.h" +#include "Error.h" #include "FSConfig.h" #include "FileUtils.h" #include "Interface.h" @@ -62,15 +62,11 @@ #include "intl/gettext.h" #include "readpassphrase.h" -// disable rlog section grouping for this file.. seems to cause problems -#undef RLOG_SECTION -#define RLOG_SECTION - -using namespace rel; -using namespace rlog; using namespace std; using gnu::autosprintf; +namespace encfs { + static const int DefaultBlockSize = 1024; // The maximum length of text passwords. If longer are needed, // use the extpass option, as extpass can return arbitrary length binary data. @@ -109,18 +105,18 @@ struct ConfigInfo { int currentSubVersion; int defaultSubVersion; } ConfigFileMapping[] = { - // current format - {".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config, - V6SubVersion, 0}, - // backward compatible support for older versions - {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config, - V5SubVersion, V5SubVersionDefault}, - {".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0}, - // no longer support earlier versions - {".encfs3", Config_V3, NULL, NULL, NULL, 0, 0}, - {".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, - {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, - {NULL, Config_None, NULL, NULL, NULL, 0, 0}}; + // current format + {".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config, + V6SubVersion, 0}, + // backward compatible support for older versions + {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config, + V5SubVersion, V5SubVersionDefault}, + {".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0}, + // no longer support earlier versions + {".encfs3", Config_V3, NULL, NULL, NULL, 0, 0}, + {".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, + {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, + {NULL, Config_None, NULL, NULL, NULL, 0, 0}}; EncFS_Root::EncFS_Root() {} @@ -217,11 +213,12 @@ ConfigType readConfig_load(ConfigInfo *nm, const char *path, config->cfgType = nm->type; return nm->type; } - } catch (rlog::Error &err) { - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "readConfig error: " << err.what(); } - rError(_("Found config file %s, but failed to load - exiting"), path); + RLOG(ERROR) << "Found config file " << path + << ", but failed to load - exiting"; exit(1); } else { // No load function - must be an unsupported type.. @@ -242,9 +239,9 @@ ConfigType readConfig(const string &rootDir, EncFSConfig *config) { char *envFile = getenv(nm->environmentOverride); if (envFile != NULL) { if (!fileExists(envFile)) { - rError( - "fatal: config file specified by environment does not exist: %s", - envFile); + RLOG(ERROR) + << "fatal: config file specified by environment does not exist: " + << envFile; exit(1); } return readConfig_load(nm, envFile, config); @@ -265,14 +262,13 @@ ConfigType readConfig(const string &rootDir, EncFSConfig *config) { * Read config file in current "V6" XML format, normally named ".encfs6.xml" * This format is in use since Apr 13, 2008 (commit 6d081f5c) */ - // Read a boost::serialization config file using an Xml reader.. -bool readV6Config(const char *configFile, EncFSConfig *cfg, - ConfigInfo *info) { +// Read a boost::serialization config file using an Xml reader.. +bool readV6Config(const char *configFile, EncFSConfig *cfg, ConfigInfo *info) { (void)info; XmlReader rdr; if (!rdr.load(configFile)) { - rError("Failed to load config file %s", configFile); + RLOG(ERROR) << "Failed to load config file " << configFile; return false; } @@ -282,34 +278,34 @@ bool readV6Config(const char *configFile, EncFSConfig *cfg, config = (*serialization)["config"]; } if (!config) { - rError("Unable to find XML configuration in file %s", configFile); + RLOG(ERROR) << "Unable to find XML configuration in file " << configFile; return false; } int version; if (!config->read("version", &version) && !config->read("@version", &version)) { - rError("Unable to find version in config file"); + RLOG(ERROR) << "Unable to find version in config file"; return false; } // version numbering was complicated by boost::archive if (version == 20 || version >= 20100713) { - rDebug("found new serialization format"); + VLOG(1) << "found new serialization format"; cfg->subVersion = version; } else if (version == 26800) { - rDebug("found 20080816 version"); + VLOG(1) << "found 20080816 version"; cfg->subVersion = 20080816; } else if (version == 26797) { - rDebug("found 20080813"); + VLOG(1) << "found 20080813"; cfg->subVersion = 20080813; } else if (version < V5SubVersion) { - rError("Invalid version %d - please fix config file", version); + RLOG(ERROR) << "Invalid version " << version << " - please fix config file"; } else { - rInfo("Boost <= 1.41 compatibility mode"); + VLOG(1) << "Boost <= 1.41 compatibility mode"; cfg->subVersion = version; } - rDebug("subVersion = %d", cfg->subVersion); + VLOG(1) << "subVersion = " << cfg->subVersion; config->read("creator", &cfg->creator); config->read("cipherAlg", &cfg->cipherIface); @@ -367,15 +363,14 @@ bool readV5Config(const char *configFile, EncFSConfig *config, if (config->subVersion > info->currentSubVersion) { /* config file specifies a version outside our supported range.. */ - rWarning(_("Config subversion %i found, but this version of" - " encfs only supports up to version %i."), - config->subVersion, info->currentSubVersion); + RLOG(WARNING) << "Config subversion " << config->subVersion + << " found, which is newer than supported version " + << info->currentSubVersion; return false; } if (config->subVersion < 20040813) { - rError( - _("This version of EncFS doesn't support " - "filesystems created before 2004-08-13")); + RLOG(ERROR) << "This version of EncFS doesn't support " + "filesystems created before 2004-08-13"; return false; } @@ -395,9 +390,9 @@ bool readV5Config(const char *configFile, EncFSConfig *config, config->blockMACRandBytes = cfgRdr["blockMACRandBytes"].readInt(0); ok = true; - } catch (rlog::Error &err) { - err.log(_RLWarningChannel); - rDebug("Error parsing data in config file %s", configFile); + } catch (encfs::Error &err) { + RLOG(WARNING) << err.what(); + VLOG(1) << "Error parsing data in config file " << configFile; ok = false; } } @@ -435,9 +430,9 @@ bool readV4Config(const char *configFile, EncFSConfig *config, config->chainedNameIV = false; ok = true; - } catch (rlog::Error &err) { - err.log(_RLWarningChannel); - rDebug("Error parsing config file %s", configFile); + } catch (encfs::Error &err) { + RLOG(WARNING) << err.what(); + VLOG(1) << "Error parsing config file " << configFile; ok = false; } } @@ -461,8 +456,8 @@ bool saveConfig(ConfigType type, const string &rootDir, try { ok = (*nm->saveFunc)(path.c_str(), config); - } catch (rlog::Error &err) { - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(WARNING) << err.what(); ok = false; } break; @@ -558,8 +553,7 @@ bool writeV6Config(const char *configFile, const EncFSConfig *cfg) { return err == tinyxml2::XML_SUCCESS; } -bool writeV5Config(const char *configFile, - const EncFSConfig *config) { +bool writeV5Config(const char *configFile, const EncFSConfig *config) { ConfigReader cfg; cfg["creator"] << config->creator; @@ -580,8 +574,7 @@ bool writeV5Config(const char *configFile, return cfg.save(configFile); } -bool writeV4Config(const char *configFile, - const EncFSConfig *config) { +bool writeV4Config(const char *configFile, const EncFSConfig *config) { ConfigReader cfg; cfg["cipher"] << config->cipherIface; @@ -632,20 +625,23 @@ static Cipher::CipherAlgorithm selectCipherAlgorithm() { // shown after algorithm name and description. // xgroup(setup) _(" -- Supports key lengths of %i to %i bits"), - it->keyLength.min(), it->keyLength.max()) << "\n"; + it->keyLength.min(), it->keyLength.max()) + << "\n"; } if (it->blockSize.min() == it->blockSize.max()) { cout << autosprintf( // shown after algorithm name and description. // xgroup(setup) - _(" -- block size %i bytes"), it->blockSize.min()) << "\n"; + _(" -- block size %i bytes"), it->blockSize.min()) + << "\n"; } else { cout << autosprintf( // shown after algorithm name and description. // xgroup(setup) _(" -- Supports block sizes of %i to %i bytes"), - it->blockSize.min(), it->blockSize.max()) << "\n"; + it->blockSize.min(), it->blockSize.max()) + << "\n"; } } @@ -777,7 +773,8 @@ static int selectBlockSize(const Cipher::CipherAlgorithm &alg) { cout << autosprintf( // xgroup(setup) _("Using filesystem block size of %i bytes"), - alg.blockSize.min()) << "\n"; + alg.blockSize.min()) + << "\n"; return alg.blockSize.min(); } @@ -869,8 +866,9 @@ static void selectBlockMAC(int *macBytes, int *macRandBytes, bool forceMac) { "performance but it also means [almost] any modifications or errors\n" "within a block will be caught and will cause a read error.")); } else { - cout << "\n\n" << _("You specified --require-macs. " - "Enabling block authentication code headers...") + cout << "\n\n" + << _("You specified --require-macs. " + "Enabling block authentication code headers...") << "\n\n"; addMAC = true; } @@ -950,7 +948,8 @@ static bool selectZeroBlockPassThrough() { "This avoids writing encrypted blocks when file holes are created.")); } -RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { +RootPtr createV6Config(EncFS_Context *ctx, + const std::shared_ptr &opts) { const std::string rootDir = opts->rootDir; bool enableIdleTracking = opts->idleTracking; bool forceDecode = opts->forceDecode; @@ -1007,7 +1006,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { if (configMode == Config_Paranoia || answer[0] == 'p') { if (reverseEncryption) { - rError(_("Paranoia configuration not supported for reverse encryption")); + cerr << _("Paranoia configuration not supported for reverse encryption"); return rootInfo; } @@ -1022,7 +1021,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { blockSize = DefaultBlockSize; alg = findCipherAlgorithm("AES", keySize); - // If case-insensitive system, opt for Block32 filename encoding +// If case-insensitive system, opt for Block32 filename encoding #if defined(__APPLE__) || defined(WIN32) nameIOIface = BlockNameIO::CurrentInterface(true); #else @@ -1042,7 +1041,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { blockSize = DefaultBlockSize; alg = findCipherAlgorithm("AES", keySize); - // If case-insensitive system, opt for Block32 filename encoding +// If case-insensitive system, opt for Block32 filename encoding #if defined(__APPLE__) || defined(WIN32) nameIOIface = BlockNameIO::CurrentInterface(true); #else @@ -1096,17 +1095,18 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { } } - shared_ptr cipher = Cipher::New(alg.name, keySize); + std::shared_ptr cipher = Cipher::New(alg.name, keySize); if (!cipher) { - rError(_("Unable to instanciate cipher %s, key size %i, block size %i"), - alg.name.c_str(), keySize, blockSize); + cerr << autosprintf( + _("Unable to instanciate cipher %s, key size %i, block size %i"), + alg.name.c_str(), keySize, blockSize); return rootInfo; } else { - rDebug("Using cipher %s, key size %i, block size %i", alg.name.c_str(), - keySize, blockSize); + VLOG(1) << "Using cipher " << alg.name << ", key size " << keySize + << ", block size " << blockSize; } - shared_ptr config(new EncFSConfig); + std::shared_ptr config(new EncFSConfig); config->cfgType = Config_V6; config->cipherIface = cipher->interface(); @@ -1129,7 +1129,8 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { cout << "\n"; // xgroup(setup) cout << _("Configuration finished. The filesystem to be created has\n" - "the following properties:") << endl; + "the following properties:") + << endl; showFSInfo(config.get()); if (config->externalIVChaining) { @@ -1140,7 +1141,8 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { "The programs 'mutt' and 'procmail' are known to fail. For\n" "more information, please see the encfs mailing list.\n" "If you would like to choose another configuration setting,\n" - "please press CTRL-C now to abort and start over.") << endl; + "please press CTRL-C now to abort and start over.") + << endl; cout << endl; } @@ -1158,7 +1160,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { // get user key and use it to encode volume key CipherKey userKey; - rDebug("useStdin: %i", useStdin); + VLOG(1) << "useStdin: " << useStdin; if (useStdin) { if (annotate) cerr << "$PROMPT$ new_passwd" << endl; userKey = config->getUserKey(useStdin); @@ -1174,19 +1176,21 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { delete[] encodedKey; if (!volumeKey) { - rWarning( - _("Failure generating new volume key! " - "Please report this error.")); + cerr << _( + "Failure generating new volume key! " + "Please report this error."); return rootInfo; } - if (!saveConfig(Config_V6, rootDir, config.get())) return rootInfo; + if (!saveConfig(Config_V6, rootDir, config.get())) { + return rootInfo; + } // fill in config struct - shared_ptr nameCoder = + std::shared_ptr nameCoder = NameIO::New(config->nameIface, cipher, volumeKey); if (!nameCoder) { - rWarning(_("Name coding interface not supported")); + cerr << _("Name coding interface not supported"); cout << _("The filename encoding interface requested is not available") << endl; return rootInfo; @@ -1208,13 +1212,14 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { rootInfo = RootPtr(new EncFS_Root); rootInfo->cipher = cipher; rootInfo->volumeKey = volumeKey; - rootInfo->root = shared_ptr(new DirNode(ctx, rootDir, fsConfig)); + rootInfo->root = + std::shared_ptr(new DirNode(ctx, rootDir, fsConfig)); return rootInfo; } void showFSInfo(const EncFSConfig *config) { - shared_ptr cipher = Cipher::New(config->cipherIface, -1); + std::shared_ptr cipher = Cipher::New(config->cipherIface, -1); { cout << autosprintf( // xgroup(diag) @@ -1243,7 +1248,7 @@ void showFSInfo(const EncFSConfig *config) { config->nameIface.revision(), config->nameIface.age()); // check if we support the filename encoding interface.. - shared_ptr nameCoder = + std::shared_ptr nameCoder = NameIO::New(config->nameIface, cipher, CipherKey()); if (!nameCoder) { // xgroup(diag) @@ -1269,7 +1274,8 @@ void showFSInfo(const EncFSConfig *config) { } if (config->kdfIterations > 0 && config->salt.size() > 0) { cout << autosprintf(_("Using PBKDF2, with %i iterations"), - config->kdfIterations) << "\n"; + config->kdfIterations) + << "\n"; cout << autosprintf(_("Salt Size: %i bits"), (int)(8 * config->salt.size())) << "\n"; } @@ -1279,14 +1285,16 @@ void showFSInfo(const EncFSConfig *config) { // xgroup(diag) _("Block Size: %i bytes + %i byte MAC header"), config->blockSize, - config->blockMACBytes + config->blockMACRandBytes) << endl; + config->blockMACBytes + config->blockMACRandBytes) + << endl; } else { // new version stores the header as part of that block size.. cout << autosprintf( // xgroup(diag) _("Block Size: %i bytes, including %i byte MAC header"), config->blockSize, - config->blockMACBytes + config->blockMACRandBytes) << endl; + config->blockMACBytes + config->blockMACRandBytes) + << endl; } } else { // xgroup(diag) @@ -1312,8 +1320,7 @@ void showFSInfo(const EncFSConfig *config) { } cout << "\n"; } - -shared_ptr EncFSConfig::getCipher() const { +std::shared_ptr EncFSConfig::getCipher() const { return Cipher::New(cipherIface, keySize); } @@ -1339,7 +1346,7 @@ unsigned char *EncFSConfig::getSaltData() const { CipherKey EncFSConfig::makeKey(const char *password, int passwdLen) { CipherKey userKey; - shared_ptr cipher = getCipher(); + std::shared_ptr cipher = getCipher(); // if no salt is set and we're creating a new password for a new // FS type, then initialize salt.. @@ -1427,7 +1434,7 @@ CipherKey EncFSConfig::getUserKey(const std::string &passProg, perror(_("Internal error: socketpair() failed")); return result; } - rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]); + VLOG(1) << "getUserKey: fds = " << fds[0] << ", " << fds[1]; pid = fork(); if (pid == -1) { @@ -1518,9 +1525,9 @@ CipherKey EncFSConfig::getNewUserKey() { return userKey; } -RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { +RootPtr initFS(EncFS_Context *ctx, const std::shared_ptr &opts) { RootPtr rootInfo; - shared_ptr config(new EncFSConfig); + std::shared_ptr config(new EncFSConfig); if (readConfig(opts->rootDir, config.get()) != Config_None) { if (config->blockMACBytes == 0 && opts->requireMac) { @@ -1539,11 +1546,12 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { } // first, instanciate the cipher. - shared_ptr cipher = config->getCipher(); + std::shared_ptr cipher = config->getCipher(); if (!cipher) { - rError(_("Unable to find cipher %s, version %i:%i:%i"), - config->cipherIface.name().c_str(), config->cipherIface.current(), - config->cipherIface.revision(), config->cipherIface.age()); + cerr << autosprintf( + _("Unable to find cipher %s, version %i:%i:%i"), + config->cipherIface.name().c_str(), config->cipherIface.current(), + config->cipherIface.revision(), config->cipherIface.age()); // xgroup(diag) cout << _("The requested cipher interface is not available\n"); return rootInfo; @@ -1552,7 +1560,7 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { if (opts->delayMount) { rootInfo = RootPtr(new EncFS_Root); rootInfo->cipher = cipher; - rootInfo->root = shared_ptr(); + rootInfo->root = std::shared_ptr(); return rootInfo; } @@ -1560,7 +1568,7 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { CipherKey userKey; if (opts->passwordProgram.empty()) { - rDebug("useStdin: %i", opts->useStdin); + VLOG(1) << "useStdin: " << opts->useStdin; if (opts->annotate) cerr << "$PROMPT$ passwd" << endl; userKey = config->getUserKey(opts->useStdin); } else @@ -1568,7 +1576,7 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { if (!userKey) return rootInfo; - rDebug("cipher key size = %i", cipher->encodedKeySize()); + VLOG(1) << "cipher key size = " << cipher->encodedKeySize(); // decode volume key.. CipherKey volumeKey = cipher->readKey(config->getKeyData(), userKey, opts->checkKey); @@ -1580,12 +1588,13 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { return rootInfo; } - shared_ptr nameCoder = + std::shared_ptr nameCoder = NameIO::New(config->nameIface, cipher, volumeKey); if (!nameCoder) { - rError(_("Unable to find nameio interface %s, version %i:%i:%i"), - config->nameIface.name().c_str(), config->nameIface.current(), - config->nameIface.revision(), config->nameIface.age()); + cerr << autosprintf( + _("Unable to find nameio interface '%s', version %i:%i:%i"), + config->nameIface.name().c_str(), config->nameIface.current(), + config->nameIface.revision(), config->nameIface.age()); // xgroup(diag) cout << _( "The requested filename coding interface is " @@ -1609,7 +1618,7 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { rootInfo->cipher = cipher; rootInfo->volumeKey = volumeKey; rootInfo->root = - shared_ptr(new DirNode(ctx, opts->rootDir, fsConfig)); + std::shared_ptr(new DirNode(ctx, opts->rootDir, fsConfig)); } else { if (opts->createIfNotFound) { // creating a new encrypted filesystem @@ -1621,14 +1630,16 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { } int remountFS(EncFS_Context *ctx) { - rDebug("Attempting to reinitialize filesystem"); + VLOG(1) << "Attempting to reinitialize filesystem"; RootPtr rootInfo = initFS(ctx, ctx->opts); if (rootInfo) { ctx->setRoot(rootInfo->root); return 0; } else { - rInfo(_("Remount failed")); + RLOG(INFO) << "Remount failed"; return -EACCES; } } + +} // namespace encfs diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index 5e09fe9..7efbbf0 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -21,15 +21,17 @@ #ifndef _FileUtils_incl_ #define _FileUtils_incl_ -#include #include #include +#include #include "CipherKey.h" #include "FSConfig.h" #include "Interface.h" #include "encfs.h" +namespace encfs { + // true if the path points to an existing node (of any type) bool fileExists(const char *fileName); // true if path is a directory @@ -50,15 +52,15 @@ class Cipher; class DirNode; struct EncFS_Root { - shared_ptr cipher; + std::shared_ptr cipher; CipherKey volumeKey; - shared_ptr root; + std::shared_ptr root; EncFS_Root(); ~EncFS_Root(); }; -typedef shared_ptr RootPtr; +typedef std::shared_ptr RootPtr; enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia }; @@ -129,9 +131,10 @@ bool saveConfig(ConfigType type, const std::string &rootdir, class EncFS_Context; -RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts); +RootPtr initFS(EncFS_Context *ctx, const std::shared_ptr &opts); -RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts); +RootPtr createV6Config(EncFS_Context *ctx, + const std::shared_ptr &opts); void showFSInfo(const EncFSConfig *config); @@ -147,4 +150,6 @@ bool readV6Config(const char *configFile, EncFSConfig *config, struct ConfigInfo *); bool writeV6Config(const char *configFile, const EncFSConfig *config); +} // namespace encfs + #endif diff --git a/encfs/Interface.cpp b/encfs/Interface.cpp index 46f4dc4..8cd8def 100644 --- a/encfs/Interface.cpp +++ b/encfs/Interface.cpp @@ -20,18 +20,10 @@ #include "Interface.h" -#include - #include "ConfigVar.h" +#include "Error.h" -namespace rlog { -class RLogChannel; -} // namespace rlog - -using namespace rel; -using namespace rlog; - -static RLogChannel *Info = DEF_CHANNEL("info/iface", Log_Info); +namespace encfs { Interface::Interface(const char *name_, int Current, int Revision, int Age) : _name(name_), _current(Current), _revision(Revision), _age(Age) {} @@ -115,9 +107,9 @@ static int diffSum(const Interface &A, const Interface &B) { const int EqualVersion = (1 * 3 + 1) * 3 + 1; bool Interface::implements(const Interface &B) const { - rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", name().c_str(), - current(), revision(), age(), B.name().c_str(), B.current(), - B.revision(), B.age()); + VLOG(1) << "checking if " << name() << "(" << current() << ":" << revision() + << ":" << age() << ") implements " << B.name() << "(" << B.current() + << ":" << B.revision() << ")"; if (name() != B.name()) return false; @@ -153,7 +145,7 @@ bool operator>=(const Interface &A, const Interface &B) { return A.name() < B.name(); } -ConfigVar &operator<<(ConfigVar &dst, const rel::Interface &iface) { +ConfigVar &operator<<(ConfigVar &dst, const Interface &iface) { dst << iface.name() << iface.current() << iface.revision() << iface.age(); return dst; } @@ -165,3 +157,5 @@ const ConfigVar &operator>>(const ConfigVar &src, Interface &iface) { src >> iface.age(); return src; } + +} // namespace encfs diff --git a/encfs/Interface.h b/encfs/Interface.h index 7c950db..2ec101a 100644 --- a/encfs/Interface.h +++ b/encfs/Interface.h @@ -23,10 +23,9 @@ #include -class ConfigVar; +namespace encfs { -// part of REL library.. -namespace rel { +class ConfigVar; class Interface { public: @@ -67,16 +66,17 @@ class Interface { int _revision; int _age; }; -} -ConfigVar &operator<<(ConfigVar &, const rel::Interface &); -const ConfigVar &operator>>(const ConfigVar &, rel::Interface &); +ConfigVar &operator<<(ConfigVar &, const Interface &); +const ConfigVar &operator>>(const ConfigVar &, Interface &); -bool operator<(const rel::Interface &A, const rel::Interface &B); -bool operator>(const rel::Interface &A, const rel::Interface &B); -bool operator<=(const rel::Interface &A, const rel::Interface &B); -bool operator>=(const rel::Interface &A, const rel::Interface &B); -bool operator==(const rel::Interface &A, const rel::Interface &B); -bool operator!=(const rel::Interface &A, const rel::Interface &B); +bool operator<(const Interface &A, const Interface &B); +bool operator>(const Interface &A, const Interface &B); +bool operator<=(const Interface &A, const Interface &B); +bool operator>=(const Interface &A, const Interface &B); +bool operator==(const Interface &A, const Interface &B); +bool operator!=(const Interface &A, const Interface &B); + +} // namespace encfs #endif diff --git a/encfs/MACFileIO.cpp b/encfs/MACFileIO.cpp index a572150..cf2760c 100644 --- a/encfs/MACFileIO.cpp +++ b/encfs/MACFileIO.cpp @@ -20,29 +20,23 @@ #include "MACFileIO.h" -#include -#include -#include -#include +#include "internal/easylogging++.h" #include +#include +#include #include "BlockFileIO.h" #include "Cipher.h" +#include "Error.h" #include "FileIO.h" #include "FileUtils.h" #include "MemoryPool.h" #include "i18n.h" -namespace rlog { -class RLogChannel; -} // namespace rlog - -using namespace rlog; -using namespace rel; using namespace std; -static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); -// +namespace encfs { + // Version 1.0 worked on blocks of size (blockSize + headerSize). // That is, it took [blockSize] worth of user data and added headers. // Version 2.0 takes [blockSize - headerSize] worth of user data and writes @@ -55,14 +49,15 @@ static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); // compatible, except at a high level by checking a revision number for the // filesystem... // -static rel::Interface MACFileIO_iface("FileIO/MAC", 2, 1, 0); +static Interface MACFileIO_iface("FileIO/MAC", 2, 1, 0); int dataBlockSize(const FSConfigPtr &cfg) { return cfg->config->blockSize - cfg->config->blockMACBytes - cfg->config->blockMACRandBytes; } -MACFileIO::MACFileIO(const shared_ptr &_base, const FSConfigPtr &cfg) +MACFileIO::MACFileIO(const std::shared_ptr &_base, + const FSConfigPtr &cfg) : BlockFileIO(dataBlockSize(cfg), cfg), base(_base), cipher(cfg->cipher), @@ -72,14 +67,14 @@ MACFileIO::MACFileIO(const shared_ptr &_base, const FSConfigPtr &cfg) warnOnly(cfg->opts->forceDecode) { rAssert(macBytes >= 0 && macBytes <= 8); rAssert(randBytes >= 0); - rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i", - cfg->config->blockSize, cfg->config->blockMACBytes, - cfg->config->blockMACRandBytes); + VLOG(1) << "fs block size = " << cfg->config->blockSize + << ", macBytes = " << cfg->config->blockMACBytes + << ", randBytes = " << cfg->config->blockMACRandBytes; } MACFileIO::~MACFileIO() {} -rel::Interface MACFileIO::interface() const { return MACFileIO_iface; } +Interface MACFileIO::interface() const { return MACFileIO_iface; } int MACFileIO::open(int flags) { return base->open(flags); } @@ -192,10 +187,10 @@ ssize_t MACFileIO::readOneBlock(const IORequest &req) const { if (fail > 0) { // uh oh.. long blockNum = req.offset / bs; - rWarning(_("MAC comparison failure in block %li"), blockNum); + RLOG(WARNING) << "MAC comparison failure in block " << blockNum; if (!warnOnly) { MemoryPool::release(mb); - throw ERROR(_("MAC comparison failure, refusing to read")); + throw Error(_("MAC comparison failure, refusing to read")); } } } @@ -204,7 +199,7 @@ ssize_t MACFileIO::readOneBlock(const IORequest &req) const { readSize -= headerSize; memcpy(req.data, tmp.data + headerSize, readSize); } else { - rDebug("readSize %i at offset %" PRIi64, (int)readSize, req.offset); + VLOG(1) << "readSize " << readSize << " at offset " << req.offset; if (readSize > 0) readSize = 0; } @@ -264,3 +259,5 @@ int MACFileIO::truncate(off_t size) { } bool MACFileIO::isWritable() const { return base->isWritable(); } + +} // namespace encfs diff --git a/encfs/MACFileIO.h b/encfs/MACFileIO.h index 94dac83..6bf4a25 100644 --- a/encfs/MACFileIO.h +++ b/encfs/MACFileIO.h @@ -21,9 +21,9 @@ #ifndef _MACFileIO_incl_ #define _MACFileIO_incl_ +#include #include #include -#include #include "BlockFileIO.h" #include "Cipher.h" @@ -31,6 +31,8 @@ #include "FSConfig.h" #include "Interface.h" +namespace encfs { + class Cipher; class FileIO; struct IORequest; @@ -42,11 +44,11 @@ class MACFileIO : public BlockFileIO { result in a warning message from encfs -- the garbled data will still be made available.. */ - MACFileIO(const shared_ptr &base, const FSConfigPtr &cfg); + MACFileIO(const std::shared_ptr &base, const FSConfigPtr &cfg); MACFileIO(); virtual ~MACFileIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual void setFileName(const char *fileName); virtual const char *getFileName() const; @@ -64,12 +66,14 @@ class MACFileIO : public BlockFileIO { virtual ssize_t readOneBlock(const IORequest &req) const; virtual bool writeOneBlock(const IORequest &req); - shared_ptr base; - shared_ptr cipher; + std::shared_ptr base; + std::shared_ptr cipher; CipherKey key; int macBytes; int randBytes; bool warnOnly; }; +} // namespace encfs + #endif diff --git a/encfs/MemoryPool.cpp b/encfs/MemoryPool.cpp index d21bdb0..532ac8c 100644 --- a/encfs/MemoryPool.cpp +++ b/encfs/MemoryPool.cpp @@ -20,9 +20,9 @@ #include "MemoryPool.h" +#include #include #include -#include #ifdef HAVE_VALGRIND_MEMCHECK_H #include @@ -33,7 +33,9 @@ #include -#define BLOCKDATA(BLOCK) (unsigned char *) BLOCK->data->data +#define BLOCKDATA(BLOCK) (unsigned char *)BLOCK->data->data + +namespace encfs { struct BlockList { BlockList *next; @@ -124,3 +126,5 @@ void MemoryPool::destroyAll() { block = next; } } + +} // namespace encfs diff --git a/encfs/MemoryPool.h b/encfs/MemoryPool.h index 657c689..fda3f27 100644 --- a/encfs/MemoryPool.h +++ b/encfs/MemoryPool.h @@ -21,6 +21,8 @@ #ifndef _MemoryPool_incl_ #define _MemoryPool_incl_ +namespace encfs { + struct MemBlock { unsigned char *data; @@ -46,4 +48,6 @@ void release(const MemBlock &el); void destroyAll(); } +} // namespace encfs + #endif diff --git a/encfs/Mutex.h b/encfs/Mutex.h index abb73b4..4592da7 100644 --- a/encfs/Mutex.h +++ b/encfs/Mutex.h @@ -23,7 +23,7 @@ #include -namespace rel { +namespace encfs { class Lock { public: @@ -50,6 +50,7 @@ inline Lock::~Lock() { } inline void Lock::leave() { _mutex = 0; } -} + +} // namespace encfs #endif diff --git a/encfs/NameIO.cpp b/encfs/NameIO.cpp index a7d8611..6495586 100644 --- a/encfs/NameIO.cpp +++ b/encfs/NameIO.cpp @@ -20,8 +20,7 @@ #include "NameIO.h" -#include -#include +#include "internal/easylogging++.h" #include // for static build. Need to reference the modules which are registered at // run-time, to ensure that the linker doesn't optimize them away. @@ -31,17 +30,18 @@ #include "BlockNameIO.h" #include "CipherKey.h" +#include "Error.h" #include "Interface.h" #include "NullNameIO.h" #include "StreamNameIO.h" using namespace std; -using namespace rel; -using namespace rlog; #define REF_MODULE(TYPE) \ if (!TYPE::Enabled()) cerr << "referenceModule: should never happen\n"; +namespace encfs { + static void AddSymbolReferences() { REF_MODULE(BlockNameIO) REF_MODULE(StreamNameIO) @@ -94,11 +94,10 @@ bool NameIO::Register(const char *name, const char *description, gNameIOMap->insert(make_pair(string(name), alg)); return true; } - -shared_ptr NameIO::New(const string &name, - const shared_ptr &cipher, - const CipherKey &key) { - shared_ptr result; +std::shared_ptr NameIO::New(const string &name, + const std::shared_ptr &cipher, + const CipherKey &key) { + std::shared_ptr result; if (gNameIOMap) { NameIOMap_t::const_iterator it = gNameIOMap->find(name); if (it != gNameIOMap->end()) { @@ -108,11 +107,10 @@ shared_ptr NameIO::New(const string &name, } return result; } - -shared_ptr NameIO::New(const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key) { - shared_ptr result; +std::shared_ptr NameIO::New(const Interface &iface, + const std::shared_ptr &cipher, + const CipherKey &key) { + std::shared_ptr result; if (gNameIOMap) { NameIOMap_t::const_iterator it; NameIOMap_t::const_iterator end = gNameIOMap->end(); @@ -139,11 +137,10 @@ void NameIO::setReverseEncryption(bool enable) { reverseEncryption = enable; } bool NameIO::getReverseEncryption() const { return reverseEncryption; } -std::string NameIO::recodePath(const char *path, - int (NameIO::*_length)(int) const, - int (NameIO::*_code)(const char *, int, - uint64_t *, char *, int) const, - uint64_t *iv) const { +std::string NameIO::recodePath( + const char *path, int (NameIO::*_length)(int) const, + int (NameIO::*_code)(const char *, int, uint64_t *, char *, int) const, + uint64_t *iv) const { string output; while (*path) { @@ -165,7 +162,7 @@ std::string NameIO::recodePath(const char *path, // figure out buffer sizes int approxLen = (this->*_length)(len); - if (approxLen <= 0) throw ERROR("Filename too small to decode"); + if (approxLen <= 0) throw Error("Filename too small to decode"); int bufSize = 0; BUFFER_INIT_S(codeBuf, 32, (unsigned int)approxLen + 1, bufSize) @@ -218,11 +215,13 @@ std::string NameIO::decodePath(const char *path, uint64_t *iv) const { return getReverseEncryption() ? _encodePath(path, iv) : _decodePath(path, iv); } -int NameIO::encodeName(const char *input, int length, char *output, int bufferLength) const { +int NameIO::encodeName(const char *input, int length, char *output, + int bufferLength) const { return encodeName(input, length, (uint64_t *)0, output, bufferLength); } -int NameIO::decodeName(const char *input, int length, char *output, int bufferLength) const { +int NameIO::decodeName(const char *input, int length, char *output, + int bufferLength) const { return decodeName(input, length, (uint64_t *)0, output, bufferLength); } @@ -290,3 +289,5 @@ int NameIO::decodeName( const char *path, int length, _decodeName( path, length, output ); } */ + +} // namespace encfs diff --git a/encfs/NameIO.h b/encfs/NameIO.h index 726325f..2264a64 100644 --- a/encfs/NameIO.h +++ b/encfs/NameIO.h @@ -22,47 +22,49 @@ #define _NameIO_incl_ #include -#include -#include #include #include +#include +#include #include #include "CipherKey.h" #include "Interface.h" +namespace encfs { + class Cipher; class NameIO { public: - typedef shared_ptr(*Constructor)(const rel::Interface &iface, - const shared_ptr &cipher, - const CipherKey &key); + typedef std::shared_ptr (*Constructor)( + const Interface &iface, const std::shared_ptr &cipher, + const CipherKey &key); struct Algorithm { std::string name; std::string description; - rel::Interface iface; + Interface iface; }; typedef std::list AlgorithmList; static AlgorithmList GetAlgorithmList(bool includeHidden = false); - static shared_ptr New(const rel::Interface &iface, - const shared_ptr &cipher, - const CipherKey &key); - static shared_ptr New(const std::string &name, - const shared_ptr &cipher, - const CipherKey &key); + static std::shared_ptr New(const Interface &iface, + const std::shared_ptr &cipher, + const CipherKey &key); + static std::shared_ptr New(const std::string &name, + const std::shared_ptr &cipher, + const CipherKey &key); static bool Register(const char *name, const char *description, - const rel::Interface &iface, Constructor constructor, + const Interface &iface, Constructor constructor, bool hidden = false); NameIO(); virtual ~NameIO(); - virtual rel::Interface interface() const = 0; + virtual Interface interface() const = 0; void setChainedNameIV(bool enable); bool getChainedNameIV() const; @@ -95,7 +97,8 @@ class NameIO { private: std::string recodePath(const char *path, int (NameIO::*codingLen)(int) const, int (NameIO::*codingFunc)(const char *, int, - uint64_t *, char *, int) const, + uint64_t *, char *, int) + const, uint64_t *iv) const; std::string _encodePath(const char *plaintextPath, uint64_t *iv) const; @@ -138,4 +141,6 @@ class NameIO { } \ } while (0); +} // namespace encfs + #endif diff --git a/encfs/NullCipher.cpp b/encfs/NullCipher.cpp index 3423b54..4c787d6 100644 --- a/encfs/NullCipher.cpp +++ b/encfs/NullCipher.cpp @@ -28,16 +28,17 @@ #include "Range.h" using namespace std; -using namespace rel; -using namespace rlog; + +namespace encfs { static Interface NullInterface("nullCipher", 1, 0, 0); static Range NullKeyRange(0); static Range NullBlockRange(1, 4096, 1); -static shared_ptr NewNullCipher(const Interface &iface, int keyLen) { +static std::shared_ptr NewNullCipher(const Interface &iface, + int keyLen) { (void)keyLen; - return shared_ptr(new NullCipher(iface)); + return std::shared_ptr(new NullCipher(iface)); } const bool HiddenCipher = true; @@ -61,8 +62,7 @@ class NullDestructor { NullDestructor &operator=(const NullDestructor &) { return *this; } void operator()(NullKey *&) {} }; - -shared_ptr gNullKey(new NullKey(), NullDestructor()); +std::shared_ptr gNullKey(new NullKey(), NullDestructor()); NullCipher::NullCipher(const Interface &iface_) { this->iface = iface_; } @@ -97,8 +97,8 @@ void NullCipher::writeKey(const CipherKey &, unsigned char *, const CipherKey &) {} bool NullCipher::compareKey(const CipherKey &A_, const CipherKey &B_) const { - shared_ptr A = dynamic_pointer_cast(A_); - shared_ptr B = dynamic_pointer_cast(B_); + std::shared_ptr A = dynamic_pointer_cast(A_); + std::shared_ptr B = dynamic_pointer_cast(B_); return A.get() == B.get(); } @@ -137,3 +137,5 @@ bool NullCipher::blockDecode(unsigned char *, int, uint64_t, } bool NullCipher::Enabled() { return true; } + +} // namespace encfs diff --git a/encfs/NullCipher.h b/encfs/NullCipher.h index 3dd63d8..2ac8a21 100644 --- a/encfs/NullCipher.h +++ b/encfs/NullCipher.h @@ -27,18 +27,20 @@ #include "CipherKey.h" #include "Interface.h" +namespace encfs { + /* Implements Cipher interface for a pass-through mode. May be useful for testing, but that's it. */ class NullCipher : public Cipher { - rel::Interface iface; + Interface iface; public: - NullCipher(const rel::Interface &iface); + NullCipher(const Interface &iface); virtual ~NullCipher(); - virtual rel::Interface interface() const; + virtual Interface interface() const; // create a new key based on a password virtual CipherKey newKey(const char *password, int passwdLength, @@ -80,4 +82,6 @@ class NullCipher : public Cipher { static bool Enabled(); }; +} // namespace encfs + #endif diff --git a/encfs/NullNameIO.cpp b/encfs/NullNameIO.cpp index 1dbcece..a2f6e34 100644 --- a/encfs/NullNameIO.cpp +++ b/encfs/NullNameIO.cpp @@ -24,15 +24,17 @@ #include #include "CipherKey.h" +#include "Error.h" #include "NameIO.h" +namespace encfs { + class Cipher; -using namespace rel; - -static shared_ptr NewNNIO(const Interface &, const shared_ptr &, - const CipherKey &) { - return shared_ptr(new NullNameIO()); +static std::shared_ptr NewNNIO(const Interface &, + const std::shared_ptr &, + const CipherKey &) { + return std::shared_ptr(new NullNameIO()); } static Interface NNIOIface("nameio/null", 1, 0, 0); @@ -76,3 +78,5 @@ int NullNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, } bool NullNameIO::Enabled() { return true; } + +} // namespace encfs diff --git a/encfs/NullNameIO.h b/encfs/NullNameIO.h index ab86f9a..6041006 100644 --- a/encfs/NullNameIO.h +++ b/encfs/NullNameIO.h @@ -23,20 +23,20 @@ #include -#include "rlog/Error.h" -#include "rlog/rlog.h" #include "Interface.h" #include "NameIO.h" +namespace encfs { + class NullNameIO : public NameIO { public: - static rel::Interface CurrentInterface(); + static Interface CurrentInterface(); NullNameIO(); virtual ~NullNameIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual int maxEncodedNameLen(int plaintextNameLen) const; virtual int maxDecodedNameLen(int encodedNameLen) const; @@ -53,4 +53,6 @@ class NullNameIO : public NameIO { private: }; +} // namespace encfs + #endif diff --git a/encfs/Range.h b/encfs/Range.h index 7ccd3e2..578a3b5 100644 --- a/encfs/Range.h +++ b/encfs/Range.h @@ -21,6 +21,8 @@ #ifndef _Range_incl_ #define _Range_incl_ +namespace encfs { + class Range { int minVal; int maxVal; @@ -86,4 +88,6 @@ inline int Range::max() const { return maxVal; } inline int Range::inc() const { return increment; } +} // namespace encfs + #endif diff --git a/encfs/RawFileIO.cpp b/encfs/RawFileIO.cpp index 34d126f..bf9f9b4 100644 --- a/encfs/RawFileIO.cpp +++ b/encfs/RawFileIO.cpp @@ -21,22 +21,25 @@ #ifdef linux #define _XOPEN_SOURCE 500 // pick up pread , pwrite #endif -#include -#include -#include -#include -#include +#include "internal/easylogging++.h" #include #include +#include +#include +#include +#include +#include "Error.h" #include "FileIO.h" #include "RawFileIO.h" using namespace std; -static rel::Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0); +namespace encfs { -FileIO *NewRawFileIO(const rel::Interface &iface) { +static Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0); + +FileIO *NewRawFileIO(const Interface &iface) { (void)iface; return new RawFileIO(); } @@ -70,7 +73,7 @@ RawFileIO::~RawFileIO() { if (_fd != -1) close(_fd); } -rel::Interface RawFileIO::interface() const { return RawFileIO_iface; } +Interface RawFileIO::interface() const { return RawFileIO_iface; } /* We shouldn't have to support all possible open flags, so untaint the flags @@ -83,14 +86,13 @@ rel::Interface RawFileIO::interface() const { return RawFileIO_iface; } */ int RawFileIO::open(int flags) { bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); - - rDebug("open call for %s file", requestWrite ? "writable" : "read only"); + VLOG(1) << "open call, requestWrite = " << requestWrite; int result = 0; // if we have a descriptor and it is writable, or we don't need writable.. if ((fd >= 0) && (canWrite || !requestWrite)) { - rDebug("using existing file descriptor"); + VLOG(1) << "using existing file descriptor"; result = fd; // success } else { int finalFlags = requestWrite ? O_RDWR : O_RDONLY; @@ -103,12 +105,12 @@ int RawFileIO::open(int flags) { int newFd = ::open(name.c_str(), finalFlags); - rDebug("open file with flags %i, result = %i", finalFlags, newFd); + VLOG(1) << "open file with flags " << finalFlags << ", result = " << newFd; if (newFd >= 0) { if (oldfd >= 0) { - rError("leaking FD?: oldfd = %i, fd = %i, newfd = %i", oldfd, fd, - newFd); + RLOG(ERROR) << "leaking FD?: oldfd = " << oldfd << ", fd = " << fd + << ", newfd = " << newFd; } // the old fd might still be in use, so just keep it around for @@ -118,12 +120,10 @@ int RawFileIO::open(int flags) { result = fd = newFd; } else { result = -errno; - rInfo("::open error: %s", strerror(errno)); + RLOG(INFO) << "::open error: " << strerror(errno); } } - if (result < 0) rInfo("file %s open failure: %i", name.c_str(), -result); - return result; } @@ -131,7 +131,9 @@ int RawFileIO::getAttr(struct stat *stbuf) const { int res = lstat(name.c_str(), stbuf); int eno = errno; - if (res < 0) rInfo("getAttr error on %s: %s", name.c_str(), strerror(eno)); + if (res < 0) { + RLOG(INFO) << "getAttr error on " << name << ": " << strerror(eno); + } return (res < 0) ? -eno : 0; } @@ -151,7 +153,7 @@ off_t RawFileIO::getSize() const { const_cast(this)->knownSize = true; return fileSize; } else { - rError("getSize on %s failed: %s", name.c_str(), strerror(errno)); + RLOG(ERROR) << "getSize on " << name << " failed: " << strerror(errno); return -1; } } else { @@ -165,8 +167,8 @@ ssize_t RawFileIO::read(const IORequest &req) const { ssize_t readSize = pread(fd, req.data, req.dataLen, req.offset); if (readSize < 0) { - rInfo("read failed at offset %" PRIi64 " for %i bytes: %s", req.offset, - req.dataLen, strerror(errno)); + RLOG(INFO) << "read failed at offset " << req.offset << " for " + << req.dataLen << " bytes: " << strerror(errno); } return readSize; @@ -186,8 +188,8 @@ bool RawFileIO::write(const IORequest &req) { if (writeSize < 0) { knownSize = false; - rInfo("write failed at offset %" PRIi64 " for %i bytes: %s", offset, - (int)bytes, strerror(errno)); + RLOG(INFO) << "write failed at offset " << offset << " for " << bytes + << " bytes: " << strerror(errno); return false; } @@ -198,8 +200,8 @@ bool RawFileIO::write(const IORequest &req) { } if (bytes != 0) { - rError("Write error: wrote %i bytes of %i, max retries reached\n", - (int)(req.dataLen - bytes), req.dataLen); + RLOG(ERROR) << "Write error: wrote " << req.dataLen - bytes << " bytes of " + << req.dataLen << ", max retries reached"; knownSize = false; return false; } else { @@ -225,8 +227,8 @@ int RawFileIO::truncate(off_t size) { if (res < 0) { int eno = errno; - rInfo("truncate failed for %s (%i) size %" PRIi64 ", error %s", - name.c_str(), fd, size, strerror(eno)); + RLOG(INFO) << "truncate failed for " << name << " (" << fd << ") size " + << size << ", error " << strerror(eno); res = -eno; knownSize = false; } else { @@ -239,3 +241,5 @@ int RawFileIO::truncate(off_t size) { } bool RawFileIO::isWritable() const { return canWrite; } + +} // namespace encfs diff --git a/encfs/RawFileIO.h b/encfs/RawFileIO.h index 12b36d1..1214a56 100644 --- a/encfs/RawFileIO.h +++ b/encfs/RawFileIO.h @@ -21,11 +21,13 @@ #ifndef _RawFileIO_incl_ #define _RawFileIO_incl_ -#include #include +#include -#include "Interface.h" #include "FileIO.h" +#include "Interface.h" + +namespace encfs { class RawFileIO : public FileIO { public: @@ -33,7 +35,7 @@ class RawFileIO : public FileIO { RawFileIO(const std::string &fileName); virtual ~RawFileIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual void setFileName(const char *fileName); virtual const char *getFileName() const; @@ -61,4 +63,6 @@ class RawFileIO : public FileIO { bool canWrite; }; +} // namespace encfs + #endif diff --git a/encfs/SSL_Cipher.cpp b/encfs/SSL_Cipher.cpp index ebac89d..3fb7163 100644 --- a/encfs/SSL_Cipher.cpp +++ b/encfs/SSL_Cipher.cpp @@ -18,6 +18,8 @@ * along with this program. If not, see . */ +#include "internal/easylogging++.h" +#include #include #include #include @@ -25,27 +27,21 @@ #include #include #include -#include -#include +#include #include #include -#include -#include #include "Cipher.h" +#include "Error.h" #include "Interface.h" #include "Mutex.h" #include "Range.h" #include "SSL_Cipher.h" #include "intl/gettext.h" -namespace rlog { -class RLogChannel; -} // namespace rlog - using namespace std; -using namespace rel; -using namespace rlog; + +namespace encfs { const int MAX_KEYLENGTH = 32; // in bytes (256 bit) const int MAX_IVLENGTH = 16; // 128 bit (AES block size, Blowfish has 64) @@ -162,7 +158,7 @@ static Interface AESInterface("ssl/aes", 3, 0, 2); static Range BFKeyRange(128, 256, 32); static Range BFBlockRange(64, 4096, 8); -static shared_ptr NewBFCipher(const Interface &iface, int keyLen) { +static std::shared_ptr NewBFCipher(const Interface &iface, int keyLen) { if (keyLen <= 0) keyLen = 160; keyLen = BFKeyRange.closest(keyLen); @@ -170,7 +166,7 @@ static shared_ptr NewBFCipher(const Interface &iface, int keyLen) { const EVP_CIPHER *blockCipher = EVP_bf_cbc(); const EVP_CIPHER *streamCipher = EVP_bf_cfb(); - return shared_ptr(new SSL_Cipher( + return std::shared_ptr(new SSL_Cipher( iface, BlowfishInterface, blockCipher, streamCipher, keyLen / 8)); } @@ -186,7 +182,8 @@ static bool BF_Cipher_registered = static Range AESKeyRange(128, 256, 64); static Range AESBlockRange(64, 4096, 16); -static shared_ptr NewAESCipher(const Interface &iface, int keyLen) { +static std::shared_ptr NewAESCipher(const Interface &iface, + int keyLen) { if (keyLen <= 0) keyLen = 192; keyLen = AESKeyRange.closest(keyLen); @@ -212,8 +209,8 @@ static shared_ptr NewAESCipher(const Interface &iface, int keyLen) { break; } - return shared_ptr(new SSL_Cipher(iface, AESInterface, blockCipher, - streamCipher, keyLen / 8)); + return std::shared_ptr(new SSL_Cipher( + iface, AESInterface, blockCipher, streamCipher, keyLen / 8)); } static bool AES_Cipher_registered = @@ -275,14 +272,14 @@ SSLKey::~SSLKey() { pthread_mutex_destroy(&mutex); } -inline unsigned char *KeyData(const shared_ptr &key) { +inline unsigned char *KeyData(const std::shared_ptr &key) { return key->buffer; } -inline unsigned char *IVData(const shared_ptr &key) { +inline unsigned char *IVData(const std::shared_ptr &key) { return key->buffer + key->keySize; } -void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, +void initKey(const std::shared_ptr &key, const EVP_CIPHER *_blockCipher, const EVP_CIPHER *_streamCipher, int _keySize) { Lock lock(key->mutex); // initialize the cipher context once so that we don't have to do it for @@ -316,8 +313,6 @@ void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, HMAC_Init_ex(&key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0); } -static RLogChannel *CipherInfo = DEF_CHANNEL("info/cipher", Log_Info); - SSL_Cipher::SSL_Cipher(const Interface &iface_, const Interface &realIface_, const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, int keySize_) { @@ -330,16 +325,15 @@ SSL_Cipher::SSL_Cipher(const Interface &iface_, const Interface &realIface_, rAssert(_ivLength == 8 || _ivLength == 16); - rLog(CipherInfo, "allocated cipher %s, keySize %i, ivlength %i", - iface.name().c_str(), _keySize, _ivLength); + VLOG(1) << "allocated cipher " << iface.name() << ", keySize " << _keySize + << ", ivlength " << _ivLength; if ((EVP_CIPHER_key_length(_blockCipher) != (int)_keySize) && iface.current() == 1) { - rWarning( - "Running in backward compatibilty mode for 1.0 - \n" - "key is really %i bits, not %i.\n" - "Consider creating a new filesystem and moving your data.", - EVP_CIPHER_key_length(_blockCipher) * 8, _keySize * 8); + RLOG(WARNING) << "Running in backward compatibilty mode for 1.0 - " + "key is really " + << EVP_CIPHER_key_length(_blockCipher) * 8 << " bits, not " + << _keySize * 8; } } @@ -357,7 +351,7 @@ Interface SSL_Cipher::interface() const { return realIface; } CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, int &iterationCount, long desiredDuration, const unsigned char *salt, int saltLen) { - shared_ptr key(new SSLKey(_keySize, _ivLength)); + std::shared_ptr key(new SSLKey(_keySize, _ivLength)); if (iterationCount == 0) { // timed run, fills in iteration count @@ -365,7 +359,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, TimedPBKDF2(password, passwdLength, salt, saltLen, _keySize + _ivLength, KeyData(key), 1000 * desiredDuration); if (res <= 0) { - rWarning("openssl error, PBKDF2 failed"); + RLOG(WARNING) << "openssl error, PBKDF2 failed"; return CipherKey(); } else iterationCount = res; @@ -374,7 +368,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, if (PKCS5_PBKDF2_HMAC_SHA1( password, passwdLength, const_cast(salt), saltLen, iterationCount, _keySize + _ivLength, KeyData(key)) != 1) { - rWarning("openssl error, PBKDF2 failed"); + RLOG(WARNING) << "openssl error, PBKDF2 failed"; return CipherKey(); } } @@ -385,7 +379,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, } CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) { - shared_ptr key(new SSLKey(_keySize, _ivLength)); + std::shared_ptr key(new SSLKey(_keySize, _ivLength)); int bytes = 0; if (iface.current() > 1) { @@ -397,8 +391,8 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) { // the reason for moving from EVP_BytesToKey to BytesToKey function.. if (bytes != (int)_keySize) { - rWarning("newKey: BytesToKey returned %i, expecting %i key bytes", bytes, - _keySize); + RLOG(WARNING) << "newKey: BytesToKey returned " << bytes << ", expecting " + << _keySize << " key bytes"; } } else { // for backward compatibility with filesystems created with 1:0 @@ -429,13 +423,13 @@ CipherKey SSL_Cipher::newRandomKey() { if (!randomize(tmpBuf, bufLen, true) || !randomize(saltBuf, saltLen, true)) return CipherKey(); - shared_ptr key(new SSLKey(_keySize, _ivLength)); + std::shared_ptr key(new SSLKey(_keySize, _ivLength)); // doesn't need to be versioned, because a random key is a random key.. // Doesn't need to be reproducable.. if (PKCS5_PBKDF2_HMAC_SHA1((char *)tmpBuf, bufLen, saltBuf, saltLen, 1000, _keySize + _ivLength, KeyData(key)) != 1) { - rWarning("openssl error, PBKDF2 failed"); + RLOG(WARNING) << "openssl error, PBKDF2 failed"; return CipherKey(); } @@ -496,25 +490,28 @@ bool SSL_Cipher::randomize(unsigned char *buf, int len, // to avoid warnings of uninitialized data from valgrind memset(buf, 0, len); int result; - if (strongRandom) + if (strongRandom) { result = RAND_bytes(buf, len); - else + } else { result = RAND_pseudo_bytes(buf, len); + } if (result != 1) { char errStr[120]; // specs require string at least 120 bytes long.. unsigned long errVal = 0; - if ((errVal = ERR_get_error()) != 0) - rWarning("openssl error: %s", ERR_error_string(errVal, errStr)); + if ((errVal = ERR_get_error()) != 0) { + RLOG(WARNING) << "openssl error: " << ERR_error_string(errVal, errStr); + } return false; - } else + } else { return true; + } } uint64_t SSL_Cipher::MAC_64(const unsigned char *data, int len, const CipherKey &key, uint64_t *chainedIV) const { - shared_ptr mk = dynamic_pointer_cast(key); + std::shared_ptr mk = dynamic_pointer_cast(key); uint64_t tmp = _checksum_64(mk.get(), data, len, chainedIV); if (chainedIV) *chainedIV = tmp; @@ -524,7 +521,7 @@ uint64_t SSL_Cipher::MAC_64(const unsigned char *data, int len, CipherKey SSL_Cipher::readKey(const unsigned char *data, const CipherKey &masterKey, bool checkKey) { - shared_ptr mk = dynamic_pointer_cast(masterKey); + std::shared_ptr mk = dynamic_pointer_cast(masterKey); rAssert(mk->keySize == _keySize); unsigned char tmpBuf[MAX_KEYLENGTH + MAX_IVLENGTH]; @@ -540,13 +537,14 @@ CipherKey SSL_Cipher::readKey(const unsigned char *data, // check for success unsigned int checksum2 = MAC_32(tmpBuf, _keySize + _ivLength, masterKey); if (checksum2 != checksum && checkKey) { - rDebug("checksum mismatch: expected %u, got %u", checksum, checksum2); - rDebug("on decode of %i bytes", _keySize + _ivLength); + VLOG(1) << "checksum mismatch: expected " << checksum << ", got " + << checksum2; + VLOG(1) << "on decode of " << _keySize + _ivLength << " bytes"; memset(tmpBuf, 0, sizeof(tmpBuf)); return CipherKey(); } - shared_ptr key(new SSLKey(_keySize, _ivLength)); + std::shared_ptr key(new SSLKey(_keySize, _ivLength)); memcpy(key->buffer, tmpBuf, _keySize + _ivLength); memset(tmpBuf, 0, sizeof(tmpBuf)); @@ -558,11 +556,11 @@ CipherKey SSL_Cipher::readKey(const unsigned char *data, void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, const CipherKey &masterKey) { - shared_ptr key = dynamic_pointer_cast(ckey); + std::shared_ptr key = dynamic_pointer_cast(ckey); rAssert(key->keySize == _keySize); rAssert(key->ivLength == _ivLength); - shared_ptr mk = dynamic_pointer_cast(masterKey); + std::shared_ptr mk = dynamic_pointer_cast(masterKey); rAssert(mk->keySize == _keySize); rAssert(mk->ivLength == _ivLength); @@ -586,8 +584,8 @@ void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, } bool SSL_Cipher::compareKey(const CipherKey &A, const CipherKey &B) const { - shared_ptr key1 = dynamic_pointer_cast(A); - shared_ptr key2 = dynamic_pointer_cast(B); + std::shared_ptr key1 = dynamic_pointer_cast(A); + std::shared_ptr key2 = dynamic_pointer_cast(B); rAssert(key1->keySize == _keySize); rAssert(key2->keySize == _keySize); @@ -625,7 +623,7 @@ int SSL_Cipher::cipherBlockSize() const { * requirement for "seed" is that is must be unique. */ void SSL_Cipher::setIVec(unsigned char *ivec, uint64_t seed, - const shared_ptr &key) const { + const std::shared_ptr &key) const { if (iface.current() >= 3) { memcpy(ivec, IVData(key), _ivLength); @@ -657,7 +655,7 @@ void SSL_Cipher::setIVec(unsigned char *ivec, uint64_t seed, decrypting the file). */ void SSL_Cipher::setIVec_old(unsigned char *ivec, unsigned int seed, - const shared_ptr &key) const { + const std::shared_ptr &key) const { /* These multiplication constants chosen as they represent (non optimal) Golumb rulers, the idea being to spread around the information in the seed. @@ -721,7 +719,7 @@ static void unshuffleBytes(unsigned char *buf, int size) { bool SSL_Cipher::streamEncode(unsigned char *buf, int size, uint64_t iv64, const CipherKey &ckey) const { rAssert(size > 0); - shared_ptr key = dynamic_pointer_cast(ckey); + std::shared_ptr key = dynamic_pointer_cast(ckey); rAssert(key->keySize == _keySize); rAssert(key->ivLength == _ivLength); @@ -747,8 +745,8 @@ bool SSL_Cipher::streamEncode(unsigned char *buf, int size, uint64_t iv64, dstLen += tmpLen; if (dstLen != size) { - rError("encoding %i bytes, got back %i (%i in final_ex)", size, dstLen, - tmpLen); + RLOG(ERROR) << "encoding " << size << " bytes, got back " << dstLen << " (" + << tmpLen << " in final_ex)"; } return true; @@ -757,7 +755,7 @@ bool SSL_Cipher::streamEncode(unsigned char *buf, int size, uint64_t iv64, bool SSL_Cipher::streamDecode(unsigned char *buf, int size, uint64_t iv64, const CipherKey &ckey) const { rAssert(size > 0); - shared_ptr key = dynamic_pointer_cast(ckey); + std::shared_ptr key = dynamic_pointer_cast(ckey); rAssert(key->keySize == _keySize); rAssert(key->ivLength == _ivLength); @@ -783,8 +781,8 @@ bool SSL_Cipher::streamDecode(unsigned char *buf, int size, uint64_t iv64, dstLen += tmpLen; if (dstLen != size) { - rError("encoding %i bytes, got back %i (%i in final_ex)", size, dstLen, - tmpLen); + RLOG(ERROR) << "decoding " << size << " bytes, got back " << dstLen << " (" + << tmpLen << " in final_ex)"; } return true; @@ -793,14 +791,14 @@ bool SSL_Cipher::streamDecode(unsigned char *buf, int size, uint64_t iv64, bool SSL_Cipher::blockEncode(unsigned char *buf, int size, uint64_t iv64, const CipherKey &ckey) const { rAssert(size > 0); - shared_ptr key = dynamic_pointer_cast(ckey); + std::shared_ptr key = dynamic_pointer_cast(ckey); rAssert(key->keySize == _keySize); rAssert(key->ivLength == _ivLength); // data must be integer number of blocks const int blockMod = size % EVP_CIPHER_CTX_block_size(&key->block_enc); if (blockMod != 0) - throw ERROR("Invalid data size, not multiple of block size"); + throw Error("Invalid data size, not multiple of block size"); Lock lock(key->mutex); @@ -815,8 +813,8 @@ bool SSL_Cipher::blockEncode(unsigned char *buf, int size, uint64_t iv64, dstLen += tmpLen; if (dstLen != size) { - rError("encoding %i bytes, got back %i (%i in final_ex)", size, dstLen, - tmpLen); + RLOG(ERROR) << "encoding " << size << " bytes, got back " << dstLen << " (" + << tmpLen << " in final_ex)"; } return true; @@ -825,14 +823,14 @@ bool SSL_Cipher::blockEncode(unsigned char *buf, int size, uint64_t iv64, bool SSL_Cipher::blockDecode(unsigned char *buf, int size, uint64_t iv64, const CipherKey &ckey) const { rAssert(size > 0); - shared_ptr key = dynamic_pointer_cast(ckey); + std::shared_ptr key = dynamic_pointer_cast(ckey); rAssert(key->keySize == _keySize); rAssert(key->ivLength == _ivLength); // data must be integer number of blocks const int blockMod = size % EVP_CIPHER_CTX_block_size(&key->block_dec); if (blockMod != 0) - throw ERROR("Invalid data size, not multiple of block size"); + throw Error("Invalid data size, not multiple of block size"); Lock lock(key->mutex); @@ -847,11 +845,13 @@ bool SSL_Cipher::blockDecode(unsigned char *buf, int size, uint64_t iv64, dstLen += tmpLen; if (dstLen != size) { - rError("decoding %i bytes, got back %i (%i in final_ex)", size, dstLen, - tmpLen); + RLOG(ERROR) << "decoding " << size << " bytes, got back " << dstLen << " (" + << tmpLen << " in final_ex)"; } return true; } bool SSL_Cipher::Enabled() { return true; } + +} // namespace encfs diff --git a/encfs/SSL_Cipher.h b/encfs/SSL_Cipher.h index 76badc1..f9613ff 100644 --- a/encfs/SSL_Cipher.h +++ b/encfs/SSL_Cipher.h @@ -21,20 +21,23 @@ #ifndef _SSL_Cipher_incl_ #define _SSL_Cipher_incl_ -#include #include +#include #include "Cipher.h" #include "CipherKey.h" #include "Interface.h" -class SSLKey; #ifndef EVP_CIPHER struct evp_cipher_st; typedef struct evp_cipher_st EVP_CIPHER; #endif +namespace encfs { + +class SSLKey; + /* Implements Cipher interface for OpenSSL's ciphers. @@ -74,21 +77,21 @@ typedef struct evp_cipher_st EVP_CIPHER; simpler to reuse the encryption algorithm as is. */ class SSL_Cipher : public Cipher { - rel::Interface iface; - rel::Interface realIface; + Interface iface; + Interface realIface; const EVP_CIPHER *_blockCipher; const EVP_CIPHER *_streamCipher; unsigned int _keySize; // in bytes unsigned int _ivLength; public: - SSL_Cipher(const rel::Interface &iface, const rel::Interface &realIface, + SSL_Cipher(const Interface &iface, const Interface &realIface, const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, int keyLength); virtual ~SSL_Cipher(); // returns the real interface, not the one we're emulating (if any).. - virtual rel::Interface interface() const; + virtual Interface interface() const; // create a new key based on a password virtual CipherKey newKey(const char *password, int passwdLength, @@ -140,11 +143,13 @@ class SSL_Cipher : public Cipher { private: void setIVec(unsigned char *ivec, uint64_t seed, - const shared_ptr &key) const; + const std::shared_ptr &key) const; // deprecated - for backward compatibility void setIVec_old(unsigned char *ivec, unsigned int seed, - const shared_ptr &key) const; + const std::shared_ptr &key) const; }; +} // namespace encfs + #endif diff --git a/encfs/StreamNameIO.cpp b/encfs/StreamNameIO.cpp index 17efe36..e07c4e5 100644 --- a/encfs/StreamNameIO.cpp +++ b/encfs/StreamNameIO.cpp @@ -20,23 +20,24 @@ #include "StreamNameIO.h" -#include -#include +#include "internal/easylogging++.h" #include #include "Cipher.h" #include "CipherKey.h" +#include "Error.h" #include "NameIO.h" #include "base64.h" #include "intl/gettext.h" -using namespace rel; using namespace std; -static shared_ptr NewStreamNameIO(const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key) { - return shared_ptr(new StreamNameIO(iface, cipher, key)); +namespace encfs { + +static std::shared_ptr NewStreamNameIO( + const Interface &iface, const std::shared_ptr &cipher, + const CipherKey &key) { + return std::shared_ptr(new StreamNameIO(iface, cipher, key)); } static bool StreamIO_registered = NameIO::Register( @@ -70,8 +71,8 @@ Interface StreamNameIO::CurrentInterface() { return Interface("nameio/stream", 2, 1, 2); } -StreamNameIO::StreamNameIO(const rel::Interface &iface, - const shared_ptr &cipher, +StreamNameIO::StreamNameIO(const Interface &iface, + const std::shared_ptr &cipher, const CipherKey &key) : _interface(iface.current()), _cipher(cipher), _key(key) {} @@ -90,7 +91,8 @@ int StreamNameIO::maxDecodedNameLen(int encodedStreamLen) const { } int StreamNameIO::encodeName(const char *plaintextName, int length, - uint64_t *iv, char *encodedName, int bufferLength) const { + uint64_t *iv, char *encodedName, + int bufferLength) const { uint64_t tmpIV = 0; if (iv && _interface >= 2) tmpIV = *iv; @@ -133,7 +135,7 @@ int StreamNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, int decodedStreamLen = decLen256 - 2; rAssert(decodedStreamLen <= bufferLength); - if (decodedStreamLen <= 0) throw ERROR("Filename too small to decode"); + if (decodedStreamLen <= 0) throw Error("Filename too small to decode"); BUFFER_INIT(tmpBuf, 32, (unsigned int)length); @@ -172,12 +174,14 @@ int StreamNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, BUFFER_RESET(tmpBuf); if (mac2 != mac) { - rDebug("checksum mismatch: expected %u, got %u", mac, mac2); - rDebug("on decode of %i bytes", decodedStreamLen); - throw ERROR("checksum mismatch in filename decode"); + VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2; + VLOG(1) << "on decode of " << decodedStreamLen << " bytes"; + throw Error("checksum mismatch in filename decode"); } return decodedStreamLen; } bool StreamNameIO::Enabled() { return true; } + +} // namespace encfs diff --git a/encfs/StreamNameIO.h b/encfs/StreamNameIO.h index 32a5327..5bb3134 100644 --- a/encfs/StreamNameIO.h +++ b/encfs/StreamNameIO.h @@ -21,24 +21,26 @@ #ifndef _StreamNameIO_incl_ #define _StreamNameIO_incl_ -#include #include +#include #include "CipherKey.h" #include "Interface.h" #include "NameIO.h" +namespace encfs { + class Cipher; class StreamNameIO : public NameIO { public: - static rel::Interface CurrentInterface(); + static Interface CurrentInterface(); - StreamNameIO(const rel::Interface &iface, const shared_ptr &cipher, + StreamNameIO(const Interface &iface, const std::shared_ptr &cipher, const CipherKey &key); virtual ~StreamNameIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual int maxEncodedNameLen(int plaintextNameLen) const; virtual int maxDecodedNameLen(int encodedNameLen) const; @@ -54,8 +56,10 @@ class StreamNameIO : public NameIO { private: int _interface; - shared_ptr _cipher; + std::shared_ptr _cipher; CipherKey _key; }; +} // namespace encfs + #endif diff --git a/encfs/XmlReader.cpp b/encfs/XmlReader.cpp index c5338fd..c0aaf9b 100644 --- a/encfs/XmlReader.cpp +++ b/encfs/XmlReader.cpp @@ -20,23 +20,17 @@ #include "XmlReader.h" -#include -#include -#include -#include +#include // for remove_if +#include // for NULL +#include // for shared_ptr -#include -#include -#include +#include // for XMLElement, XMLNode, XMLDocument (ptr only) -#include - -#include -#include - -#include "base64.h" +#include "Error.h" #include "Interface.h" -#include "shared_ptr.h" +#include "base64.h" + +namespace encfs { XmlValue::~XmlValue() {} @@ -44,7 +38,7 @@ XmlValuePtr XmlValue::operator[](const char *path) const { return find(path); } XmlValuePtr XmlValue::find(const char *path) const { // Shouldn't get here. - rError("in XmlValue::find(%s)", path); + RLOG(ERROR) << "in XmlValue::find for path " << path; return XmlValuePtr(); } @@ -88,7 +82,8 @@ bool XmlValue::read(const char *path, bool *out) const { return true; } -bool XmlValue::readB64(const char *path, unsigned char *data, int length) const { +bool XmlValue::readB64(const char *path, unsigned char *data, + int length) const { XmlValuePtr value = find(path); if (!value) return false; @@ -98,25 +93,26 @@ bool XmlValue::readB64(const char *path, unsigned char *data, int length) const int decodedSize = B64ToB256Bytes(s.size()); if (decodedSize != length) { - rError("decoding bytes len %d, expecting output len %d, got %d", s.size(), - length, decodedSize); + RLOG(ERROR) << "decoding bytes len " << s.size() + << ", expecting output len " << length << ", got " + << decodedSize; return false; } if (!B64StandardDecode(data, (unsigned char *)s.data(), s.size())) { - rError("B64 decode failure on \"%s\"", s.c_str()); + RLOG(ERROR) << "B64 decode failure on \"" << s << "\""; return false; } return true; } -bool XmlValue::read(const char *path, rel::Interface *out) const { +bool XmlValue::read(const char *path, Interface *out) const { XmlValuePtr node = find(path); if (!node) return false; bool ok = node->read("name", &out->name()) && node->read("major", &out->current()) && - node->read("minor", &out->revision()); + node->read("minor", &out->revision()); return ok; } @@ -161,7 +157,7 @@ class XmlNode : virtual public XmlValue { }; struct XmlReader::XmlReaderData { - shared_ptr doc; + std::shared_ptr doc; }; XmlReader::XmlReader() : pd(new XmlReaderData()) {} @@ -178,15 +174,17 @@ bool XmlReader::load(const char *fileName) { XmlValuePtr XmlReader::operator[](const char *name) const { tinyxml2::XMLNode *node = pd->doc->FirstChildElement(name); if (node == NULL) { - rError("Xml node %s not found", name); + RLOG(ERROR) << "Xml node " << name << " not found"; return XmlValuePtr(new XmlValue()); } tinyxml2::XMLElement *element = node->ToElement(); if (element == NULL) { - rError("Xml node %s not element", name); + RLOG(ERROR) << "Xml node " << name << " not element"; return XmlValuePtr(new XmlValue()); } return XmlValuePtr(new XmlNode(element)); } + +} // namespace encfs diff --git a/encfs/XmlReader.h b/encfs/XmlReader.h index bae8e58..4a56b16 100644 --- a/encfs/XmlReader.h +++ b/encfs/XmlReader.h @@ -21,12 +21,15 @@ #ifndef _XmlReader_incl_ #define _XmlReader_incl_ +#include #include + #include "Interface.h" -#include "shared_ptr.h" + +namespace encfs { class XmlValue; -typedef shared_ptr XmlValuePtr; +typedef std::shared_ptr XmlValuePtr; class XmlValue { std::string value; @@ -49,7 +52,7 @@ class XmlValue { bool read(const char *path, double *out) const; bool read(const char *path, bool *out) const; - bool read(const char *path, rel::Interface *out) const; + bool read(const char *path, Interface *out) const; protected: virtual XmlValuePtr find(const char *name) const; @@ -66,7 +69,9 @@ class XmlReader { private: struct XmlReaderData; - shared_ptr pd; + std::shared_ptr pd; }; +} // namespace encfs + #endif diff --git a/encfs/autosprintf.cpp b/encfs/autosprintf.cpp index 16e8549..c32aa8f 100644 --- a/encfs/autosprintf.cpp +++ b/encfs/autosprintf.cpp @@ -27,12 +27,10 @@ /* Specification. */ #include "autosprintf.h" -#include -#include -//#include "lib-asprintf.h" -#include -#include -#include +#include // for va_list +#include // for NULL, vasprintf +#include // for free +#include // for strdup namespace gnu { diff --git a/encfs/autosprintf.h b/encfs/autosprintf.h index 2ebb66b..9303168 100644 --- a/encfs/autosprintf.h +++ b/encfs/autosprintf.h @@ -32,8 +32,8 @@ #endif #endif -#include #include +#include namespace gnu { /* A temporary object, usually allocated on the stack, representing diff --git a/encfs/base64.cpp b/encfs/base64.cpp index af080f2..1c69164 100644 --- a/encfs/base64.cpp +++ b/encfs/base64.cpp @@ -20,10 +20,11 @@ #include "base64.h" -#include -#include +#include // for toupper -#include +#include "Error.h" + +namespace encfs { // change between two powers of two, stored as the low bits of the bytes in the // arrays. @@ -202,7 +203,7 @@ bool B64StandardDecode(unsigned char *out, const unsigned char *in, int inLen) { while (in < end) { unsigned char v = *in++; if (v > 'z') { - rError("Invalid character: %d", (unsigned int)v); + RLOG(ERROR) << "Invalid character: " << (unsigned int)v; return false; } unsigned char c = d[v]; @@ -211,7 +212,7 @@ bool B64StandardDecode(unsigned char *out, const unsigned char *in, int inLen) { case WHITESPACE: continue; /* skip whitespace */ case INVALID: - rError("Invalid character: %d", (unsigned int)v); + RLOG(ERROR) << "Invalid character: " << (unsigned int)v; return false; /* invalid input, return error */ case EQUALS: /* pad character, end of data */ in = end; @@ -276,3 +277,5 @@ std::string B64StandardEncode(std::vector inputBuffer) { } return encodedString; } + +} // namespace encfs diff --git a/encfs/base64.h b/encfs/base64.h index d88f681..796cfd6 100644 --- a/encfs/base64.h +++ b/encfs/base64.h @@ -21,8 +21,10 @@ #ifndef _base64_incl_ #define _base64_incl_ -#include -#include +#include // for string +#include // for vector + +namespace encfs { inline int B64ToB256Bytes(int numB64Bytes) { return (numB64Bytes * 6) / 8; // round down @@ -68,8 +70,11 @@ void AsciiToB32(unsigned char *out, const unsigned char *in, int length); // Decode standard B64 into the output array. // Used only to decode legacy Boost XML serialized config format. // The output size must be at least B64ToB256Bytes(inputLen). -bool B64StandardDecode(unsigned char *out, const unsigned char *in, int inputLen); +bool B64StandardDecode(unsigned char *out, const unsigned char *in, + int inputLen); std::string B64StandardEncode(std::vector input); +} // namespace encfs + #endif diff --git a/encfs/encfs.cpp b/encfs/encfs.cpp index de4eac1..dc72369 100644 --- a/encfs/encfs.cpp +++ b/encfs/encfs.cpp @@ -17,8 +17,13 @@ #include "encfs.h" +#include +#include +#include +#include #include #include +#include #include #include #include @@ -26,11 +31,6 @@ #include #include #include -#include -#include -#include -#include -#include #ifdef linux #include #endif @@ -41,25 +41,18 @@ #include #endif -#include -#include +#include "internal/easylogging++.h" #include #include #include #include "Context.h" #include "DirNode.h" +#include "Error.h" #include "FileNode.h" #include "FileUtils.h" #include "fuse.h" -namespace rel { -class Lock; -} // namespace rel -namespace rlog { -class RLogChannel; -} // namespace rlog - #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif @@ -68,12 +61,10 @@ class RLogChannel; using namespace std; using namespace std::placeholders; -using namespace rlog; -using rel::Lock; -#define GET_FN(ctx, finfo) ctx->getNode((void *)(uintptr_t) finfo->fh) +namespace encfs { -static RLogChannel *Info = DEF_CHANNEL("info", Log_Info); +#define GET_FN(ctx, finfo) ctx->getNode((void *)(uintptr_t)finfo->fh) static EncFS_Context *context() { return (EncFS_Context *)fuse_get_context()->private_data; @@ -97,24 +88,25 @@ static int withCipherPath(const char *opName, const char *path, EncFS_Context *ctx = context(); int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { string cyName = FSRoot->cipherPath(path); - rLog(Info, "%s %s", opName, cyName.c_str()); + VLOG(1) << "op: " << opName << " : " << cyName; res = op(ctx, cyName); if (res == -1) { int eno = errno; - rInfo("%s error: %s", opName, strerror(eno)); + VLOG(1) << "op: " << opName << " error: " << strerror(eno); res = -eno; - } else if (!passReturnCode) + } else if (!passReturnCode) { res = ESUCCESS; - } catch (rlog::Error &err) { - rError("withCipherPath: error caught in %s: %s", opName, err.message()); - err.log(_RLWarningChannel); + } + } catch (encfs::Error &err) { + RLOG(ERROR) << "withCipherPath: error caught in " << opName << ": " + << err.what(); } return res; } @@ -126,11 +118,11 @@ static int withFileNode(const char *opName, const char *path, EncFS_Context *ctx = context(); int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { - shared_ptr fnode; + std::shared_ptr fnode; if (fi != NULL) fnode = GET_FN(ctx, fi); @@ -138,29 +130,28 @@ static int withFileNode(const char *opName, const char *path, fnode = FSRoot->lookupNode(path, opName); rAssert(fnode.get() != NULL); - rLog(Info, "%s %s", opName, fnode->cipherName()); + VLOG(1) << "op: " << opName << " : " << fnode->cipherName(); // check that we're not recursing into the mount point itself if (FSRoot->touchesMountpoint(fnode->cipherName())) { - rInfo("%s error: Tried to touch mountpoint: '%s'", - opName, fnode->cipherName()); - return res; // still -EIO + VLOG(1) << "op: " << opName << " error: Tried to touch mountpoint: '" + << fnode->cipherName() << "'"; + return res; // still -EIO } res = op(fnode.get()); - if (res < 0) rInfo("%s error: %s", opName, strerror(-res)); - } catch (rlog::Error &err) { - rError("withFileNode: error caught in %s: %s", opName, err.message()); - err.log(_RLWarningChannel); + LOG_IF(res < 0, INFO) << "op: " << opName << " error: " << strerror(-res); + } catch (encfs::Error &err) { + RLOG(ERROR) << "withFileNode: error caught in " << opName << ": " + << err.what(); } return res; } /* - The rLog messages below always prints out encrypted filenames, not - plaintext. The reason is so that it isn't possible to leak information - about the encrypted data through rlog interfaces. + The log messages below always print encrypted filenames, not + plaintext. This avoids possibly leaking information to log files. The purpose of this layer of code is to take the FUSE request and dispatch to the internal interfaces. Any marshaling of arguments and return types @@ -171,7 +162,7 @@ int _do_getattr(FileNode *fnode, struct stat *stbuf) { int res = fnode->getAttr(stbuf); if (res == ESUCCESS && S_ISLNK(stbuf->st_mode)) { EncFS_Context *ctx = context(); - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (FSRoot) { // determine plaintext link size.. Easiest to read and decrypt.. std::vector buf(stbuf->st_size + 1, '\0'); @@ -185,8 +176,9 @@ int _do_getattr(FileNode *fnode, struct stat *stbuf) { stbuf->st_size = FSRoot->plainPath(buf.data()).length(); res = ESUCCESS; - } else + } else { res = -errno; + } } } @@ -206,14 +198,14 @@ int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) { EncFS_Context *ctx = context(); int res = ESUCCESS; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { DirTraverse dt = FSRoot->openDir(path); - rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str()); + VLOG(1) << "getdir on " << FSRoot->cipherPath(path); if (dt.valid()) { int fileType = 0; @@ -228,13 +220,12 @@ int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) { name = dt.nextPlaintextName(&fileType, &inode); } } else { - rInfo("getdir request invalid, path: '%s'", path); + VLOG(1) << "getdir request invalid, path: '" << path << "'"; } return res; - } catch (rlog::Error &err) { - rError("Error caught in getdir"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "Error caught in getdir"; return -EIO; } } @@ -245,14 +236,14 @@ int encfs_mknod(const char *path, mode_t mode, dev_t rdev) { if (isReadOnly(ctx)) return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { - shared_ptr fnode = FSRoot->lookupNode(path, "mknod"); + std::shared_ptr fnode = FSRoot->lookupNode(path, "mknod"); - rLog(Info, "mknod on %s, mode %i, dev %" PRIi64, fnode->cipherName(), mode, - (int64_t)rdev); + VLOG(1) << "mknod on " << fnode->cipherName() << ", mode " << mode + << ", dev " << rdev; uid_t uid = 0; gid_t gid = 0; @@ -266,16 +257,16 @@ int encfs_mknod(const char *path, mode_t mode, dev_t rdev) { if (ctx->publicFilesystem && -res == EACCES) { // try again using the parent dir's group string parent = fnode->plaintextParent(); - rInfo("trying public filesystem workaround for %s", parent.c_str()); - shared_ptr dnode = FSRoot->lookupNode(parent.c_str(), "mknod"); + VLOG(1) << "trying public filesystem workaround for " << parent; + std::shared_ptr dnode = + FSRoot->lookupNode(parent.c_str(), "mknod"); struct stat st; if (dnode->getAttr(&st) == 0) res = fnode->mknod(mode, rdev, uid, st.st_gid); } - } catch (rlog::Error &err) { - rError("error caught in mknod"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in mknod: " << err.what(); } return res; } @@ -287,7 +278,7 @@ int encfs_mkdir(const char *path, mode_t mode) { if (isReadOnly(ctx)) return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { @@ -302,15 +293,15 @@ int encfs_mkdir(const char *path, mode_t mode) { if (ctx->publicFilesystem && -res == EACCES) { // try again using the parent dir's group string parent = parentDirectory(path); - shared_ptr dnode = FSRoot->lookupNode(parent.c_str(), "mkdir"); + std::shared_ptr dnode = + FSRoot->lookupNode(parent.c_str(), "mkdir"); struct stat st; if (dnode->getAttr(&st) == 0) res = FSRoot->mkdir(path, mode, uid, st.st_gid); } - } catch (rlog::Error &err) { - rError("error caught in mkdir"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in mkdir: " << err.what(); } return res; } @@ -321,16 +312,15 @@ int encfs_unlink(const char *path) { if (isReadOnly(ctx)) return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { // let DirNode handle it atomically so that it can handle race // conditions res = FSRoot->unlink(path); - } catch (rlog::Error &err) { - rError("error caught in unlink"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in unlink: " << err.what(); } return res; } @@ -347,7 +337,7 @@ int encfs_rmdir(const char *path) { int _do_readlink(EncFS_Context *ctx, const string &cyName, char *buf, size_t size) { int res = ESUCCESS; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; res = ::readlink(cyName.c_str(), buf, size - 1); @@ -359,6 +349,7 @@ int _do_readlink(EncFS_Context *ctx, const string &cyName, char *buf, try { decodedName = FSRoot->plainPath(buf); } catch (...) { + VLOG(1) << "caught error decoding path"; } if (!decodedName.empty()) { @@ -367,7 +358,7 @@ int _do_readlink(EncFS_Context *ctx, const string &cyName, char *buf, return ESUCCESS; } else { - rWarning("Error decoding link"); + RLOG(WARNING) << "Error decoding link"; return -1; } } @@ -386,7 +377,7 @@ int encfs_symlink(const char *to, const char *from) { if (isReadOnly(ctx)) return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { @@ -394,7 +385,7 @@ int encfs_symlink(const char *to, const char *from) { // allow fully qualified names in symbolic links. string toCName = FSRoot->relativeCipherPath(to); - rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str()); + VLOG(1) << "symlink " << fromCName << " -> " << toCName; // use setfsuid / setfsgid so that the new link will be owned by the // uid/gid provided by the fuse_context. @@ -413,9 +404,8 @@ int encfs_symlink(const char *to, const char *from) { res = -errno; else res = ESUCCESS; - } catch (rlog::Error &err) { - rError("error caught in symlink"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in symlink: " << err.what(); } return res; } @@ -426,14 +416,13 @@ int encfs_link(const char *from, const char *to) { if (isReadOnly(ctx)) return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { res = FSRoot->link(from, to); - } catch (rlog::Error &err) { - rError("error caught in link"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in link: " << err.what(); } return res; } @@ -444,14 +433,13 @@ int encfs_rename(const char *from, const char *to) { if (isReadOnly(ctx)) return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { res = FSRoot->rename(from, to); - } catch (rlog::Error &err) { - rError("error caught in rename"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in rename: " << err.what(); } return res; } @@ -521,25 +509,24 @@ int encfs_open(const char *path, struct fuse_file_info *file) { return -EROFS; int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); + std::shared_ptr FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { - shared_ptr fnode = + std::shared_ptr fnode = FSRoot->openNode(path, "open", file->flags, &res); if (fnode) { - rLog(Info, "encfs_open for %s, flags %i", fnode->cipherName(), - file->flags); + VLOG(1) << "encfs_open for " << fnode->cipherName() << ", flags " + << file->flags; if (res >= 0) { file->fh = (uintptr_t)ctx->putNode(path, fnode); res = ESUCCESS; } } - } catch (rlog::Error &err) { - rError("error caught in open"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in open: " << err.what(); } return res; @@ -574,11 +561,10 @@ int encfs_release(const char *path, struct fuse_file_info *finfo) { EncFS_Context *ctx = context(); try { - ctx->eraseNode(path, (void *)(uintptr_t) finfo->fh); + ctx->eraseNode(path, (void *)(uintptr_t)finfo->fh); return ESUCCESS; - } catch (rlog::Error &err) { - rError("error caught in release"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in release: " << err.what(); return -EIO; } } @@ -626,16 +612,15 @@ int encfs_statfs(const char *path, struct statvfs *st) { rAssert(st != NULL); string cyName = ctx->rootCipherDir; - rLog(Info, "doing statfs of %s", cyName.c_str()); + VLOG(1) << "doing statfs of " << cyName; res = statvfs(cyName.c_str(), st); if (!res) { // adjust maximum name length.. st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx.. } if (res == -1) res = -errno; - } catch (rlog::Error &err) { - rError("error caught in statfs"); - err.log(_RLWarningChannel); + } catch (encfs::Error &err) { + RLOG(ERROR) << "error caught in statfs: " << err.what(); } return res; } @@ -726,4 +711,6 @@ int encfs_removexattr(const char *path, const char *name) { bind(_do_removexattr, _1, _2, name)); } +} // namespace encfs + #endif // HAVE_XATTR diff --git a/encfs/encfs.h b/encfs/encfs.h index 6539e50..85b09db 100644 --- a/encfs/encfs.h +++ b/encfs/encfs.h @@ -21,13 +21,15 @@ #ifndef _encfs_incl_ #define _encfs_incl_ +#include "internal/easylogging++.h" #include -#include #include #include #include "config.h" +namespace encfs { + #if defined(HAVE_SYS_XATTR_H) | defined(HAVE_ATTR_XATTR_H) #define HAVE_XATTR #endif @@ -39,8 +41,7 @@ static __inline int setfsuid(uid_t uid) { uid_t olduid = geteuid(); if (seteuid(uid) != 0) { - // ignore error. - rDebug("seteuid error: %i", errno); + VLOG(1) << "seteuid error: " << errno; } return olduid; @@ -50,8 +51,7 @@ static __inline int setfsgid(gid_t gid) { gid_t oldgid = getegid(); if (setegid(gid) != 0) { - // ignore error. - rDebug("setfsgid error: %i", errno); + VLOG(1) << "setfsgid error: " << errno; } return oldgid; @@ -105,4 +105,6 @@ int encfs_removexattr(const char *path, const char *name); int encfs_utimens(const char *path, const struct timespec ts[2]); +} // namespace encfs + #endif diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp index ce313e5..22caf8a 100644 --- a/encfs/encfsctl.cpp +++ b/encfs/encfsctl.cpp @@ -17,19 +17,16 @@ #include #include +#include #include -#include -#include -#include +#include #include #include #include +#include #include #include #include -#include -#include -#include #include #define NO_DES @@ -38,6 +35,7 @@ #include "Cipher.h" #include "CipherKey.h" #include "DirNode.h" +#include "Error.h" #include "FSConfig.h" #include "FileNode.h" #include "FileUtils.h" @@ -51,9 +49,11 @@ #define PATH_MAX 4096 #endif -using namespace rlog; using namespace std; using gnu::autosprintf; +using namespace encfs; + +INITIALIZE_EASYLOGGINGPP static int showInfo(int argc, char **argv); static int showVersion(int argc, char **argv); @@ -76,47 +76,45 @@ struct CommandOpts { const char *argStr; const char *usageStr; } commands[] = { - {"info", 1, 1, showInfo, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show information (Default command)")}, - {"showKey", 1, 1, cmd_showKey, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show key")}, - {"passwd", 1, 1, chpasswd, "(root dir)", - // xgroup(usage) - gettext_noop(" -- change password for volume")}, - {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", - // xgroup(usage) - gettext_noop( - " -- change password for volume, taking password" - " from standard input.\n\tNo prompts are issued.")}, - {"autocheckpasswd", 1, 1, ckpasswdAutomaticly, "(root dir)", - // xgroup(usage) - gettext_noop( - " -- check password for volume, taking password" - " from standard input.\n\tNo prompts are issued.")}, - {"ls", 1, 2, cmd_ls, 0, 0}, - {"showcruft", 1, 1, cmd_showcruft, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show undecodable filenames in the volume")}, - {"cat", 2, 3, cmd_cat, "[--extpass=prog] (root dir) path", - // xgroup(usage) - gettext_noop(" -- decodes the file and cats it to standard out")}, - {"decode", 1, 100, cmd_decode, - "[--extpass=prog] (root dir) [encoded-name ...]", - // xgroup(usage) - gettext_noop(" -- decodes name and prints plaintext version")}, - {"encode", 1, 100, cmd_encode, - "[--extpass=prog] (root dir) [plaintext-name ...]", - // xgroup(usage) - gettext_noop(" -- encodes a filename and print result")}, - {"export", 2, 2, cmd_export, "(root dir) path", - // xgroup(usage) - gettext_noop(" -- decrypts a volume and writes results to path")}, - {"--version", 0, 0, showVersion, "", - // xgroup(usage) - gettext_noop(" -- print version number and exit")}, - {0, 0, 0, 0, 0, 0}}; + {"info", 1, 1, showInfo, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show information (Default command)")}, + {"showKey", 1, 1, cmd_showKey, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show key")}, + {"passwd", 1, 1, chpasswd, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume")}, + {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume, taking password" + " from standard input.\n\tNo prompts are issued.")}, + {"autocheckpasswd", 1, 1, ckpasswdAutomaticly, "(root dir)", + // xgroup(usage) + gettext_noop(" -- check password for volume, taking password" + " from standard input.\n\tNo prompts are issued.")}, + {"ls", 1, 2, cmd_ls, 0, 0}, + {"showcruft", 1, 1, cmd_showcruft, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show undecodable filenames in the volume")}, + {"cat", 2, 3, cmd_cat, "[--extpass=prog] (root dir) path", + // xgroup(usage) + gettext_noop(" -- decodes the file and cats it to standard out")}, + {"decode", 1, 100, cmd_decode, + "[--extpass=prog] (root dir) [encoded-name ...]", + // xgroup(usage) + gettext_noop(" -- decodes name and prints plaintext version")}, + {"encode", 1, 100, cmd_encode, + "[--extpass=prog] (root dir) [plaintext-name ...]", + // xgroup(usage) + gettext_noop(" -- encodes a filename and print result")}, + {"export", 2, 2, cmd_export, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decrypts a volume and writes results to path")}, + {"--version", 0, 0, showVersion, "", + // xgroup(usage) + gettext_noop(" -- print version number and exit")}, + {0, 0, 0, 0, 0, 0}}; static void usage(const char *name) { cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n" @@ -167,7 +165,7 @@ static int showInfo(int argc, char **argv) { string rootDir = argv[1]; if (!checkDir(rootDir)) return EXIT_FAILURE; - EncFSConfig* config = new EncFSConfig; + EncFSConfig *config = new EncFSConfig; ConfigType type = readConfig(rootDir, config); // show information stored in config.. @@ -184,38 +182,43 @@ static int showInfo(int argc, char **argv) { return EXIT_FAILURE; case Config_V3: // xgroup(diag) - cout << "\n" << autosprintf(_("Version 3 configuration; " - "created by %s\n"), - config->creator.c_str()); + cout << "\n" + << autosprintf(_("Version 3 configuration; " + "created by %s\n"), + config->creator.c_str()); break; case Config_V4: // xgroup(diag) - cout << "\n" << autosprintf(_("Version 4 configuration; " - "created by %s\n"), - config->creator.c_str()); + cout << "\n" + << autosprintf(_("Version 4 configuration; " + "created by %s\n"), + config->creator.c_str()); break; case Config_V5: // xgroup(diag) - cout << "\n" << autosprintf(_("Version 5 configuration; " - "created by %s (revision %i)\n"), - config->creator.c_str(), config->subVersion); + cout << "\n" + << autosprintf(_("Version 5 configuration; " + "created by %s (revision %i)\n"), + config->creator.c_str(), config->subVersion); break; case Config_V6: // xgroup(diag) - cout << "\n" << autosprintf(_("Version 6 configuration; " - "created by %s (revision %i)\n"), - config->creator.c_str(), config->subVersion); + cout << "\n" + << autosprintf(_("Version 6 configuration; " + "created by %s (revision %i)\n"), + config->creator.c_str(), config->subVersion); break; } showFSInfo(config); + delete config; return EXIT_SUCCESS; } static RootPtr initRootInfo(int &argc, char **&argv) { RootPtr result; - shared_ptr opts(new EncFS_Opts()); + std::shared_ptr opts(new EncFS_Opts()); opts->createIfNotFound = false; opts->checkKey = false; @@ -232,7 +235,7 @@ static RootPtr initRootInfo(int &argc, char **&argv) { opts->passwordProgram.assign(optarg); break; default: - rWarning(_("getopt error: %i"), res); + RLOG(WARNING) << "getopt error: " << res; break; } } @@ -262,7 +265,7 @@ static RootPtr initRootInfo(const char *crootDir) { RootPtr result; if (checkDir(rootDir)) { - shared_ptr opts(new EncFS_Opts()); + std::shared_ptr opts(new EncFS_Opts()); opts->rootDir = rootDir; opts->createIfNotFound = false; opts->checkKey = false; @@ -341,7 +344,7 @@ static int cmd_ls(int argc, char **argv) { if (dt.valid()) { for (string name = dt.nextPlaintextName(); !name.empty(); name = dt.nextPlaintextName()) { - shared_ptr fnode = + std::shared_ptr fnode = rootInfo->root->lookupNode(name.c_str(), "encfsctl-ls"); struct stat stbuf; fnode->getAttr(&stbuf); @@ -364,10 +367,10 @@ static int cmd_ls(int argc, char **argv) { // apply an operation to every block in the file template -int processContents(const shared_ptr &rootInfo, const char *path, - T &op) { +int processContents(const std::shared_ptr &rootInfo, + const char *path, T &op) { int errCode = 0; - shared_ptr node = + std::shared_ptr node = rootInfo->root->openNode(path, "encfsctl", O_RDONLY, &errCode); if (!node) { @@ -421,8 +424,8 @@ static int cmd_cat(int argc, char **argv) { } static int copyLink(const struct stat &stBuf, - const shared_ptr &rootInfo, const string &cpath, - const string &destName) { + const std::shared_ptr &rootInfo, + const string &cpath, const string &destName) { std::vector buf(stBuf.st_size + 1, '\0'); int res = ::readlink(cpath.c_str(), buf.data(), stBuf.st_size); if (res == -1) { @@ -442,9 +445,10 @@ static int copyLink(const struct stat &stBuf, return EXIT_SUCCESS; } -static int copyContents(const shared_ptr &rootInfo, +static int copyContents(const std::shared_ptr &rootInfo, const char *encfsName, const char *targetName) { - shared_ptr node = rootInfo->root->lookupNode(encfsName, "encfsctl"); + std::shared_ptr node = + rootInfo->root->lookupNode(encfsName, "encfsctl"); if (!node) { cerr << "unable to open " << encfsName << "\n"; @@ -484,7 +488,7 @@ static bool endsWith(const string &str, char ch) { return str[str.length() - 1] == ch; } -static int traverseDirs(const shared_ptr &rootInfo, +static int traverseDirs(const std::shared_ptr &rootInfo, string volumeDir, string destDir) { if (!endsWith(volumeDir, '/')) volumeDir.append("/"); if (!endsWith(destDir, '/')) destDir.append("/"); @@ -493,7 +497,7 @@ static int traverseDirs(const shared_ptr &rootInfo, // with the same permissions { struct stat st; - shared_ptr dirNode = + std::shared_ptr dirNode = rootInfo->root->lookupNode(volumeDir.c_str(), "encfsctl"); if (dirNode->getAttr(&st)) return EXIT_FAILURE; @@ -547,7 +551,8 @@ static int cmd_export(int argc, char **argv) { return traverseDirs(rootInfo, "/", destDir); } -int showcruft(const shared_ptr &rootInfo, const char *dirName) { +int showcruft(const std::shared_ptr &rootInfo, + const char *dirName) { int found = 0; DirTraverse dt = rootInfo->root->openDir(dirName); if (dt.valid()) { @@ -607,17 +612,19 @@ static int cmd_showcruft(int argc, char **argv) { // depend upon this broken singular form, so it isn't easy to change. cerr << autosprintf(ngettext("Found %i invalid file.", "Found %i invalid files.", filesFound), - filesFound) << "\n"; + filesFound) + << "\n"; return EXIT_SUCCESS; } -static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, char **argv) { +static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, + char **argv) { (void)argc; string rootDir = argv[1]; if (!checkDir(rootDir)) return EXIT_FAILURE; - EncFSConfig* config = new EncFSConfig; + EncFSConfig *config = new EncFSConfig; ConfigType cfgType = readConfig(rootDir, config); if (cfgType == Config_None) { @@ -626,7 +633,8 @@ static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, c } // instanciate proper cipher - shared_ptr cipher = Cipher::New(config->cipherIface, config->keySize); + std::shared_ptr cipher = + Cipher::New(config->cipherIface, config->keySize); if (!cipher) { cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), config->cipherIface.name().c_str()); @@ -648,8 +656,7 @@ static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, c return EXIT_FAILURE; } - if(checkOnly) - { + if (checkOnly) { cout << _("Password is correct\n"); return EXIT_SUCCESS; } @@ -708,7 +715,8 @@ static int ckpasswdAutomaticly(int argc, char **argv) { } int main(int argc, char **argv) { - RLogInit(argc, argv); + START_EASYLOGGINGPP(argc, argv); + encfs::initLogging(); #if defined(ENABLE_NLS) && defined(LOCALEDIR) setlocale(LC_ALL, ""); @@ -719,15 +727,19 @@ int main(int argc, char **argv) { SSL_load_error_strings(); SSL_library_init(); - StdioNode *slog = new StdioNode(STDERR_FILENO); - slog->subscribeTo(GetGlobalChannel("error")); - slog->subscribeTo(GetGlobalChannel("warning")); - if (argc < 2) { usage(argv[0]); return EXIT_FAILURE; } + // Skip over uninteresting args. + while (argc > 2 && *argv[1] == '-') { + VLOG(1) << "skipping arg " << argv[1]; + argc--; + argv[1] = argv[0]; + argv++; + } + if (argc == 2 && !(*argv[1] == '-' && *(argv[1] + 1) == '-')) { // default command when only 1 argument given -- treat the argument as // a directory.. @@ -747,7 +759,8 @@ int main(int argc, char **argv) { (argc - 2 > commands[offset].maxOptions)) { cerr << autosprintf( _("Incorrect number of arguments for command \"%s\""), - argv[1]) << "\n"; + argv[1]) + << "\n"; } else return (*commands[offset].func)(argc - 1, argv + 1); } diff --git a/encfs/main.cpp b/encfs/main.cpp index 6b4365a..ab69992 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -16,27 +16,24 @@ * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include #include #include +#include #include +#include #include +#include +#include +#include +#include #include "Context.h" +#include "Error.h" #include "FileUtils.h" #include "MemoryPool.h" #include "autosprintf.h" @@ -46,8 +43,6 @@ #include "i18n.h" #include "openssl.h" -class DirNode; - // Fuse version >= 26 requires another argument to fuse_unmount, which we // don't have. So use the backward compatible call instead.. extern "C" void fuse_unmount_compat22(const char *mountpoint); @@ -60,10 +55,15 @@ extern "C" void fuse_unmount_compat22(const char *mountpoint); #define LONG_OPT_REQUIRE_MAC 515 using namespace std; -using namespace rlog; -using namespace rel; +using namespace encfs; using gnu::autosprintf; +INITIALIZE_EASYLOGGINGPP + +namespace encfs { + +class DirNode; + // Maximum number of arguments that we're going to pass on to fuse. Doesn't // affect how many arguments we can handle, just how many we can pass on.. const int MaxFuseArgs = 32; @@ -74,14 +74,14 @@ const int MaxFuseArgs = 32; * derived from the arguments */ struct EncFS_Args { - bool isDaemon; // true == spawn in background, log to syslog - bool isThreaded; // true == threaded - bool isVerbose; // false == only enable warning/error messages - int idleTimeout; // 0 == idle time in minutes to trigger unmount + bool isDaemon; // true == spawn in background, log to syslog + bool isThreaded; // true == threaded + bool isVerbose; // false == only enable warning/error messages + int idleTimeout; // 0 == idle time in minutes to trigger unmount const char *fuseArgv[MaxFuseArgs]; int fuseArgc; - shared_ptr opts; + std::shared_ptr opts; // for debugging // In case someone sends me a log dump, I want to know how what options are @@ -110,6 +110,8 @@ struct EncFS_Args { static int oldStderr = STDERR_FILENO; +} // namespace encfs + static void usage(const char *name) { // xgroup(usage) cerr << autosprintf(_("Build: encfs version %s"), VERSION) << "\n\n" @@ -117,7 +119,8 @@ static void usage(const char *name) { << autosprintf( _("Usage: %s [options] rootDir mountPoint [-- [FUSE Mount " "Options]]"), - name) << "\n\n" + name) + << "\n\n" // xgroup(usage) << _("Common Options:\n" " -H\t\t\t" @@ -141,8 +144,9 @@ static void usage(const char *name) { "\t\t\t(for filesystems using MAC block headers)\n") << _(" --public\t\t" "act as a typical multi-user filesystem\n" - "\t\t\t(encfs must be run as root)\n") << _(" --reverse\t\t" - "reverse encryption\n") + "\t\t\t(encfs must be run as root)\n") + << _(" --reverse\t\t" + "reverse encryption\n") // xgroup(usage) << _(" --extpass=program\tUse external program for password prompt\n" @@ -151,13 +155,15 @@ static void usage(const char *name) { " encfs ~/.crypt ~/crypt\n" "\n") // xgroup(usage) - << _("For more information, see the man page encfs(1)") << "\n" << endl; + << _("For more information, see the man page encfs(1)") << "\n" + << endl; } static void FuseUsage() { // xgroup(usage) cerr << _("encfs [options] rootDir mountPoint -- [FUSE Mount Options]\n" - "valid FUSE Mount Options follow:\n") << endl; + "valid FUSE Mount Options follow:\n") + << endl; int argc = 2; const char *argv[] = {"...", "-h"}; @@ -177,7 +183,7 @@ static string slashTerminate(const string &src) { } static bool processArgs(int argc, char *argv[], - const shared_ptr &out) { + const std::shared_ptr &out) { // set defaults out->isDaemon = true; out->isThreaded = true; @@ -337,7 +343,7 @@ static bool processArgs(int argc, char *argv[], break; case 'P': if (geteuid() != 0) - rWarning(_("option '--public' ignored for non-root user")); + RLOG(WARNING) << "option '--public' ignored for non-root user"; else { out->opts->ownerCreate = true; // add 'allow_other' option @@ -362,7 +368,7 @@ static bool processArgs(int argc, char *argv[], // missing parameter for option.. break; default: - rWarning(_("getopt error: %i"), res); + RLOG(WARNING) << "getopt error: " << res; break; } } @@ -378,7 +384,7 @@ static bool processArgs(int argc, char *argv[], out->opts->mountPoint = slashTerminate(argv[optind++]); } else { // no mount point specified - rWarning(_("Missing one or more arguments, aborting.")); + cerr << _("Missing one or more arguments, aborting."); return false; } @@ -426,7 +432,8 @@ static bool processArgs(int argc, char *argv[], cerr << // xgroup(usage) _("When specifying daemon mode, you must use absolute paths " - "(beginning with '/')") << endl; + "(beginning with '/')") + << endl; return false; } @@ -439,7 +446,8 @@ static bool processArgs(int argc, char *argv[], cerr << // xgroup(usage) _("The raw directory may not be a subdirectory of the " - "mount point.") << endl; + "mount point.") + << endl; return false; } } @@ -462,13 +470,13 @@ static bool processArgs(int argc, char *argv[], if (!isDirectory(out->opts->rootDir.c_str()) && !userAllowMkdir(out->opts->annotate ? 1 : 0, out->opts->rootDir.c_str(), 0700)) { - rWarning(_("Unable to locate root directory, aborting.")); + cerr << _("Unable to locate root directory, aborting."); return false; } if (!isDirectory(out->opts->mountPoint.c_str()) && - !userAllowMkdir(out->opts->annotate ? 2 : 0, out->opts->mountPoint.c_str(), - 0700)) { - rWarning(_("Unable to locate mount point, aborting.")); + !userAllowMkdir(out->opts->annotate ? 2 : 0, + out->opts->mountPoint.c_str(), 0700)) { + cerr << _("Unable to locate mount point, aborting."); return false; } @@ -486,23 +494,27 @@ void *encfs_init(fuse_conn_info *conn) { // set fuse connection options conn->async_read = true; + if (ctx->args->isDaemon) { + // Switch to using syslog. + encfs::rlogAction = el::base::DispatchAction::SysLog; + } + // if an idle timeout is specified, then setup a thread to monitor the // filesystem. if (ctx->args->idleTimeout > 0) { - rDebug("starting idle monitoring thread"); + VLOG(1) << "starting idle monitoring thread"; ctx->running = true; int res = pthread_create(&ctx->monitorThread, 0, idleMonitor, (void *)ctx); if (res != 0) { - rError( - "error starting idle monitor thread, " - "res = %i, errno = %i", - res, errno); + RLOG(ERROR) << "error starting idle monitor thread, " + "res = " + << res << ", errno = " << errno; } } if (ctx->args->isDaemon && oldStderr >= 0) { - rInfo("Closing stderr"); + VLOG(1) << "Closing stderr"; close(oldStderr); oldStderr = -1; } @@ -516,19 +528,18 @@ void encfs_destroy(void *_ctx) { ctx->running = false; // wake up the thread if it is waiting.. - rDebug("waking up monitoring thread"); + VLOG(1) << "waking up monitoring thread"; pthread_mutex_lock(&ctx->wakeupMutex); pthread_cond_signal(&ctx->wakeupCond); pthread_mutex_unlock(&ctx->wakeupMutex); - rDebug("joining with idle monitoring thread"); + VLOG(1) << "joining with idle monitoring thread"; pthread_join(ctx->monitorThread, 0); - rDebug("join done"); + VLOG(1) << "join done"; } } int main(int argc, char *argv[]) { - // initialize the logging library - RLogInit(argc, argv); + encfs::initLogging(); #if defined(ENABLE_NLS) && defined(LOCALEDIR) setlocale(LC_ALL, ""); @@ -536,17 +547,9 @@ int main(int argc, char *argv[]) { textdomain(PACKAGE); #endif - // log to stderr by default.. - std::unique_ptr slog(new StdioNode(STDERR_FILENO)); - std::unique_ptr logNode; - - // show error and warning output - slog->subscribeTo(GetGlobalChannel("error")); - slog->subscribeTo(GetGlobalChannel("warning")); - // anything that comes from the user should be considered tainted until // we've processed it and only allowed through what we support. - shared_ptr encfsArgs(new EncFS_Args); + std::shared_ptr encfsArgs(new EncFS_Args); for (int i = 0; i < MaxFuseArgs; ++i) encfsArgs->fuseArgv[i] = NULL; // libfuse expects null args.. @@ -556,13 +559,11 @@ int main(int argc, char *argv[]) { } if (encfsArgs->isVerbose) { - // subscribe to more logging channels.. - slog->subscribeTo(GetGlobalChannel("info")); - slog->subscribeTo(GetGlobalChannel("debug")); + el::Loggers::setVerboseLevel(1); } - rDebug("Root directory: %s", encfsArgs->opts->rootDir.c_str()); - rDebug("Fuse arguments: %s", encfsArgs->toString().c_str()); + VLOG(1) << "Root directory: " << encfsArgs->opts->rootDir; + VLOG(1) << "Fuse arguments: " << encfsArgs->toString(); fuse_operations encfs_oper; // in case this code is compiled against a newer FUSE library and new @@ -609,15 +610,15 @@ int main(int argc, char *argv[]) { encfs_oper.fgetattr = encfs_fgetattr; // encfs_oper.lock = encfs_lock; encfs_oper.utimens = encfs_utimens; -// encfs_oper.bmap = encfs_bmap; + // encfs_oper.bmap = encfs_bmap; openssl_init(encfsArgs->isThreaded); // context is not a smart pointer because it will live for the life of // the filesystem. - EncFS_Context *ctx = new EncFS_Context; + auto ctx = std::shared_ptr(new EncFS_Context); ctx->publicFilesystem = encfsArgs->opts->ownerCreate; - RootPtr rootInfo = initFS(ctx, encfsArgs->opts); + RootPtr rootInfo = initFS(ctx.get(), encfsArgs->opts); int returnCode = EXIT_FAILURE; @@ -637,7 +638,8 @@ int main(int argc, char *argv[]) { cerr << _("Note: requested single-threaded mode, but an idle\n" "timeout was specified. The filesystem will operate\n" "single-threaded, but threads will still be used to\n" - "implement idle checking.") << endl; + "implement idle checking.") + << endl; } // reset umask now, since we don't want it to interfere with the @@ -645,14 +647,6 @@ int main(int argc, char *argv[]) { umask(0); if (encfsArgs->isDaemon) { - // switch to logging just warning and error messages via syslog - logNode.reset(new SyslogNode("encfs")); - logNode->subscribeTo(GetGlobalChannel("warning")); - logNode->subscribeTo(GetGlobalChannel("error")); - - // disable stderr reporting.. - slog.reset(); - // keep around a pointer just in case we end up needing it to // report a fatal condition later (fuse_main exits unexpectedly)... oldStderr = dup(STDERR_FILENO); @@ -671,7 +665,7 @@ int main(int argc, char *argv[]) { // fuse_main returns an error code in newer versions of fuse.. int res = fuse_main(encfsArgs->fuseArgc, const_cast(encfsArgs->fuseArgv), &encfs_oper, - (void *)ctx); + (void *)ctx.get()); time(&endTime); @@ -692,16 +686,16 @@ int main(int argc, char *argv[]) { fclose(out); } } catch (std::exception &ex) { - rError(_("Internal error: Caught exception from main loop: %s"), - ex.what()); + RLOG(ERROR) << "Internal error: Caught exception from main loop: " + << ex.what(); } catch (...) { - rError(_("Internal error: Caught unexpected exception")); + RLOG(ERROR) << "Internal error: Caught unexpected exception"; } } // cleanup so that we can check for leaked resources.. rootInfo.reset(); - ctx->setRoot(shared_ptr()); + ctx->setRoot(std::shared_ptr()); MemoryPool::destroyAll(); openssl_shutdown(encfsArgs->isThreaded); @@ -721,7 +715,7 @@ static bool unmountFS(EncFS_Context *ctx); static void *idleMonitor(void *_arg) { EncFS_Context *ctx = (EncFS_Context *)_arg; - shared_ptr arg = ctx->args; + std::shared_ptr arg = ctx->args; const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; int idleCycles = 0; @@ -744,10 +738,11 @@ static void *idleMonitor(void *_arg) { break; } - rDebug("num open files: %i", openCount); + VLOG(1) << "num open files: " << openCount; } - rDebug("idle cycle count: %i, timeout after %i", idleCycles, timeoutCycles); + VLOG(1) << "idle cycle count: " << idleCycles << ", timeout after " + << timeoutCycles; struct timeval currentTime; gettimeofday(¤tTime, 0); @@ -759,24 +754,23 @@ static void *idleMonitor(void *_arg) { pthread_mutex_unlock(&ctx->wakeupMutex); - rDebug("Idle monitoring thread exiting"); + VLOG(1) << "Idle monitoring thread exiting"; return 0; } static bool unmountFS(EncFS_Context *ctx) { - shared_ptr arg = ctx->args; + std::shared_ptr arg = ctx->args; if (arg->opts->mountOnDemand) { - rDebug("Detaching filesystem %s due to inactivity", - arg->opts->mountPoint.c_str()); + VLOG(1) << "Detaching filesystem due to inactivity: " + << arg->opts->mountPoint; - ctx->setRoot(shared_ptr()); + ctx->setRoot(std::shared_ptr()); return false; } else { // Time to unmount! - // xgroup(diag) - rWarning(_("Unmounting filesystem %s due to inactivity"), - arg->opts->mountPoint.c_str()); + RLOG(WARNING) << "Unmounting filesystem due to inactivity: " + << arg->opts->mountPoint; fuse_unmount(arg->opts->mountPoint.c_str()); return true; } diff --git a/encfs/makeKey.cpp b/encfs/makeKey.cpp index 35471cc..d0a14d2 100644 --- a/encfs/makeKey.cpp +++ b/encfs/makeKey.cpp @@ -18,19 +18,22 @@ * this program. If not, see . */ -#include -#include #include #include +#include #include +#include #include "Cipher.h" #include "CipherKey.h" #include "openssl.h" using namespace std; +using namespace encfs; -void genKey(const shared_ptr &cipher) { +INITIALIZE_EASYLOGGINGPP + +void genKey(const std::shared_ptr &cipher) { CipherKey key = cipher->newRandomKey(); // encode with itself @@ -54,7 +57,7 @@ int main(int argc, char **argv) { openssl_init(false); // get a list of the available algorithms - shared_ptr cipher = Cipher::New(type, size); + std::shared_ptr cipher = Cipher::New(type, size); genKey(cipher); // openssl_shutdown(false); diff --git a/encfs/openssl.cpp b/encfs/openssl.cpp index 6128294..801a105 100644 --- a/encfs/openssl.cpp +++ b/encfs/openssl.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #define NO_DES @@ -32,6 +31,10 @@ #include #endif +#include "Error.h" + +namespace encfs { + unsigned long pthreads_thread_id() { return (unsigned long)pthread_self(); } static pthread_mutex_t *crypto_locks = NULL; @@ -41,7 +44,7 @@ void pthreads_locking_callback(int mode, int n, const char *caller_file, (void)caller_line; if (!crypto_locks) { - rDebug("Allocating %i locks for OpenSSL", CRYPTO_num_locks()); + VLOG(1) << "Allocating " << CRYPTO_num_locks() << " locks for OpenSSL"; crypto_locks = new pthread_mutex_t[CRYPTO_num_locks()]; for (int i = 0; i < CRYPTO_num_locks(); ++i) pthread_mutex_init(crypto_locks + i, 0); @@ -94,3 +97,5 @@ void openssl_shutdown(bool threaded) { if (threaded) pthreads_locking_cleanup(); } + +} // namespace encfs diff --git a/encfs/openssl.h b/encfs/openssl.h index 28ee1a8..47eb01f 100644 --- a/encfs/openssl.h +++ b/encfs/openssl.h @@ -21,7 +21,11 @@ #ifndef _openssl_incl_ #define _openssl_incl_ +namespace encfs { + void openssl_init(bool isThreaded); void openssl_shutdown(bool isThreaded); +} // namespace encfs + #endif diff --git a/encfs/readpassphrase.cpp b/encfs/readpassphrase.cpp index a400b01..daf4a56 100644 --- a/encfs/readpassphrase.cpp +++ b/encfs/readpassphrase.cpp @@ -37,16 +37,16 @@ static const char rcsid[] = #ifndef HAVE_READPASSPHRASE -#include -#include -#include -#include -#include #include #include #include #include #include +#include +#include +#include +#include +#include #include "readpassphrase.h" diff --git a/encfs/shared_ptr.h b/encfs/shared_ptr.h deleted file mode 100644 index 068a70f..0000000 --- a/encfs/shared_ptr.h +++ /dev/null @@ -1,29 +0,0 @@ - -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2012 Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifndef _SHARED_PTR_incl_ -#define _SHARED_PTR_incl_ - -#include -using std::shared_ptr; -using std::dynamic_pointer_cast; - -#endif diff --git a/encfs/test.cpp b/encfs/test.cpp index 411706d..56a7ab1 100644 --- a/encfs/test.cpp +++ b/encfs/test.cpp @@ -16,8 +16,6 @@ * */ -#include -#include #include #include #include @@ -25,16 +23,14 @@ #include #include #include - -#include -#include -#include -#include +#include +#include #include "BlockNameIO.h" #include "Cipher.h" #include "CipherKey.h" #include "DirNode.h" +#include "Error.h" #include "FSConfig.h" #include "FileUtils.h" #include "Interface.h" @@ -42,6 +38,7 @@ #include "NameIO.h" #include "Range.h" #include "StreamNameIO.h" +#include "internal/easylogging++.h" #define NO_DES #include @@ -50,13 +47,15 @@ #endif using namespace std; -using namespace rel; -using namespace rlog; +using namespace encfs; + +INITIALIZE_EASYLOGGINGPP const int FSBlockSize = 256; -static int checkErrorPropogation(const shared_ptr &cipher, int size, - int byteToChange, const CipherKey &key) { +static int checkErrorPropogation(const std::shared_ptr &cipher, + int size, int byteToChange, + const CipherKey &key) { MemBlock orig = MemoryPool::allocate(size); MemBlock data = MemoryPool::allocate(size); @@ -132,7 +131,7 @@ static bool testNameCoding(DirNode &dirNode, bool verbose) { return true; } -bool runTests(const shared_ptr &cipher, bool verbose) { +bool runTests(const std::shared_ptr &cipher, bool verbose) { // create a random key if (verbose) { cerr << "Generating new key, output will be different on each run\n\n"; @@ -143,7 +142,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) { { CipherKey encodingKey = cipher->newRandomKey(); int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char[encodedKeySize]; + unsigned char keyBuf[encodedKeySize]; cipher->writeKey(key, keyBuf, encodingKey); CipherKey key2 = cipher->readKey(keyBuf, encodingKey); @@ -164,7 +163,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) { { CipherKey encodingKey = cipher->newRandomKey(); int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char[encodedKeySize]; + unsigned char keyBuf[encodedKeySize]; cipher->writeKey(key, keyBuf, encodingKey); @@ -266,7 +265,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) { if (!verbose) { { // test stream mode, this time without IV chaining - fsCfg->nameCoding = shared_ptr( + fsCfg->nameCoding = std::shared_ptr( new StreamNameIO(StreamNameIO::CurrentInterface(), cipher, key)); fsCfg->nameCoding->setChainedNameIV(false); @@ -277,7 +276,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) { { // test block mode, this time without IV chaining - fsCfg->nameCoding = shared_ptr( + fsCfg->nameCoding = std::shared_ptr( new BlockNameIO(BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize())); fsCfg->nameCoding->setChainedNameIV(false); @@ -331,7 +330,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) { if (verbose) { cerr << "modification of 1 byte affected between " << minChanges << " and " << maxChanges << " decoded bytes\n"; - cerr << "minimum change at byte " << minAt << " and maximum at byte " + cerr << "minimum change at byte " << minAt << " and maximum at byte " << maxAt << "\n"; } } @@ -357,7 +356,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) { if (verbose) { cerr << "modification of 1 byte affected between " << minChanges << " and " << maxChanges << " decoded bytes\n"; - cerr << "minimum change at byte " << minAt << " and maximum at byte " + cerr << "minimum change at byte " << minAt << " and maximum at byte " << maxAt << "\n"; } } @@ -370,7 +369,7 @@ static bool testCipherSize(const string &name, int keySize, int blockSize, cerr << name << ", key length " << keySize << ", block size " << blockSize << ": "; - shared_ptr cipher = Cipher::New(name, keySize); + std::shared_ptr cipher = Cipher::New(name, keySize); if (!cipher) { cerr << "FAILED TO CREATE\n"; return false; @@ -382,7 +381,7 @@ static bool testCipherSize(const string &name, int keySize, int blockSize, cerr << "FAILED\n"; return false; } - } catch (rlog::Error &er) { + } catch (encfs::Error &er) { cerr << "Error: " << er.what() << "\n"; return false; } @@ -391,14 +390,8 @@ static bool testCipherSize(const string &name, int keySize, int blockSize, } int main(int argc, char *argv[]) { - RLogInit(argc, argv); - - StdioNode stdLog(STDERR_FILENO); - stdLog.subscribeTo(RLOG_CHANNEL("error")); - stdLog.subscribeTo(RLOG_CHANNEL("warning")); -#ifndef NO_DEBUG - stdLog.subscribeTo(RLOG_CHANNEL("debug")); -#endif + START_EASYLOGGINGPP(argc, argv); + encfs::initLogging(); SSL_load_error_strings(); SSL_library_init(); @@ -442,7 +435,7 @@ int main(int argc, char *argv[]) { } // run one test with verbose output too.. - shared_ptr cipher = Cipher::New("AES", 192); + std::shared_ptr cipher = Cipher::New("AES", 192); if (!cipher) { cerr << "\nNo AES cipher found, skipping verbose test.\n"; } else { diff --git a/internal/easylogging++.h b/internal/easylogging++.h new file mode 100755 index 0000000..f9a67fe --- /dev/null +++ b/internal/easylogging++.h @@ -0,0 +1,6663 @@ +// +// Easylogging++ v9.80 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2015 muflihun.com +// +// This library is released under the MIT Licence. +// http://easylogging.muflihun.com/licence.php +// +// easylogging@muflihun.com +// +// https://github.com/easylogging/easyloggingpp +// http://easylogging.muflihun.com +// http://muflihun.com +// +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H +// Compilers and C++0x/C++11 Evaluation +#if defined(__GNUC__) +# define ELPP_COMPILER_GCC 1 +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# elif(ELPP_GCC_VERSION >= 40801) +# define ELPP_CXX11 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +#endif // defined(__GNUC__) +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +# define ELPP_CRT_DBG_WARNINGS 1 +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif // (_MSC_VER == 1600) +#endif // defined(_MSC_VER) +// Clang++ +#if defined(__clang__) && (__clang__ == 1) +# define ELPP_COMPILER_CLANG 1 +# define ELPP_CLANG_VERSION (__clang_major__ * 10000 \ + + __clang_minor__ * 100 \ + + __clang_patchlevel__) +# if (ELPP_CLANG_VERSION >= 30300) +# define ELPP_CXX11 1 +# endif // (ELPP_CLANG_VERSION >= 30300) +#endif // defined(__clang__) && (__clang__ == 1) +// MinGW +#if defined(__MINGW32__) || defined(__MINGW64__) +# define ELPP_MINGW 1 +#endif // defined(__MINGW32__) || defined(__MINGW64__) +// Cygwin +#if defined(__CYGWIN__) && (__CYGWIN__ == 1) +# define ELPP_CYGWIN 1 +#endif // defined(__CYGWIN__) && (__CYGWIN__ == 1) +// Intel C++ +#if defined(__INTEL_COMPILER) +# define ELPP_COMPILER_INTEL 1 +#endif +// Operating System Evaluation +// Windows +#if defined(_WIN32) || defined(_WIN64) +# define ELPP_OS_WINDOWS 1 +#endif // defined(_WIN32) || defined(_WIN64) +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#endif // (defined(__linux) || defined(__linux__)) +// Mac +#if defined(__APPLE__) +# define ELPP_OS_MAC 1 +#endif // defined(__APPLE__) +// FreeBSD +#if defined(__FreeBSD__) +# define ELPP_OS_FREEBSD 1 +#endif +// Solaris +#if defined(__sun) +# define ELPP_OS_SOLARIS 1 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#endif // ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +// Android +#if defined(__ANDROID__) +# define ELPP_OS_ANDROID 1 +#endif // defined(__ANDROID__) +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ + std::stringstream internalInfoStream; internalInfoStream << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ + << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ + << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ + "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ + std::stringstream internalInfoStream; internalInfoStream << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ + << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ + << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ + << ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ + { char buff[256]; strerror_s(buff, 256, errno); \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ + std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ + << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ + << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ + if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ + std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ + << ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if defined(ELPP_STACKTRACE_ON_CRASH) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# endif // ELPP_COMPILER_GCC +#endif // (defined(ELPP_STACKTRACE_ON_CRASH)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD) +# define ELPP_USE_STD_THREADING 1 +#endif // (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD) +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#if ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800) +# define ELPP_VARIADIC_TEMPLATES_SUPPORTED 1 +#endif // ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800) +// Logging Enable/Disable macros +#if (!defined(ELPP_DISABLE_LOGS)) +# define ELPP_LOGGING_ENABLED 1 +#endif // (!defined(ELPP_DISABLE_LOGS)) +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#if ELPP_STACKTRACE +# include +# include +#endif // ELPP_STACKTRACE +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) +# include +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned short EnumType; +typedef std::shared_ptr StoragePointer; +typedef int VerboseLevel; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { +protected: + NoCopy(void) {} +private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { +private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situtaions + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { +public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; + } + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr) { + if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0)) + return Level::Global; + if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0)) + return Level::Debug; + if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0)) + return Level::Info; + if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0)) + return Level::Warning; + if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0)) + return Level::Error; + if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0)) + return Level::Fatal; + if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0)) + return Level::Verbose; + if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0)) + return Level::Trace; + return Level::Unknown; + } + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static inline void forEachLevel(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= lIndexMax); + } +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for correponding level and logger + Filename = 16, + /// @brief Specifies milliseconds width. Width can be within range (1-6) + MillisecondsWidth = 32, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { +public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; + } + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr) { + if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0)) + return ConfigurationType::Enabled; + if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0)) + return ConfigurationType::ToFile; + if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0)) + return ConfigurationType::ToStandardOutput; + if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0)) + return ConfigurationType::Format; + if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0)) + return ConfigurationType::Filename; + if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0)) + return ConfigurationType::MillisecondsWidth; + if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0)) + return ConfigurationType::PerformanceTracking; + if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0)) + return ConfigurationType::MaxLogFileSize; + if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0)) + return ConfigurationType::LogFlushThreshold; + return ConfigurationType::Unknown; + } + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= cIndexMax); + } +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384 +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { + // Level log values - These are values that are replaced in place of %level format specifier + static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO "); + static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); + static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN "); + static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); + static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); + static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER"); + static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); + static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); + static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); + static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); + static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); + static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); + static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); + static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); + // Format specifiers - These are used to define log format + static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); + static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); + static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); + static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); + static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); + static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); + static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); + static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); + static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); + static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); + static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); + static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); + static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); + static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); + static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); + static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; + // Date/time + static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", + "September", "October", "November", "December" }; + static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; + static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; + static const int kYearBase = 1900; + static const char* kAm = "AM"; + static const char* kPm = "PM"; + // Miscellaneous constants + static const char* kDefaultLoggerId = "default"; + static const char* kPerformanceLoggerId = "performance"; + static const char* kSysLogLoggerId = "syslog"; + static const char* kNullPointer = "nullptr"; + static const char kFormatSpecifierChar = '%'; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + static const char kFormatSpecifierCharValue = 'v'; +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + static const unsigned int kMaxLogPerContainer = 100; + static const unsigned int kMaxLogPerCounter = 100000; + static const unsigned int kDefaultMillisecondsWidth = 3; + static const base::type::VerboseLevel kMaxVerboseLevel = 9; + static const char* kUnknownUser = "user"; + static const char* kUnknownHost = "unknown-host"; +#if defined(ELPP_DEFAULT_LOG_FILE) + static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +# if ELPP_OS_UNIX +# if ELPP_OS_ANDROID + static const char* kDefaultLogFile = "logs/myeasylog.log"; +# else + static const char* kDefaultLogFile = "logs/myeasylog.log"; +# endif // ELPP_OS_ANDROID +# elif ELPP_OS_WINDOWS + static const char* kDefaultLogFile = "logs\\myeasylog.log"; +# endif // ELPP_OS_UNIX +#endif // defined(ELPP_DEFAULT_LOG_FILE) +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +#if ELPP_OS_WINDOWS + static const char* kFilePathSeperator = "\\"; +#else + static const char* kFilePathSeperator = "/"; +#endif // ELPP_OS_WINDOWS + static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; + static const char* kConfigurationComment = "##"; + static const char* kConfigurationLevel = "*"; + static const char* kConfigurationLoggerId = "--"; + static const std::size_t kSourceFilenameMaxLength = 100; + static const std::size_t kSourceLineMaxLength = 10; + static const Level kPerformanceTrackerDefaultLevel = Level::Info; + const struct { + double value; + const base::type::char_t* unit; + } kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("mis") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } + }; + static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); + const struct { + int numb; + const char* name; + const char* brief; + const char* detail; + } kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." }, + { SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithemetic operation issue such as division by zero or operation resulting in overflow." }, + { SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data."}, + { SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." }, + { SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." }, + }; + static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6, + User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12, + Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15 +}; +/// @brief A milliseconds width class containing actual width and offset for date/time +class MillisecondsWidth { +public: + MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); } + explicit MillisecondsWidth(int width) { init(width); } + bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; } + int m_width; unsigned int m_offset; +private: + void init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultMillisecondsWidth; + } + m_width = width; + switch (m_width) { + case 3: m_offset = 1000; break; + case 4: m_offset = 100; break; + case 5: m_offset = 10; break; + case 6: m_offset = 1; break; + default: m_offset = 1000; break; + } + } +}; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static inline +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned +static inline const char* charPtrVal(const char* pointer) { + return pointer == nullptr ? base::consts::kNullPointer : pointer; +} +/// @brief Aborts application due with user-defined status +static inline void abort(int status, const std::string& reason = std::string()) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as
flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::mutex +class Mutex : base::NoCopy { +public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_init(&m_underlyingMutex, nullptr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + +private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { +public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } +private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static inline std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +static inline void msleep(int) { + // No implementation for non std::thread version +} +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static inline std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +static inline void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +#if ELPP_ASYNC_LOGGING + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +#endif // ELPP_ASYNC_LOGGING +} +typedef std::mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { +public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { return true; } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { +public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } +private: + NoScopedLock(void); +}; +} // namespace internal +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +static inline void msleep(int) { + // No custom implementation +} +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { +public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } +protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} +private: + base::threading::Mutex m_mutex; +}; +} // namespace threading +namespace utils { +class File : base::StaticClass { +public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out | base::type::fstream_t::app); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +#if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); + elppUnicodeLocale = elppUnicodeLocaleWindows; +#endif + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; + } + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + std::streampos currPos = fs->tellg(); + fs->seekg(0, fs->end); + std::size_t size = static_cast(fs->tellg()); + fs->seekg(currPos); + return size; + } + + /// @brief Determines whether or not provided path exist in current file system + static inline bool pathExists(const char* path, bool considerFile = false) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX + } + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeperator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; + } + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* seperator = base::consts::kFilePathSeperator) { + if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(seperator); + if (lastSlashAt == 0) { + return std::string(seperator); + } + return fullPath.substr(0, lastSlashAt + 1); + } + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); + } + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* seperator = base::consts::kFilePathSeperator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(seperator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); + } +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { +public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + break; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; + } + + /// @brief Trims string from start + /// @param [in,out] str String to trim + static inline std::string& ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun(&std::isspace)))); + return str; + } + + /// @brief Trim string from end + /// @param [in,out] str String to trim + static inline std::string& rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun(&std::isspace))).base(), str.end()); + return str; + } + + /// @brief Trims string from left and right + /// @param [in,out] str String to trim + static inline std::string& trim(std::string& str) { + return ltrim(rtrim(str)); + } + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static inline bool startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); + } + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static inline bool endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); + } + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; + } + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; + } + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } + } +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); + } +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static inline std::string& toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + return str; + } + + /// @brief Compares cstring equality - uses strcmp + static inline bool cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; + } + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + if (strlen(s1) != strlen(s2)) return false; + while (*s1 != '\0' && *s2 != '\0') { + if (::toupper(*s1) != ::toupper(*s2)) return false; + ++s1; + ++s2; + } + return true; + } + + /// @brief Returns true if c exist in str + static inline bool contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; + } + + static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast('0'); + return addToBuff(p, buf, bufLim); + } + + static inline char* addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; + } + + static inline char* clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; + } + + /// @brief Converst wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; + } +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { +public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; + } +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static inline std::string getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); + } + + /// @brief Reads android device name + static std::string getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); + } +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + if (hBuff[strlen(hBuff) - 1] == '\n') { + hBuff[strlen(hBuff) - 1] = '\0'; + } + return std::string(hBuff); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + } + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); + } + /// @brief Gets current username. + static inline std::string currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID + } + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen + static inline std::string currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID + } + /// @brief Whether or not terminal supports colors + static inline bool termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" || + term == "screen" || term == "linux" || term == "cygwin"; + } +}; +extern std::string s_currentUser; +extern std::string s_currentHost; +extern bool s_termSupportsColor; +#define ELPP_INITI_BASIC_DECLR \ + namespace el {\ + namespace base {\ + namespace utils {\ + std::string s_currentUser = el::base::utils::OS::currentUser(); \ + std::string s_currentHost = el::base::utils::OS::currentHost(); \ + bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \ + }\ + }\ + } +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { +public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast(present * secOffSet); + tv->tv_usec = static_cast(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS + } + + /// @brief Gets current date and time with milliseconds. + /// @param format User provided date/time format + /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null) + /// @returns string based date time in specified format. + static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) { + struct timeval currTime; + gettimeofday(&currTime); + struct ::tm timeInfo; + buildTimeInfo(&currTime, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(currTime.tv_usec / msWidth->m_offset), msWidth); + return std::string(buff_); + } + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + double result = static_cast(time); + base::type::EnumType start = static_cast(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (result <= base::consts::kTimeFormats[i].value) { + break; + } + result /= base::consts::kTimeFormats[i].value; + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << result << " " << unit; + return ss.str(); + } + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); + } else { + return static_cast((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000); + } + } + +private: + static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::localtime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; + _time64(&t); + localtime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = localtime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX + } + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::MillisecondsWidth* msWidth) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // milliseconds + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; + } +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { +public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + inline void setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } + } + /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') + inline bool hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); + } + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + inline const char* getParamValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); + } + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + inline bool hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); + } + /// @brief Returns true if no params available. This exclude argv[0] + inline bool empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); + } + /// @brief Returns total number of arguments. This exclude argv[0] + inline std::size_t size(void) const { + return m_params.size() + m_paramsWithValue.size(); + } + inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; + } + +private: + int m_argc; + char** m_argv; + std::map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { +public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + +protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + +private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { +public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + +protected: + virtual inline void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + + /// @brief Registers new registry to repository. + virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + + /// @brief Unregisters single entry mapped to specified unique key + inline void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + base::utils::safeDelete(existing); + this->list().erase(uniqKey); + } + } + + /// @brief Gets pointer from repository. If none found, nullptr is returned. + inline T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + +private: + virtual inline void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { +public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + +protected: + virtual inline void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + + /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate + /// in order to validate pointer. + template + inline T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + +private: + virtual inline void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; + +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { +public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; +private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { +public: + LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0) { + } + + LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format) { + parseFromFormat(m_userFormat); + } + + LogFormat(const LogFormat& logFormat) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_format = logFormat.m_format; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + } + + LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + } + + LogFormat& operator=(const LogFormat& logFormat) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + return *this; + } + + virtual ~LogFormat(void) { + } + + inline bool operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; + } + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){ + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); + } + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + +protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } + } + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + std::string s = base::utils::s_currentUser; + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + base::utils::s_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + base::utils::s_currentHost); + } + // Ignore Level::Global and Level::Unknown + } + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + +private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { +public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { return m_formatSpecifier; } + inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + +private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { +public: + Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { + } + + Configuration& operator=(const Configuration& c) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + return *this; + } + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { + } + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); + } + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { + } + + inline bool operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); + } + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + +private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { +public: + /// @brief Default constructor with empty repository + Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { + } + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } + } + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) { + // We initial assertion with true because if we have assertion diabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)), + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; + } + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; + } + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + inline void setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } + } + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; + } + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + inline bool hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL + } + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + inline void set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } + } + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + inline void set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); + } + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#else + ELPP_UNUSED(base::consts::kDefaultLogFile); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::ToFile, std::string("true"), true); + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + } + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + } + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; + } + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; + } + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } + } + static inline bool isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); + } + + static inline bool isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); + } + + static inline bool isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + (line[0] >= 65 || line[0] <= 90 || line[0] >= 97 || line[0] <= 122) && + (assignment != std::string::npos) && + (line.size() > assignment); + } + + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; + } + }; + +private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } + } + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } + } + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); + } + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); + } +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::map LogStreamsReferenceMap; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { +public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); + } + + TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); + } + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + inline bool enabled(Level level) { + return getConfigByVal(level, &m_enabledMap, "enabled"); + } + + inline bool toFile(Level level) { + return getConfigByVal(level, &m_toFileMap, "toFile"); + } + + inline const std::string& filename(Level level) { + return getConfigByRef(level, &m_filenameMap, "filename"); + } + + inline bool toStandardOutput(Level level) { + return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); + } + + inline const base::LogFormat& logFormat(Level level) { + return getConfigByRef(level, &m_logFormatMap, "logFormat"); + } + + inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) { + return getConfigByRef(level, &m_millisecondsWidthMap, "millisecondsWidth"); + } + + inline bool performanceTracking(Level level = Level::Global) { + return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); + } + + inline base::type::fstream_t* fileStream(Level level) { + return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + } + + inline std::size_t maxLogFileSize(Level level) { + return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + } + + inline std::size_t logFlushThreshold(Level level) { + return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); + } + +private: + Configurations* m_configurations; + std::map m_enabledMap; + std::map m_toFileMap; + std::map m_filenameMap; + std::map m_toStandardOutputMap; + std::map m_logFormatMap; + std::map m_millisecondsWidthMap; + std::map m_performanceTrackingMap; + std::map m_fileStreamMap; + std::map m_maxLogFileSizeMap; + std::map m_logFlushThresholdMap; + base::LogStreamsReferenceMap* m_logStreamsReference; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T unsafeGetConfigByVal(Level level, const std::map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + inline Conf_T& unsafeGetConfigByRef(Level level, std::map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::map* confMap, bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensative operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) { + setValue(Level::Global, + base::MillisecondsWidth(static_cast(getULong(conf->value()))), &m_millisecondsWidthMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_maxLogFileSizeMap); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + withFileSizeLimit.push_back(conf); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } + } + + unsigned long getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); + } + + std::string resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::MillisecondsWidth msWidth(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; + } + + void insertFile(Level level, const std::string& fullFilename) { + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); + } + + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + PreRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; + } + + bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, PreRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { +public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, unsigned long int lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, unsigned long int lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline unsigned long int lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, unsigned long int lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + unsigned long int m_lineNumber; + }; + +private: + const char* m_filename; + unsigned long int m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { +public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; + } + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; + } + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; + } + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { +public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { return m_enabled; } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } +protected: + virtual void handle(const T* handlePtr) = 0; +private: + bool m_enabled; +}; +class LogDispatchData { +public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { return m_logMessage; } + inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } +private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + + inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } + inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } +}; +class LogDispatchCallback : public Callback { +private: + friend class base::LogDispatcher; +}; +class PerformanceTrackingCallback : public Callback { +private: + friend class base::PerformanceTracker; +}; +class LogBuilder : base::NoCopy { +public: + virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")} + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!base::utils::s_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + } +private: + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configuations from. +class Logger : public base::threading::ThreadSafe, public Loggable { +public: + Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + } + + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); + } + + Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + + Logger& operator=(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + return *this; + } + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; + } + + /// @brief Reconfigures logger using existing configurations + inline void reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); + } + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static inline bool isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; + } + /// @brief Flushes logger to sync all log files for all levels + inline void flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); + } + + inline void flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + m_unflushedCount.find(level)->second = 0; + } + } + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ + template \ + inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ + template \ + inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::map m_unflushedCount; + base::LogStreamsReferenceMap* m_logStreamsReference; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); + } + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); + } +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { +public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); + } + + virtual ~RegisteredLoggers(void) { + flushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + } + return logger_; + } + + bool remove(const std::string& id) { + if (id == "default") { + return false; + } + Logger* logger = base::utils::Registry::get(id); + if (logger != nullptr) { + unregister(logger); + } + return true; + } + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline base::LogStreamsReferenceMap* logStreamsReference(void) { + return &m_logStreamsReference; + } + + inline void flushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + base::threading::ScopedLock scopedLock(lock()); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); + it != m_logStreamsReference.end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } + } + +private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMap m_logStreamsReference; + friend class el::base::Storage; +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { +public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { + } + + /// @brief Sets verbose level. Accepted range is 0-9 + inline void setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level < 0) + m_level = 0; + else if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; + } + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, level); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, level); + } + } + + bool allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + std::map::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } + } + + inline const std::map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(atoi(commandLineArgs->getParamValue("--v"))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(atoi(commandLineArgs->getParamValue("--V"))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } + } + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + +private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::map m_modules; +}; +} // namespace base +class LogMessage { +public: + LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(std::move(logger->stream().str())) { + } + inline Level level(void) const { return m_level; } + inline const std::string& file(void) const { return m_file; } + inline unsigned long int line(void) const { return m_line; } // NOLINT + inline const std::string& func(void) const { return m_func; } + inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; } + inline Logger* logger(void) const { return m_logger; } + inline const base::type::string_t& message(void) const { return m_message; } +private: + Level m_level; + std::string m_file; + unsigned long int m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { +public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { return &m_logMessage; } + inline LogDispatchData* data(void) { return &m_dispatchData; } + inline base::type::string_t logLine(void) { return m_logLine; } +private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { +public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } +private: + std::queue m_queue; +}; +class IWorker { +public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { +public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(0x0), + m_vRegistry(new base::VRegistry(0, &m_flags)), +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#else + ELPP_UNUSED(base::consts::kSysLogLoggerId); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING + installPerformanceTrackingCallback(std::string("DefaultPerformanceTrackingCallback")); + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING + } + + virtual ~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); + } + + inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); + } + + inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(lock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); + } + + inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; + } + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return callback(id, &m_logDispatchCallbacks); + } + + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return installCallback(id, &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + uninstallCallback(id, &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return callback(id, &m_performanceTrackingCallbacks); + } +private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::map m_logDispatchCallbacks; + std::map m_performanceTrackingCallbacks; + std::vector m_customFormatSpecifiers; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) + } + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } + + template + inline bool installCallback(const std::string& id, std::map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + inline void uninstallCallback(const std::string& id, std::map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + inline T* callback(const std::string& id, std::map* mapT) { + typename std::map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { +protected: + void handle(const LogDispatchData* data) { + m_data = data; + dispatch(std::move(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog))); + } +private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) + } +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { +protected: + void handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } + } +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { +public: + AsyncDispatchWorker() { + setContinueRunning(false); + } + + virtual ~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); + } + + inline bool clean() { + std::mutex m; + std::unique_lock lk(m); + cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); + } + + inline void emptyQueue() { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } + } + + virtual inline void start() { + base::threading::msleep(5000); // Wait extra few seconds + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::runner, this); + t1.join(); + } + + void handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) + } + + void run() { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } + } + + static void* runner(void *context) { + static_cast(context)->run(); + return NULL; + } + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningMutex); + m_continueRunning = value; + } + bool continueRunning(void) { + return m_continueRunning; + } +private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningMutex; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { +public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + base::threading::getCurrentThreadId()); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->millisecondsWidth(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), + base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), + base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()())); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; + } +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { +public: + LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(std::move(logMessage)), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } + // We minimize the time of ELPP's lock - this lock is released after log is written + base::threading::ScopedLock scopedLock(ELPP->lock()); + base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(&m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + } + +private: + bool m_proceed; + LogMessage m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { +public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { return getContainer().begin(); } + iterator end(void) { return getContainer().end(); } +private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, public std::priority_queue { +public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { +public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { +public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { +public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} + void initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); + } + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ + inline MessageBuilder& operator<<(LOG_TYPE msg) {\ + m_logger->stream() << msg;\ + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ + m_logger->stream() << " ";\ + }\ + return *this;\ + } + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + inline MessageBuilder& operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + inline MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + inline MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + inline MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + +/// @brief Macro used internally that can be used externally to make containers easylogging++ friendly +/// +/// @detail This macro expands to write an ostream& operator<< for container. This container is expected to +/// have begin() and end() methods that return respective iterators +/// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets +/// @param SizeMethod Method used to get size of container. +/// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro +/// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ + el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ + const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ + ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ + ContainerType::const_iterator elem = container.begin();\ + ContainerType::const_iterator endElem = container.end();\ + std::size_t size_ = container.SizeMethod; \ + ss << ELPP_LITERAL("[");\ + for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ + ss << ElementInstance;\ + ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ + }\ + if (elem != endElem) {\ + ss << ELPP_LITERAL("...");\ + }\ + ss << ELPP_LITERAL("]");\ + return ss;\ + } +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ + ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG +private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeperator; + + template + inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { +public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { +public: + Writer(Level level, const char* file, unsigned long int line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + Writer& construct(Logger* logger, bool needLock = true) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; + } + + Writer& construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; + } +protected: + Level m_level; + const char* m_file; + const unsigned long int m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + ELPP->acquireLock(); + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + ELPP->releaseLock(); // Need to unlock it for next writer + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } + } + + void processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED + } + + void triggerDispatch(void) { + if (m_proceed) { + base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger), m_dispatchAction).dispatch(); + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; + } +}; +class PErrorWriter : public base::Writer { +public: + PErrorWriter(Level level, const char* file, unsigned long int line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } + } +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); + } + template + inline void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } + } + template + void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, s, value, args...); + } + template + inline void Logger::log(Level level, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, log); + } +# if ELPP_VERBOSE_LOG + template + inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, s, value, args...); + } + template + inline void Logger::verbose(int vlevel, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, log); + } +# else + template + inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; + } + template + inline void Logger::verbose(int, const T&) { + return; + } +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ + template \ + inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ + log(LOG_LEVEL, s, value, args...);\ + }\ + template \ + inline void Logger::FUNCTION_NAME(const T& value) {\ + log(LOG_LEVEL, value);\ + } +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ + template \ + inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ + return;\ + }\ + template \ + inline void Logger::FUNCTION_NAME(const T&) {\ + return;\ + } + +# if ELPP_INFO_LOG + LOGGER_LEVEL_WRITERS(info, Level::Info) +# else + LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG + LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else + LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG + LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else + LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG + LOGGER_LEVEL_WRITERS(error, Level::Error) +# else + LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG + LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else + LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG + LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else + LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ + if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ + if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ + if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER +#if defined(ELPP_PERFORMANCE_LOGGER) +# define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER +#else +# define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId +#endif +class PerformanceTrackingData { +public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; } + inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; } + inline bool firstCheckpoint(void) const { return m_firstCheckpoint; } + inline std::string checkpointId(void) const { return m_checkpointId; } + inline const char* file(void) const { return m_file; } + inline unsigned long int line(void) const { return m_line; } + inline const char* func(void) const { return m_func; } + inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; } + inline const std::string& loggerId(void) const; +private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + unsigned long int m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { +public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuraton at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + } + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) + } + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); + } + inline Level level(void) const { return m_level; } +private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast(m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { +protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str(); + } +private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; } +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +class StackTrace : base::NoCopy { +public: + static const std::size_t kMaxStack = 64; + static const std::size_t kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) { + m_index = index; + m_location = std::string(loc); + m_demangled = std::string(demang); + m_hex = std::string(hex); + m_addr = std::string(addr); + } + StackTraceEntry(std::size_t index, char* loc) { + m_index = index; + m_location = std::string(loc); + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled + << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + return ss; + } + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; + } + +private: + std::vector m_stack; + + void generateNew(void) { +#if ELPP_STACKTRACE + m_stack.clear(); + void* stack[kMaxStack]; + std::size_t size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + char* mangName = nullptr; + char* hex = nullptr; + char* addr = nullptr; + for (char* c = strings[i]; *c; ++c) { + switch (*c) { + case '(': + mangName = c; + break; + case '+': + hex = c; + break; + case ')': + addr = c; + break; + } + } + // Perform demangling if parsed properly + if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { + *mangName++ = '\0'; + *hex++ = '\0'; + *addr++ = '\0'; + int status = 0; + char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, strings[i]); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE + } +}; +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} +static inline void crashAbort(int sig) { + base::utils::abort(sig); +} +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { +public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } + } + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } + } + +private: + Handler m_handler; +}; +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ + el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { +public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { +public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); + } + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); + } + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { +public: + /// @brief Gets existing or registers new logger + static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static inline bool unregisterLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->remove(identity); + } + /// @brief Whether or not logger with id is registered + static inline bool hasLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->has(identity); + } + /// @brief Reconfigures specified logger with new configurations + static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; + } + /// @brief Reconfigures logger with new configurations after looking it up using identity + static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); + } + /// @brief Reconfigures logger's single configuration + static inline Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; + } + /// @brief Reconfigures all the existing loggers with new configurations + static inline void reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } + } + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static inline void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } + } + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } + } + /// @brief Returns current default + static inline const Configurations* defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); + } + /// @brief Returns log stream reference pointer if needed by user + static inline const base::LogStreamsReferenceMap* logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); + } + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); + } + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static inline std::vector* populateAllLoggerIds(std::vector* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; + } + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + } + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempeted to configure logger using configuration file provided in argument + static inline bool configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; + } + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static inline void flushAll(void) { + ELPP->registeredLoggers()->flushAll(); + } + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); } + ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); } + ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static inline void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static inline void setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); + } + /// @brief Gets current verbose level + static inline base::type::VerboseLevel verboseLevel(void) { + return ELPP->vRegistry()->level(); + } + /// @brief Sets vmodules as specified (on the fly) + static inline void setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } + } + /// @brief Clears vmodules + static inline void clearVModules(void) { + ELPP->vRegistry()->clearModules(); + } +}; +class VersionInfo : base::StaticClass { +public: + /// @brief Current version number + static inline const std::string version(void) { return std::string("9.80"); } + /// @brief Release date of current version + static inline const std::string releaseDate(void) { return std::string("08-01-2015 0850hrs"); } +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_FUNC +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \ + el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ + el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ + el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ + C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ + CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ + C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ + CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ + C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ + CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ + C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ + CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ + C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ + C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +namespace el { +namespace base { +namespace utils { +template +static T* checkNotNull(T* ptr, const char* name, const char* loggers, ...) { + CLOG_IF(ptr == nullptr, FATAL, loggers) << "Check failed: [" << name << " != nullptr]"; + return ptr; +} +} // namespace utils +} // namespace base +} // namespace el +#define CCHECK_NOTNULL(ptr, ...) el::base::utils::checkNotNull(ptr, #ptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL(ptr, __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val)\ + ELPP_INITI_BASIC_DECLR\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage(val);\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP\ + ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ + new el::base::AsyncDispatchWorker()))\ + +#else +# define INITIALIZE_EASYLOGGINGPP\ + ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP\ + ELPP_INITI_BASIC_DECLR\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage;\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } +// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols +#define SHARE_EASYLOGGINGPP(initializedStorage)\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage(initializedStorage);\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H