Move file IO to standalone class
Adds object oriented way to work with zlib. Error handling for file IO
is based on C++ exceptions.
Replaces printf-like output with direct writing. This prevents buffer
overflow in zlib during write operations.
Change-Id: I626be309250c623cd60021ee6c17518855a171a6
Signed-off-by: Artem Senichev <a.senichev@yadro.com>
diff --git a/Makefile.am b/Makefile.am
index 578bf07..32adb6d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,10 +15,14 @@
src/dbus_server.cpp \
src/dbus_watch.hpp \
src/dbus_watch.cpp \
+ src/log_file.hpp \
+ src/log_file.cpp \
src/log_manager.hpp \
src/log_manager.cpp \
src/log_storage.hpp \
- src/log_storage.cpp
+ src/log_storage.cpp \
+ src/zlib_exception.hpp \
+ src/zlib_exception.cpp
hostlogger_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
hostlogger_LDADD = $(SDBUSPLUS_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS)
diff --git a/src/log_file.cpp b/src/log_file.cpp
new file mode 100644
index 0000000..0f7a20a
--- /dev/null
+++ b/src/log_file.cpp
@@ -0,0 +1,76 @@
+/**
+ * @brief Log file.
+ *
+ * This file is part of HostLogger project.
+ *
+ * Copyright (c) 2020 YADRO
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "log_file.hpp"
+
+#include "zlib_exception.hpp"
+
+LogFile::LogFile(const char* fileName)
+{
+ fd_ = gzopen(fileName, "w");
+ if (fd_ == Z_NULL)
+ throw ZlibException(ZlibException::Create, Z_ERRNO, fd_, fileName);
+ fileName_ = fileName;
+}
+
+LogFile::~LogFile()
+{
+ if (fd_ != Z_NULL)
+ gzclose_w(fd_);
+}
+
+void LogFile::close()
+{
+ if (fd_ != Z_NULL)
+ {
+ const int rc = gzclose_w(fd_);
+ if (rc != Z_OK)
+ throw ZlibException(ZlibException::Close, rc, fd_, fileName_);
+ fd_ = Z_NULL;
+ fileName_.clear();
+ }
+}
+
+void LogFile::write(time_t timeStamp, const std::string& message) const
+{
+ int rc;
+
+ // Convert time stamp and write it
+ tm tmLocal;
+ localtime_r(&timeStamp, &tmLocal);
+ rc = gzprintf(fd_, "[ %02i:%02i:%02i ]: ", tmLocal.tm_hour, tmLocal.tm_min,
+ tmLocal.tm_sec);
+ if (rc <= 0)
+ throw ZlibException(ZlibException::Write, rc, fd_, fileName_);
+
+ // Write message
+ const size_t len = message.length();
+ if (len)
+ {
+ rc = gzwrite(fd_, message.data(), static_cast<unsigned int>(len));
+ if (rc <= 0)
+ throw ZlibException(ZlibException::Write, rc, fd_, fileName_);
+ }
+
+ // Write EOL
+ rc = gzputc(fd_, '\n');
+ if (rc <= 0)
+ throw ZlibException(ZlibException::Write, rc, fd_, fileName_);
+}
diff --git a/src/log_file.hpp b/src/log_file.hpp
new file mode 100644
index 0000000..8494f8b
--- /dev/null
+++ b/src/log_file.hpp
@@ -0,0 +1,67 @@
+/**
+ * @brief Log file.
+ *
+ * This file is part of HostLogger project.
+ *
+ * Copyright (c) 2020 YADRO
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <zlib.h>
+
+#include <ctime>
+#include <string>
+
+/** @class LogFile
+ * @brief Log file writer.
+ */
+class LogFile
+{
+ public:
+ /** @brief Constructor - open new file for writing logs.
+ *
+ * @param[in] fileName - path to the file
+ *
+ * @throw ZlibException in case of errors
+ */
+ LogFile(const char* fileName);
+
+ ~LogFile();
+
+ LogFile(const LogFile&) = delete;
+ LogFile& operator=(const LogFile&) = delete;
+
+ /** @brief Close file.
+ *
+ * @throw ZlibException in case of errors
+ */
+ void close();
+
+ /** @brief Write log message to file.
+ *
+ * @param[in] timeStamp - time stamp of the log message
+ * @param[in] message - log message text
+ *
+ * @throw ZlibException in case of errors
+ */
+ void write(time_t timeStamp, const std::string& message) const;
+
+ private:
+ /** @brief File name. */
+ std::string fileName_;
+ /** @brief zLib file descriptor. */
+ gzFile fd_;
+};
diff --git a/src/log_manager.cpp b/src/log_manager.cpp
index 66a08c6..fde2f6c 100644
--- a/src/log_manager.cpp
+++ b/src/log_manager.cpp
@@ -159,7 +159,7 @@
if (logFile.empty())
return EIO;
- rc = storage_.write(logFile.c_str());
+ rc = storage_.save(logFile.c_str());
if (rc != 0)
return rc;
diff --git a/src/log_storage.cpp b/src/log_storage.cpp
index 96b0b12..ff9b213 100644
--- a/src/log_storage.cpp
+++ b/src/log_storage.cpp
@@ -21,15 +21,14 @@
#include "log_storage.hpp"
#include "config.hpp"
+#include "log_file.hpp"
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
-LogStorage::LogStorage() : last_complete_(true)
-{
-}
+#include <exception>
void LogStorage::parse(const char* data, size_t len)
{
@@ -92,7 +91,7 @@
return messages_.empty();
}
-int LogStorage::write(const char* fileName) const
+int LogStorage::save(const char* fileName) const
{
int rc = 0;
@@ -102,57 +101,35 @@
return 0;
}
- const gzFile fd = gzopen(fileName, "w");
- if (fd == Z_NULL)
+ try
{
- rc = errno;
- fprintf(stderr, "Unable to open file %s: error [%i] %s\n", fileName, rc,
- strerror(rc));
- return rc;
+ LogFile log(fileName);
+
+ // Write full datetime stamp as the first record
+ const time_t& tmStart = messages_.begin()->timeStamp;
+ tm tmLocal;
+ localtime_r(&tmStart, &tmLocal);
+ char tmText[20]; // size of "%F %T" asciiz (YYYY-MM-DD HH:MM:SS)
+ strftime(tmText, sizeof(tmText), "%F %T", &tmLocal);
+ std::string titleMsg = ">>> Log collection started at ";
+ titleMsg += tmText;
+ log.write(tmStart, titleMsg);
+
+ // Write messages
+ for (auto it = messages_.begin(); it != messages_.end(); ++it)
+ log.write(it->timeStamp, it->text);
+
+ log.close();
}
-
- // Write full datetime stamp as the first record
- const time_t& logStartTime = messages_.begin()->timeStamp;
- tm localTime = {0};
- localtime_r(&logStartTime, &localTime);
- char msgText[64];
- snprintf(msgText, sizeof(msgText),
- ">>> Log collection started at %02i.%02i.%i %02i:%02i:%02i",
- localTime.tm_mday, localTime.tm_mon + 1, localTime.tm_year + 1900,
- localTime.tm_hour, localTime.tm_min, localTime.tm_sec);
- const Message startMsg = {logStartTime, msgText};
- rc |= write(fd, startMsg);
-
- // Write messages
- for (auto it = messages_.begin(); rc == 0 && it != messages_.end(); ++it)
- rc |= write(fd, *it);
-
- rc = gzclose_w(fd);
- if (rc != Z_OK)
- fprintf(stderr, "Unable to close file %s: error [%i]\n", fileName, rc);
+ catch (std::exception& e)
+ {
+ rc = EIO;
+ fprintf(stderr, "%s\n", e.what());
+ }
return rc;
}
-int LogStorage::write(gzFile fd, const Message& msg) const
-{
- // Convert timestamp to local time
- tm localTime = {0};
- localtime_r(&msg.timeStamp, &localTime);
-
- // Write message to the file
- const int rc =
- gzprintf(fd, "[ %02i:%02i:%02i ]: %s\n", localTime.tm_hour,
- localTime.tm_min, localTime.tm_sec, msg.text.c_str());
- if (rc <= 0)
- {
- fprintf(stderr, "Unable to write file: error [%i]\n", -rc);
- return EIO;
- }
-
- return 0;
-}
-
void LogStorage::shrink()
{
if (loggerConfig.storageSizeLimit)
diff --git a/src/log_storage.hpp b/src/log_storage.hpp
index 550afd9..5361c88 100644
--- a/src/log_storage.hpp
+++ b/src/log_storage.hpp
@@ -33,9 +33,6 @@
class LogStorage
{
public:
- /** @brief Constructor. */
- LogStorage();
-
/** @brief Parse input log stream and append messages to the storage.
*
* @param[in] data - pointer to the message buffer
@@ -58,7 +55,7 @@
*
* @return error code, 0 if operation completed successfully
*/
- int write(const char* fileName) const;
+ int save(const char* fileName) const;
private:
/** @struct Message
@@ -79,15 +76,6 @@
*/
void append(const char* msg, size_t len);
- /** @brief Write message to the file.
- *
- * @param[in] fd - descriptor of the file to write
- * @param[in] msg - message to write
- *
- * @return error code, 0 if operation completed successfully
- */
- int write(gzFile fd, const Message& msg) const;
-
/** @brief Shrink storage by removing oldest messages. */
void shrink();
@@ -95,5 +83,5 @@
/** @brief List of messages. */
std::list<Message> messages_;
/** @brief Flag to indicate that the last message is incomplete. */
- bool last_complete_;
+ bool last_complete_ = true;
};
diff --git a/src/zlib_exception.cpp b/src/zlib_exception.cpp
new file mode 100644
index 0000000..e88c7b3
--- /dev/null
+++ b/src/zlib_exception.cpp
@@ -0,0 +1,77 @@
+/**
+ * @brief zLib exception.
+ *
+ * This file is part of HostLogger project.
+ *
+ * Copyright (c) 2020 YADRO
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "zlib_exception.hpp"
+
+#include <cstring>
+
+ZlibException::ZlibException(Operation op, int code, gzFile fd,
+ const std::string& fileName)
+{
+ std::string details;
+ if (code == Z_ERRNO)
+ {
+ // System error
+ const int errCode = errno ? errno : EIO;
+ details = strerror(errCode);
+ }
+ else if (fd != Z_NULL)
+ {
+ // Try to get description from zLib
+ int lastErrCode = 0;
+ const char* lastErrDesc = gzerror(fd, &lastErrCode);
+ if (lastErrCode)
+ {
+ details = '[';
+ details += std::to_string(lastErrCode);
+ details += "] ";
+ details += lastErrDesc;
+ }
+ }
+ if (details.empty())
+ {
+ details = "Internal zlib error (code ";
+ details += std::to_string(code);
+ details += ')';
+ }
+
+ what_ = "Unable to ";
+ switch (op)
+ {
+ case Create:
+ what_ += "create";
+ break;
+ case Close:
+ what_ += "close";
+ break;
+ case Write:
+ what_ += "write";
+ break;
+ }
+ what_ += " file ";
+ what_ += fileName;
+ what_ += ": ";
+ what_ += details;
+}
+
+const char* ZlibException::what() const noexcept
+{
+ return what_.c_str();
+}
diff --git a/src/zlib_exception.hpp b/src/zlib_exception.hpp
new file mode 100644
index 0000000..b147301
--- /dev/null
+++ b/src/zlib_exception.hpp
@@ -0,0 +1,58 @@
+/**
+ * @brief zLib exception.
+ *
+ * This file is part of HostLogger project.
+ *
+ * Copyright (c) 2020 YADRO
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <zlib.h>
+
+#include <exception>
+#include <string>
+
+/** @class ZlibException
+ * @brief zLib exception.
+ */
+class ZlibException : public std::exception
+{
+ public:
+ /** @brief File operation types. */
+ enum Operation
+ {
+ Create,
+ Write,
+ Close
+ };
+
+ /** @brief Constructor.
+ *
+ * @param[in] op - type of operation
+ * @param[in] code - zLib status code
+ * @param[in] fd - zLib file descriptor
+ * @param[in] fileName - file name
+ */
+ ZlibException(Operation op, int code, gzFile fd,
+ const std::string& fileName);
+
+ // From std::exception
+ const char* what() const noexcept override;
+
+ private:
+ /** @brief Error description buffer. */
+ std::string what_;
+};