Initial commit for the Dump core file monitor infrastructure.
Add an inotify watch to the known core dump location.
Resolves openbmc/openbmc#1504
Change-Id: I0093c9f601d82917ca2efb53a4d47ed98f0eaa7f
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 6ff8965..30d5a88 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,35 +2,54 @@
# Build these headers, don't install them
noinst_HEADERS = \
- dump_entry.hpp
+ dump_entry.hpp \
+ dump_watch.hpp
nobase_nodist_include_HEADERS = \
+ xyz/openbmc_project/Dump/Monitor/error.hpp \
xyz/openbmc_project/Dump/Internal/Create/server.hpp
sbin_PROGRAMS = \
- phosphor-dump-manager
+ phosphor-dump-manager \
+ phosphor-dump-monitor
phosphor_dump_manager_SOURCES = \
dump_manager_main.cpp \
dump_entry.cpp \
xyz/openbmc_project/Dump/Internal/Create/server.cpp
+phosphor_dump_monitor_SOURCES = \
+ dump_watch_main.cpp \
+ dump_watch.cpp \
+ xyz/openbmc_project/Dump/Monitor/error.cpp
+
phosphor_dump_manager_CXXFLAGS = \
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
- $(SDBUSPLUS_CFLAGS)
+ $(SDBUSPLUS_CFLAGS) \
+ $(PHOSPHOR_LOGGING_CFLAGS)
+
+phosphor_dump_monitor_CXXFLAGS = \
+ $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
+ $(PHOSPHOR_LOGGING_CFLAGS)
phosphor_dump_manager_LDADD = \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
- $(SDBUSPLUS_LIBS)
+ $(SDBUSPLUS_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS)
+
+phosphor_dump_monitor_LDADD = \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS) \
+ -lstdc++fs
# Be sure to build needed files before compiling
BUILT_SOURCES = \
- xyz/openbmc_project/Dump/Internal/Create/server.cpp \
- xyz/openbmc_project/Dump/Internal/Create/server.hpp
+ xyz/openbmc_project/Dump/Internal/Create/server.cpp \
+ xyz/openbmc_project/Dump/Internal/Create/server.hpp \
+ xyz/openbmc_project/Dump/Monitor/error.cpp \
+ xyz/openbmc_project/Dump/Monitor/error.hpp
-CLEANFILES = \
- xyz/openbmc_project/Dump/Internal/Create/server.cpp \
- xyz/openbmc_project/Dump/Internal/Create/server.hpp
+CLEANFILES=${BUILT_SOURCES}
xyz/openbmc_project/Dump/Internal/Create/server.cpp: \
xyz/openbmc_project/Dump/Internal/Create.interface.yaml \
@@ -44,3 +63,12 @@
@mkdir -p `dirname $@`
$(SDBUSPLUSPLUS) -r $(srcdir) interface server-header \
xyz.openbmc_project.Dump.Internal.Create > $@
+
+xyz/openbmc_project/Dump/Monitor/error.hpp: \
+${top_srcdir}/xyz/openbmc_project/Dump/Monitor.errors.yaml
+ @mkdir -p `dirname $@`
+ $(SDBUSPLUSPLUS) -r $(srcdir) error exception-header xyz.openbmc_project.Dump.Monitor > $@
+
+xyz/openbmc_project/Dump/Monitor/error.cpp: ${top_srcdir}/xyz/openbmc_project/Dump/Monitor.errors.yaml
+ @mkdir -p `dirname $@`
+ $(SDBUSPLUSPLUS) -r $(srcdir) error exception-cpp xyz.openbmc_project.Dump.Monitor > $@
diff --git a/configure.ac b/configure.ac
index 9cf8ba2..fb1f382 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,10 +13,19 @@
AC_CHECK_PROG([DIRNAME], dirname, dirname)
# Check for libraries
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221],,\
+ AC_MSG_ERROR(["systemd required and not found."]))
PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],,\
AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."]))
PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,\
AC_MSG_ERROR(["Requires sdbusplus package."]))
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],,\
+ AC_MSG_ERROR(["Requires phosphor-logging package."]))
+
+# Check for sdbus++
+AC_PATH_PROG([SDBUSPLUSPLUS], [sdbus++])
+AS_IF([test "x$SDBUSPLUSPLUS" == "x"],
+ AC_MSG_ERROR(["Requires sdbus++"]))
# Check for sdbus++
AC_PATH_PROG([SDBUSPLUSPLUS], [sdbus++])
@@ -38,5 +47,9 @@
AS_IF([test "x$DUMP_OBJPATH" == "x"], [DUMP_OBJPATH="/xyz/openbmc_project/dump"])
AC_DEFINE_UNQUOTED([DUMP_OBJPATH], ["$DUMP_OBJPATH"], [The dump manager Dbus root])
+AC_ARG_VAR(CORE_FILE_DIR, [Directory where core dumps are placed])
+AS_IF([test "x$CORE_FILE_DIR" == "x"], [CORE_FILE_DIR="/var/lib/systemd/coredump"])
+AC_DEFINE_UNQUOTED([CORE_FILE_DIR], ["$CORE_FILE_DIR"], [Directory where core dumps are placed])
+
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/dump_watch.cpp b/dump_watch.cpp
new file mode 100644
index 0000000..6208466
--- /dev/null
+++ b/dump_watch.cpp
@@ -0,0 +1,159 @@
+#include <stdexcept>
+#include <cstddef>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include "config.h"
+#include "dump_watch.hpp"
+#include <experimental/filesystem>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include "elog-errors.hpp"
+#include <xyz/openbmc_project/Dump/Monitor/error.hpp>
+#include "xyz/openbmc_project/Common/error.hpp"
+
+namespace phosphor
+{
+namespace dump
+{
+
+CustomFd::~CustomFd()
+{
+ if (fd >= 0)
+ {
+ close(fd);
+ }
+}
+
+namespace inotify
+{
+
+using namespace std::string_literals;
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Dump::Monitor::Error;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+namespace fs = std::experimental::filesystem;
+
+Watch::~Watch()
+{
+ if ((fd() >= 0) && (wd >= 0))
+ {
+ inotify_rm_watch(fd(), wd);
+ }
+}
+
+Watch::Watch(sd_event* loop):
+ fd(inotifyInit())
+{
+ // Check if CORE DIR exists.
+ fs::path coreDirPath(CORE_FILE_DIR);
+ if (!fs::is_directory(coreDirPath))
+ {
+ namespace metadata = xyz::openbmc_project::Dump::Monitor;
+ elog<InvalidCorePath>(metadata::InvalidCorePath::PATH(CORE_FILE_DIR));
+ }
+
+ //Check for existing coredumps, Dump manager should handle this before
+ //starting the core monitoring.
+ //This is to handle coredumps created prior to Dump applications start,
+ //or missing coredump reporting due to Dump application crashs.
+ if (!fs::is_empty(coreDirPath))
+ {
+ //TODO openbmc/openbmc#1510 Enable Dump collection function here.
+ }
+
+ wd = inotify_add_watch(fd(), CORE_FILE_DIR, IN_CLOSE_WRITE);
+ if (-1 == wd)
+ {
+ auto error = errno;
+ log<level::ERR>("Error occurred during the inotify_add_watch call",
+ entry("ERRNO=%s", strerror(error)));
+ elog<InternalFailure>();
+ }
+
+ auto rc = sd_event_add_io(loop,
+ nullptr,
+ fd(),
+ EPOLLIN,
+ callback,
+ this);
+ if (0 > rc)
+ {
+ // Failed to add to event loop
+ auto error = errno;
+ log<level::ERR>("Error occurred during the sd_event_add_io call",
+ entry("ERRNO=%s", strerror(error)));
+ elog<InternalFailure>();
+ }
+}
+
+int Watch::inotifyInit()
+{
+ auto fd = inotify_init1(IN_NONBLOCK);
+
+ if (-1 == fd)
+ {
+ auto error = errno;
+ log<level::ERR>("Error occurred during the inotify_init1",
+ entry("ERRNO=%s", strerror(error)));
+ elog<InternalFailure>();
+ }
+
+ return fd;
+}
+
+int Watch::callback(sd_event_source* s,
+ int fd,
+ uint32_t revents,
+ void* userdata)
+{
+ if (!(revents & EPOLLIN))
+ {
+ return 0;
+ }
+
+ //Maximum inotify events supported in the buffer
+ constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1;
+ uint8_t buffer[maxBytes];
+
+ auto bytes = read(fd, buffer, maxBytes);
+ if (0 > bytes)
+ {
+ //Failed to read inotify event
+ //Report error and return
+ auto error = errno;
+ log<level::ERR>("Error occurred during the read",
+ entry("ERRNO=%s", strerror(error)));
+ report<InternalFailure>();
+ return 0;
+ }
+
+ auto offset = 0;
+
+ std::vector<fs::path> corePaths;
+
+ while (offset < bytes)
+ {
+ auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
+ if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR))
+ {
+ corePaths.emplace_back(fs::path(CORE_FILE_DIR) / event->name);
+ }
+
+ offset += offsetof(inotify_event, name) + event->len;
+ }
+
+ // Generate new BMC Dump( Core dump Type) incase valid cores
+ if (!corePaths.empty())
+ {
+ //TODO openbmc/openbmc#1510 Enable Dump collection function here
+ }
+ return 0;
+}
+
+} // namespace inotify
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_watch.hpp b/dump_watch.hpp
new file mode 100644
index 0000000..3f7bceb
--- /dev/null
+++ b/dump_watch.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <systemd/sd-event.h>
+#include <unistd.h>
+
+namespace phosphor
+{
+namespace dump
+{
+
+/** @struct CustomFd
+ *
+ * RAII wrapper for file descriptor.
+ */
+struct CustomFd
+{
+ private:
+ /** @brief File descriptor */
+ int fd = -1;
+
+ public:
+ CustomFd(const CustomFd&) = delete;
+ CustomFd& operator=(const CustomFd&) = delete;
+ CustomFd(CustomFd&&) = delete;
+ CustomFd& operator=(CustomFd&&) = delete;
+
+ /** @brief Saves File descriptor and uses it to do file operation
+ *
+ * @param[in] fd - File descriptor
+ */
+ CustomFd(int fd) : fd(fd) {}
+
+ ~CustomFd();
+
+ int operator()() const
+ {
+ return fd;
+ }
+};
+
+namespace inotify
+{
+/** @class Watch
+ *
+ * @brief Adds inotify watch on core file directories.
+ *
+ * The inotify watch is hooked up with sd-event, so that on call back,
+ * appropriate actions are taken to collect the core files.
+ */
+class Watch
+{
+ public:
+ /** @brief ctor - hook inotify watch with sd-event
+ *
+ * @param[in] loop - sd-event object
+ */
+ Watch(sd_event* loop);
+
+ Watch(const Watch&) = delete;
+ Watch& operator=(const Watch&) = delete;
+ Watch(Watch&&) = default;
+ Watch& operator=(Watch&&) = default;
+
+ /* @brief dtor - remove inotify watch and close fd's */
+ ~Watch();
+
+ private:
+ /** @brief sd-event callback
+ *
+ * @param[in] s - event source, floating (unused) in our case
+ * @param[in] fd - inotify fd
+ * @param[in] revents - events that matched for fd
+ * @param[in] userdata - pointer to Watch object
+ * @returns 0 on success, -1 on fail
+ */
+ static int callback(sd_event_source* s,
+ int fd,
+ uint32_t revents,
+ void* userdata);
+
+ /** initialize an inotify instance and returns file descriptor */
+ int inotifyInit();
+
+ /** @brief core file directory watch descriptor */
+ int wd = -1;
+
+ /** @brief file descriptor manager */
+ CustomFd fd;
+};
+
+} // namespace inotify
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_watch_main.cpp b/dump_watch_main.cpp
new file mode 100644
index 0000000..6c7d98b
--- /dev/null
+++ b/dump_watch_main.cpp
@@ -0,0 +1,43 @@
+#include <exception>
+#include "dump_watch.hpp"
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include "elog-errors.hpp"
+#include <xyz/openbmc_project/Dump/Monitor/error.hpp>
+#include "xyz/openbmc_project/Common/error.hpp"
+
+int main(int argc, char* argv[])
+{
+ sd_event* loop = nullptr;
+ sd_event_default(&loop);
+
+ using namespace phosphor::logging;
+
+ using InvalidCorePath =
+ sdbusplus::xyz::openbmc_project::Dump::Monitor::Error::InvalidCorePath;
+ using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+ try
+ {
+ phosphor::dump::inotify::Watch watch(loop);
+ sd_event_loop(loop);
+ }
+
+ catch (InvalidCorePath& e)
+ {
+ commit<InvalidCorePath>();
+ return -1;
+ }
+
+ catch (InternalFailure& e)
+ {
+ commit<InternalFailure>();
+ return -1;
+ }
+
+ sd_event_unref(loop);
+
+ return 0;
+}
diff --git a/elog-errors.hpp b/elog-errors.hpp
new file mode 100644
index 0000000..f5c6466
--- /dev/null
+++ b/elog-errors.hpp
@@ -0,0 +1,89 @@
+// This file was autogenerated. Do not edit!
+// See elog-gen.py for more details
+#pragma once
+
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <sdbusplus/exception.hpp>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+
+namespace sdbusplus
+{
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Dump
+{
+namespace Monitor
+{
+namespace Error
+{
+ struct InvalidCorePath;
+} // namespace Error
+} // namespace Monitor
+} // namespace Dump
+} // namespace openbmc_project
+} // namespace xyz
+} // namespace sdbusplus
+
+
+namespace phosphor
+{
+
+namespace logging
+{
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Dump
+{
+namespace Monitor
+{
+namespace _InvalidCorePath
+{
+
+struct PATH
+{
+ static constexpr auto str = "PATH=%s";
+ static constexpr auto str_short = "PATH";
+ using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
+ explicit constexpr PATH(const char* a) : _entry(entry(str, a)) {};
+ type _entry;
+};
+
+} // namespace _InvalidCorePath
+
+struct InvalidCorePath
+{
+ static constexpr auto L = level::ERR;
+ using PATH = _InvalidCorePath::PATH;
+ using metadata_types = std::tuple<PATH>;
+
+};
+
+} // namespace Monitor
+} // namespace Dump
+} // namespace openbmc_project
+} // namespace xyz
+
+
+namespace details
+{
+
+template <>
+struct map_exception_type<sdbusplus::xyz::openbmc_project::Dump::Monitor::Error::InvalidCorePath>
+{
+ using type = xyz::openbmc_project::Dump::Monitor::InvalidCorePath;
+};
+
+}
+
+
+} // namespace logging
+
+} // namespace phosphor
diff --git a/xyz/openbmc_project/Dump/Monitor.errors.yaml b/xyz/openbmc_project/Dump/Monitor.errors.yaml
new file mode 100644
index 0000000..7a2478c
--- /dev/null
+++ b/xyz/openbmc_project/Dump/Monitor.errors.yaml
@@ -0,0 +1,2 @@
+- name: InvalidCorePath
+ description: Invalid core directory path
diff --git a/xyz/openbmc_project/Dump/Monitor.metadata.yaml b/xyz/openbmc_project/Dump/Monitor.metadata.yaml
new file mode 100644
index 0000000..045a4ea
--- /dev/null
+++ b/xyz/openbmc_project/Dump/Monitor.metadata.yaml
@@ -0,0 +1,5 @@
+- name: InvalidCorePath
+ level: ERR
+ meta:
+ - str: "PATH=%s"
+ type: string