From cdbcb4664d9afd3142ed438396c1eca9bb0d94c3 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 4 Dec 2016 17:21:47 +0100 Subject: [PATCH] added --lograwdatafile and --lograwdatasize options similar to --dumpfile/--dumpsize for logging received/sent bytes to a dedicated text file, extracted dump/raw file handling to a separate class, switched to an interface instead of a method for dumping/logging raw data, changed default dump file name to /tmp/ebusd_dump.bin, use "s_" for static variables, formatting --- src/ebusd/main.cpp | 120 +++++++++++++++++++++-------------- src/ebusd/main.h | 9 ++- src/ebusd/mainloop.cpp | 81 ++++++++++++++++------- src/ebusd/mainloop.h | 15 ++++- src/lib/ebus/device.cpp | 113 +++++++++++---------------------- src/lib/ebus/device.h | 107 +++++++++++-------------------- src/lib/utils/Makefile.am | 4 +- src/lib/utils/rotatefile.cpp | 96 ++++++++++++++++++++++++++++ src/lib/utils/rotatefile.h | 95 +++++++++++++++++++++++++++ 9 files changed, 416 insertions(+), 224 deletions(-) create mode 100644 src/lib/utils/rotatefile.cpp create mode 100644 src/lib/utils/rotatefile.h diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 86a22fb4..4946ee45 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -24,6 +24,7 @@ #include "mainloop.h" #include "bushandler.h" #include "log.h" +#include "rotatefile.h" #include #include #include @@ -90,8 +91,10 @@ static struct options opt = { "/var/ebusd/html", // htmlPath PACKAGE_LOGFILE, // logFile false, // logRaw + PACKAGE_LOGFILE, // logRawFile + 100, // logRawSize false, // dump - "/tmp/ebus_dump.bin", // dumpFile + "/tmp/" PACKAGE "_dump.bin", // dumpFile 100 // dumpSize }; @@ -130,8 +133,10 @@ static const char argpdoc[] = #define O_HTMLPA (O_HTTPPT+1) #define O_LOGARE (O_HTMLPA+1) #define O_LOGLEV (O_LOGARE+1) -#define O_LOGRAW (O_LOGLEV+1) -#define O_DMPFIL (O_LOGRAW+1) +#define O_RAW (O_LOGLEV+1) +#define O_RAWFIL (O_RAW+1) +#define O_RAWSIZ (O_RAWFIL+1) +#define O_DMPFIL (O_RAWSIZ+1) #define O_DMPSIZ (O_DMPFIL+1) /** the definition of the known program arguments. */ @@ -173,24 +178,28 @@ static const struct argp_option argpoptions[] = { {"logfile", 'l', "FILE", 0, "Write log to FILE (only for daemon) [" PACKAGE_LOGFILE "]", 0 }, {"logareas", O_LOGARE, "AREAS", 0, "Only write log for matching AREA(S): main,network,bus,update,all [all]", 0 }, {"loglevel", O_LOGLEV, "LEVEL", 0, "Only write log below or equal to LEVEL: error/notice/info/debug [notice]", 0 }, - {"lograwdata", O_LOGRAW, NULL, 0, "Log each received/sent byte on the bus", 0 }, - {NULL, 0, NULL, 0, "Dump options:", 6 }, - {"dump", 'D', NULL, 0, "Enable dump of received bytes", 0 }, - {"dumpfile", O_DMPFIL, "FILE", 0, "Dump received bytes to FILE [/tmp/ebus_dump.bin]", 0 }, - {"dumpsize", O_DMPSIZ, "SIZE", 0, "Make dump files no larger than SIZE kB [100]", 0 }, + {NULL, 0, NULL, 0, "Raw logging options:", 6 }, + {"lograwdata", O_RAW, NULL, 0, "Log each received/sent byte on the bus", 0 }, + {"lograwdatafile", O_RAWFIL, "FILE", 0, "Write raw log to FILE [" PACKAGE_LOGFILE "]", 0 }, + {"lograwdatasize", O_RAWSIZ, "SIZE", 0, "Make raw log file no larger than SIZE kB [100]", 0 }, + + {NULL, 0, NULL, 0, "Binary dump options:", 7 }, + {"dump", 'D', NULL, 0, "Enable binary dump of received bytes", 0 }, + {"dumpfile", O_DMPFIL, "FILE", 0, "Dump received bytes to FILE [/tmp/" PACKAGE "_dump.bin]", 0 }, + {"dumpsize", O_DMPSIZ, "SIZE", 0, "Make dump file no larger than SIZE kB [100]", 0 }, {NULL, 0, NULL, 0, NULL, 0 }, }; /** the global @a DataFieldTemplates. */ -static DataFieldTemplates globalTemplates; +static DataFieldTemplates s_globalTemplates; /** * the loaded @a DataFieldTemplates by path (may also carry * @a globalTemplates as replacement for missing file). */ -static map templatesByPath; +static map s_templatesByPath; /** * The program argument parsing function. @@ -404,15 +413,32 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) return EINVAL; } break; - case O_LOGRAW: // --lograwdata + + // Raw logging options: + case O_RAW: // --lograwdata opt->logRaw = true; break; + case O_RAWFIL: // --lograwdatafile=/var/log/ebusd.log + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid dumpfile"); + return EINVAL; + } + opt->logRawFile = arg; + break; + case O_RAWSIZ: // --lograwdatasize=100 + opt->logRawSize = parseInt(arg, 10, 1, 1000000, result); + if (result != RESULT_OK) { + argp_error(state, "invalid dumpsize"); + return EINVAL; + } + break; - // Dump options: + + // Binary dump options: case 'D': // --dump opt->dump = true; break; - case O_DMPFIL: // --dumpfile=/tmp/ebus_dump.bin + case O_DMPFIL: // --dumpfile=/tmp/ebusd_dump.bin if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { argp_error(state, "invalid dumpfile"); return EINVAL; @@ -521,12 +547,13 @@ void shutdown() s_messageMap = NULL; } // free templates - for (map::iterator it = templatesByPath.begin(); it != templatesByPath.end(); it++) { - if (it->second!=&globalTemplates) + for (map::iterator it = s_templatesByPath.begin(); it != s_templatesByPath.end(); it++) { + if (it->second!=&s_globalTemplates) { delete it->second; + } it->second = NULL; } - templatesByPath.clear(); + s_templatesByPath.clear(); // reset all signal handlers to default signal(SIGHUP, SIG_DFL); @@ -620,13 +647,14 @@ static result_t collectConfigFiles(const string path, const string prefix, const DataFieldTemplates* getTemplates(const string filename) { string path; size_t pos = filename.find_last_of('/'); - if (pos!=string::npos) + if (pos!=string::npos) { path = filename.substr(0, pos); - map::iterator it = templatesByPath.find(path); - if (it!=templatesByPath.end()) { + } + map::iterator it = s_templatesByPath.find(path); + if (it!=s_templatesByPath.end()) { return it->second; } - return &globalTemplates; + return &s_globalTemplates; } /** @@ -639,17 +667,17 @@ DataFieldTemplates* getTemplates(const string filename) { * @return the @a DataFieldTemplates. */ static bool readTemplates(const string path, const string extension, bool available, bool verbose=false) { - map::iterator it = templatesByPath.find(path); - if (it!=templatesByPath.end()) { + map::iterator it = s_templatesByPath.find(path); + if (it!=s_templatesByPath.end()) { return false; } DataFieldTemplates* templates; if (path==opt.configPath || !available) { - templates = &globalTemplates; + templates = &s_globalTemplates; } else { - templates = new DataFieldTemplates(globalTemplates); + templates = new DataFieldTemplates(s_globalTemplates); } - templatesByPath[path] = templates; + s_templatesByPath[path] = templates; if (!available) { // global templates are stored as replacement in order to determine whether the directory was already loaded return true; @@ -741,14 +769,14 @@ result_t loadConfigFiles(MessageMap* messages, bool verbose, bool denyRecursive) { logInfo(lf_main, "loading configuration files from %s", opt.configPath); messages->clear(); - globalTemplates.clear(); - for (map::iterator it = templatesByPath.begin(); it != templatesByPath.end(); it++) { - if (it->second!=&globalTemplates) { + s_globalTemplates.clear(); + for (map::iterator it = s_templatesByPath.begin(); it != s_templatesByPath.end(); it++) { + if (it->second!=&s_globalTemplates) { delete it->second; } it->second = NULL; } - templatesByPath.clear(); + s_templatesByPath.clear(); result_t result = readConfigFiles(string(opt.configPath), ".csv", messages, (!opt.scanConfig || opt.checkConfig) && !denyRecursive, verbose); if (result == RESULT_OK) { @@ -908,18 +936,6 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS return RESULT_OK; } -/** - * Create a log message for a received/sent raw data byte. - * @param byte the raw data byte. - * @param received true if the byte was received, false if it was sent. - */ -static void logRawData(const unsigned char byte, bool received) -{ - if (received) - logNotice(lf_bus, "<%02x", byte); - else - logNotice(lf_bus, ">%02x", byte); -} /** * Main method. @@ -965,10 +981,17 @@ int main(int argc, char* argv[]) continue; } unsigned char address = master[1]; - string file; - res = loadScanConfigFile(s_messageMap, address, slave, file, true); - if (res==RESULT_OK) - logInfo(lf_main, "scan config %2.2x: file %s loaded", address, file.c_str()); + Message* message = s_messageMap->getScanMessage(address); + if (!message) { + logError(lf_main, "invalid scan address %2.2x", address); + } else { + message->storeLastData(master, slave); + string file; + res = loadScanConfigFile(s_messageMap, address, slave, file, true); + if (res==RESULT_OK) { + logInfo(lf_main, "scan config %2.2x: file %s loaded", address, file.c_str()); + } + } } if (result == RESULT_OK && opt.checkConfig > 1) { logNotice(lf_main, "configuration dump:"); @@ -978,8 +1001,9 @@ int main(int argc, char* argv[]) return 0; } + // open the device - Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend, &logRawData); + Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); if (device == NULL) { logError(lf_main, "unable to create device %s", opt.device); return EINVAL; @@ -1003,9 +1027,9 @@ int main(int argc, char* argv[]) // load configuration files loadConfigFiles(s_messageMap); - if (s_messageMap->sizeConditions()>0 && opt.pollInterval==0) + if (s_messageMap->sizeConditions()>0 && opt.pollInterval==0) { logError(lf_main, "conditions require a poll interval > 0"); - + } // wait for end of MainLoop s_mainLoop->join(); diff --git a/src/ebusd/main.h b/src/ebusd/main.h index 1e28b510..b9a5ba6e 100644 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -59,10 +59,13 @@ struct options const char* htmlPath; //!< path for HTML files served by the HTTP port [/var/ebusd/html] const char* logFile; //!< log file name [/var/log/ebusd.log] - bool logRaw; //!< log each received/sent byte on the bus - bool dump; //!< dump received bytes - const char* dumpFile; //!< dump file name [/tmp/ebus_dump.bin] + bool logRaw; //!< raw log each received/sent byte on the bus + const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log] + int logRawSize; //!< maximum size of raw log file in kB [100] + + bool dump; //!< binary dump received bytes + const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin] int dumpSize; //!< maximum size of dump file in kB [100] }; diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 3b68ecde..49df494e 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -49,19 +49,21 @@ MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* message m_address(opt.address), m_scanConfig(opt.scanConfig), m_initialScan(opt.initialScan), m_enableHex(opt.enableHex) { - // setup Device - m_device->setLogRaw(opt.logRaw); - m_device->setDumpRawFile(opt.dumpFile); - m_device->setDumpRawMaxSize(opt.dumpSize); - m_device->setDumpRaw(opt.dump); - // open Device result_t result = m_device->open(); - if (result != RESULT_OK) + if (result != RESULT_OK) { logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); - else if (!m_device->isValid()) + } else if (!m_device->isValid()) { logError(lf_bus, "device %s not available", m_device->getName()); - + } + m_device->setListener(this); + if (opt.dumpFile[0]) { + m_dumpFile = new RotateFile(opt.dumpFile, opt.dumpSize); + } + if (opt.logRawFile[0] && strcmp(opt.logRawFile, opt.logFile)!=0) { + m_logRawFile = new RotateFile(opt.logRawFile, opt.logRawSize, true); + } + m_logRawEnabled = opt.logRaw; // create BusHandler unsigned int latency; if (opt.latency<0) { @@ -86,6 +88,14 @@ MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* message MainLoop::~MainLoop() { join(); + if (m_dumpFile) { + delete m_dumpFile; + m_dumpFile = NULL; + } + if (m_logRawFile) { + delete m_logRawFile; + m_logRawFile = NULL; + } if (m_network != NULL) { delete m_network; m_network = NULL; @@ -233,6 +243,22 @@ void MainLoop::run() } } +void MainLoop::notifyDeviceData(const unsigned char byte, bool received) +{ + if (received && m_dumpFile) { + m_dumpFile->write((unsigned char*)&byte, 1); + } + if (m_logRawFile) { + m_logRawFile->write((unsigned char*)&byte, 1, received); + } else if (m_logRawEnabled){ + if (received) { + logNotice(lf_bus, "<%02x", byte); + } else { + logNotice(lf_bus, ">%02x", byte); + } + } +} + string MainLoop::decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload) { ostringstream result; @@ -1105,25 +1131,32 @@ string MainLoop::executeLog(vector &args) string MainLoop::executeRaw(vector &args) { - if (args.size() != 1) + if (args.size() != 1) { return "usage: raw\n" - " Toggle logging raw bytes."; - - bool enabled = !m_device->getLogRaw(); - m_device->setLogRaw(enabled); - - return enabled ? "raw output enabled" : "raw output disabled"; + " Toggle logging of each byte."; + } + bool enabled; + if (m_logRawFile) { + enabled = !m_logRawFile->isEnabled(); + m_logRawFile->setEnabled(enabled); + } else { + enabled = !m_logRawEnabled; + m_logRawEnabled = enabled; + } + return enabled ? "raw logging enabled" : "raw logging disabled"; } string MainLoop::executeDump(vector &args) { - if (args.size() != 1) + if (args.size() != 1) { return "usage: dump\n" - " Toggle dumping raw bytes."; - - bool enabled = !m_device->getDumpRaw(); - m_device->setDumpRaw(enabled); - + " Toggle binary dump of received bytes."; + } + if (!m_dumpFile) { + return "dump not configured"; + } + bool enabled = !m_dumpFile->isEnabled(); + m_dumpFile->setEnabled(enabled); return enabled ? "dump enabled" : "dump disabled"; } @@ -1193,8 +1226,8 @@ string MainLoop::executeHelp() " log Set log area/level: log [AREA[,AREA]*] [LEVEL]\n" " AREA: main|network|bus|update|all\n" " LEVEL: error|notice|info|debug\n" - " raw Toggle logging raw bytes\n" - " dump Toggle dumping raw bytes\n" + " raw Toggle logging of each byte\n" + " dump Toggle binary dump of received bytes\n" " reload Reload CSV config files\n" " quit|q Close connection\n" " help|h Print help help [COMMAND]"; diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 47abc567..0b2cd027 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -22,6 +22,7 @@ #include "message.h" #include "network.h" #include "bushandler.h" +#include "rotatefile.h" /** \file mainloop.h */ @@ -30,7 +31,7 @@ using namespace std; /** * The main loop handling requests from connected clients. */ -class MainLoop : public Thread +class MainLoop : public Thread, DeviceListener { public: @@ -64,6 +65,9 @@ public: */ void addMessage(NetMessage* message) { m_netQueue.push(message); } + // @copydoc + virtual void notifyDeviceData(const unsigned char byte, bool received); + private: /** the @a Device instance. */ @@ -72,6 +76,15 @@ private: /** the number of reconnects requested from the @a Device. */ unsigned int m_reconnectCount; + /** the @a RotateFile for writing sent/received bytes in log format, or NULL. */ + RotateFile* m_logRawFile; + + /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */ + bool m_logRawEnabled; + + /** the @a RotateFile for dumping received data, or NULL. */ + RotateFile* m_dumpFile; + /** the @a MessageMap instance. */ MessageMap* m_messages; diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 66e7774b..4ef6813f 100644 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -42,11 +42,9 @@ using namespace std; Device::~Device() { close(); - m_dumpRawStream.close(); } -Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend, - void (*logRawFunc)(const unsigned char byte, bool received)) +Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) { if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) { char* in = strdup(name); @@ -81,9 +79,9 @@ Device* Device::create(const char* name, const bool checkDevice, const bool read free(in); address.sin_family = AF_INET; address.sin_port = (in_port_t)htons((uint16_t)port); - return new NetworkDevice(name, address, readOnly, initialSend, logRawFunc, udp); + return new NetworkDevice(name, address, readOnly, initialSend, udp); } - return new SerialDevice(name, checkDevice, readOnly, initialSend, logRawFunc); + return new SerialDevice(name, checkDevice, readOnly, initialSend); } void Device::close() @@ -96,34 +94,34 @@ void Device::close() bool Device::isValid() { - if (m_fd == -1) + if (m_fd == -1) { return false; - - if (m_checkDevice) + } + if (m_checkDevice) { checkDevice(); - + } return m_fd != -1; } result_t Device::send(const unsigned char value) { - if (!isValid()) + if (!isValid()) { return RESULT_ERR_DEVICE; - - if (m_readOnly || write(value) != 1) + } + if (m_readOnly || write(value) != 1) { return RESULT_ERR_SEND; - - if (m_logRaw && m_logRawFunc != NULL) - (*m_logRawFunc)(value, false); - + } + if (m_listener != NULL) { + m_listener->notifyDeviceData(value, false); + } return RESULT_OK; } result_t Device::recv(const long timeout, unsigned char& value) { - if (!isValid()) + if (!isValid()) { return RESULT_ERR_DEVICE; - + } if (!available() && timeout > 0) { int ret; struct timespec tdiff; @@ -154,80 +152,42 @@ result_t Device::recv(const long timeout, unsigned char& value) ret = 1; // ignore timeout if neither ppoll nor pselect are available #endif #endif - if (ret == -1) return RESULT_ERR_DEVICE; - if (ret == 0) return RESULT_ERR_TIMEOUT; + if (ret == -1) { + return RESULT_ERR_DEVICE; + } + if (ret == 0) { + return RESULT_ERR_TIMEOUT; + } } // directly read byte from device ssize_t nbytes = read(value); - if (nbytes == 0) + if (nbytes == 0) { return RESULT_ERR_EOF; - if (nbytes < 0) + } + if (nbytes < 0) { return RESULT_ERR_DEVICE; - - if (m_logRaw && m_logRawFunc != NULL) - (*m_logRawFunc)(value, true); - - if (m_dumpRaw && m_dumpRawStream.is_open()) { - m_dumpRawStream.write((char*)&value, 1); - m_dumpRawFileSize++; - if ((m_dumpRawFileSize%1024) == 0) - m_dumpRawStream.flush(); - - if (m_dumpRawFileSize >= m_dumpRawMaxSize * 1024) { - string oldfile = string(m_dumpRawFile) + ".old"; - if (rename(m_dumpRawFile, oldfile.c_str()) == 0) { - m_dumpRawStream.close(); - m_dumpRawStream.open(m_dumpRawFile, ios::out | ios::binary | ios::app); - m_dumpRawFileSize = 0; - } - } } - + if (m_listener != NULL) { + m_listener->notifyDeviceData(value, true); + } return RESULT_OK; } -void Device::setDumpRaw(bool dumpRaw) -{ - if (dumpRaw == m_dumpRaw) - return; - - m_dumpRaw = dumpRaw; - - if (!dumpRaw || m_dumpRawFile == NULL) - m_dumpRawStream.close(); - else { - m_dumpRawStream.open(m_dumpRawFile, ios::out | ios::binary | ios::app); - m_dumpRawFileSize = 0; - } -} - -void Device::setDumpRawFile(const char* dumpFile) { - if ((dumpFile == NULL) ? (m_dumpRawFile == NULL) : (m_dumpRawFile != NULL && (m_dumpRawFile == dumpFile || strcmp(dumpFile, m_dumpRawFile) == 0))) - return; - - m_dumpRawStream.close(); - m_dumpRawFile = dumpFile; - - if (m_dumpRaw && m_dumpRawFile != NULL) { - m_dumpRawStream.open(m_dumpRawFile, ios::out | ios::binary | ios::app); - m_dumpRawFileSize = 0; - } -} - result_t SerialDevice::open() { - if (m_fd != -1) + if (m_fd != -1) { close(); - + } struct termios newSettings; // open file descriptor m_fd = ::open(m_name, O_RDWR | O_NOCTTY); - if (m_fd < 0) + if (m_fd < 0) { return RESULT_ERR_NOTFOUND; + } if (isatty(m_fd) == 0) { close(); return RESULT_ERR_NOTFOUND; @@ -283,7 +243,6 @@ void SerialDevice::close() void SerialDevice::checkDevice() { int port; - if (ioctl(m_fd, TIOCMGET, &port) == -1) { close(); } @@ -292,13 +251,13 @@ void SerialDevice::checkDevice() result_t NetworkDevice::open() { - if (m_fd != -1) + if (m_fd != -1) { close(); - + } m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0); - if (m_fd < 0) + if (m_fd < 0) { return RESULT_ERR_GENERIC_IO; - + } int ret; if (m_udp) { struct sockaddr_in address = m_address; diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index b9688a8d..6a033d5b 100644 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -38,6 +38,28 @@ using namespace std; +/** + * Interface for listening to data received on/sent to a device. + */ +class DeviceListener +{ +public: + + /** + * Destructor. + */ + virtual ~DeviceListener() {} + + /** + * Listener method that is called when a data byte was received/sent. + * @param byte the data byte received/sent. + * @param received @a true on reception, @a false on sending. + */ + virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract + +}; + + /** * The base class for accessing an eBUS. */ @@ -51,13 +73,10 @@ public: * @param checkDevice whether to regularly check the device availability (only for serial devices). * @param readOnly whether to allow read access to the device only. * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @param logRawFunc the function to call for logging raw data, or NULL. */ - Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend, - void (*logRawFunc)(const unsigned char byte, bool received)) + Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1), - m_logRaw(false), m_logRawFunc(logRawFunc), - m_dumpRaw(false), m_dumpRawFile(NULL), m_dumpRawMaxSize(0), m_dumpRawStream(), m_dumpRawFileSize(0) {} + m_listener(NULL) {} /** * Destructor. @@ -70,12 +89,10 @@ public: * @param checkDevice whether to regularly check the device availability (only for serial devices). * @param readOnly whether to allow read access to the device only. * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @param logRawFunc the function to call for logging raw data, or NULL. * @return the new @a Device, or NULL on error. * Note: the caller needs to free the created instance. */ - static Device* create(const char* name, const bool checkDevice=true, const bool readOnly=false, const bool initialSend=false, - void (*logRawFunc)(const unsigned char byte, bool received)=NULL); + static Device* create(const char* name, const bool checkDevice=true, const bool readOnly=false, const bool initialSend=false); /** * Get the transfer latency of this device. @@ -109,42 +126,6 @@ public: */ result_t recv(const long timeout, unsigned char& value); - /** - * Get whether logging of raw data is enabled. - * @return whether logging of raw data is enabled. - */ - bool getLogRaw() { return m_logRaw; } - - /** - * Enable or disable logging of raw data. - * @param logRaw true to enable logging of raw data, false to disable it. - */ - void setLogRaw(bool logRaw=true) { m_logRaw = logRaw; } - - /** - * Get whether dumping of raw data to a file is enabled. - * @return whether dumping of raw data to a file is enabled. - */ - bool getDumpRaw() { return m_dumpRaw; } - - /** - * Enable or disable dumping of raw data to a file. - * @param dumpRaw true to enable dumping of raw data to a file, false to disable it. - */ - void setDumpRaw(bool dumpRaw=true); - - /** - * Set the name of the file to dump raw data to. - * @param dumpFile the name of the file to dump raw data to. - */ - void setDumpRawFile(const char* dumpFile); - - /** - * Set the maximum size of a file to dump raw data to. - * @param maxSize the maximum size of a file to dump raw data to. - */ - void setDumpRawMaxSize(const long maxSize) { m_dumpRawMaxSize = maxSize; } - /** * Return the device name. * @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). @@ -163,6 +144,12 @@ public: */ bool isReadOnly() const { return m_readOnly; } + /** + * Set the @a DeviceListener. + * @param listener the @a DeviceListener. + */ + void setListener(DeviceListener* listener) { m_listener = listener; } + protected: /** * Check if the device is still available and close it if not. @@ -206,26 +193,9 @@ protected: int m_fd; private: - /** whether logging of raw data is enabled. */ - bool m_logRaw; - /** the function to call for logging raw data, or NULL. */ - void (*m_logRawFunc)(const unsigned char byte, bool received); - - /** whether dumping of raw data to a file is enabled. */ - bool m_dumpRaw; - - /** the name of the file to dump raw data to. */ - const char* m_dumpRawFile; - - /** the maximum size of @a m_dumpFile, or 0 for infinite. */ - long m_dumpRawMaxSize; - - /** the @a ofstream for dumping raw data to. */ - ofstream m_dumpRawStream; - - /** the number of bytes already written to the @a m_dumpFile. */ - long m_dumpRawFileSize; + /** the @a DeviceListener, or NULL. */ + DeviceListener* m_listener; }; @@ -241,11 +211,9 @@ public: * @param checkDevice whether to regularly check the device availability (only for serial devices). * @param readOnly whether to allow read access to the device only. * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @param logRawFunc the function to call for logging raw data, or NULL. */ - SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend, - void (*logRawFunc)(const unsigned char byte, bool received)) - : Device(name, checkDevice, readOnly, initialSend, logRawFunc) {} + SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) + : Device(name, checkDevice, readOnly, initialSend) {} // @copydoc virtual result_t open(); @@ -275,12 +243,11 @@ public: * @param address the socket address of the device. * @param readOnly whether to allow read access to the device only. * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @param logRawFunc the function to call for logging raw data, or NULL. * @param udp true for UDP, false to TCP. */ NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend, - void (*logRawFunc)(const unsigned char byte, bool received), const bool udp) - : Device(name, true, readOnly, initialSend, logRawFunc), m_address(address), m_udp(udp), + const bool udp) + : Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp), m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {} // @copydoc diff --git a/src/lib/utils/Makefile.am b/src/lib/utils/Makefile.am index 262980fe..f04a474c 100644 --- a/src/lib/utils/Makefile.am +++ b/src/lib/utils/Makefile.am @@ -11,7 +11,9 @@ libutils_a_SOURCES = log.cpp \ clock.h \ clock.cpp \ queue.h \ - notify.h + notify.h \ + rotatefile.h \ + rotatefile.cpp distclean-local: -rm -f Makefile.in diff --git a/src/lib/utils/rotatefile.cpp b/src/lib/utils/rotatefile.cpp new file mode 100644 index 00000000..65cfe511 --- /dev/null +++ b/src/lib/utils/rotatefile.cpp @@ -0,0 +1,96 @@ +/* + * ebusd - daemon for communication with eBUS heating systems. + * Copyright (C) 2016 John Baier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "rotatefile.h" +#include "clock.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +RotateFile::~RotateFile() +{ + if (m_stream) { + fclose(m_stream); + m_stream = NULL; + } +} + +bool RotateFile::setEnabled(bool enabled) +{ + if (enabled==m_enabled) { + return false; + } + m_enabled = enabled; + if (m_stream) { + fclose(m_stream); + m_stream = NULL; + } + if (enabled) { + m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); + m_fileSize = 0; + } + return true; +} + +void RotateFile::write(unsigned char* value, unsigned int size, bool received) +{ + if (!m_enabled || !m_stream) { + return; + } + if (m_textMode) { + struct timespec ts; + struct tm* tm; + clockGettime(&ts); + tm = localtime(&ts.tv_sec); + char* buf; + fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, ts.tv_nsec/1000000, + received ? '<' : '>' + ); + for (unsigned int pos=0; pos= m_maxSize * 1024) { + string oldfile = string(m_fileName)+".old"; + if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) { + fclose(m_stream); + m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); + m_fileSize = 0; + } + } +} diff --git a/src/lib/utils/rotatefile.h b/src/lib/utils/rotatefile.h new file mode 100644 index 00000000..fd4dfd10 --- /dev/null +++ b/src/lib/utils/rotatefile.h @@ -0,0 +1,95 @@ +/* + * ebusd - daemon for communication with eBUS heating systems. + * Copyright (C) 2016 John Baier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBUTILS_ROTATEFILE_H_ +#define LIBUTILS_ROTATEFILE_H_ + +#include +#include +#include + +/** @file rotatefile.h + * Helpers for writing to rotating files. + */ + +using namespace std; + +/** + * Helper class for writing to a rotating file with maximum size. + */ +class RotateFile +{ +public: + /** + * Construct a new instance. + * @param fileName the name of the file write to. + * @param maxSize the maximum size of the file to write to. + * @param textMode whether to write each byte with prefixed timestamp and direction as text. + */ + RotateFile(const string fileName, const long maxSize, const bool textMode=false) + : m_enabled(false), m_fileName(fileName), m_maxSize(maxSize), m_textMode(textMode), m_stream(), m_fileSize(0) {} + + /** + * Destructor. + */ + virtual ~RotateFile(); + + /** + * Enable or disable writing to the file. + * @param enabled @p true to enable writing to the file, @p false to disable it. + * @return @p true when the state was changed, @p false otherwise. + */ + bool setEnabled(bool enabled=true); + + /** + * Return whether writing to the file is enabled. + * @return whether writing to the file is enabled. + */ + bool isEnabled() { return m_enabled; } + + /** + * Write a number of bytes to the stream. + * @param value the pointer to the bytes to write. + * @param size the number of bytes to write. + * @param received @a true on reception, @a false on sending (only relevant in text mode). + */ + void write(unsigned char* value, unsigned int size, bool received=true); + +private: + /** whether writing to the file is enabled. */ + bool m_enabled; + + /** the name of the file write to. */ + const string m_fileName; + + /** the maximum size of @a m_file, or 0 for infinite. */ + const long m_maxSize; + + /** whether to write each byte with prefixed timestamp and direction as text. */ + const bool m_textMode; + + /** the @a FILE to writing to. */ + FILE* m_stream; + + /** the number of bytes already written to the @a m_file. */ + unsigned long m_fileSize; + +}; + +#endif // LIBUTILS_ROTATEFILE_H_ +