Add error/event log commit dbus method

Create error/event log based on message id (process id)
and metadata values.

Change-Id: Iaf944130c4075ae2d2c192e5ef840451561791bc
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/log_manager.cpp b/log_manager.cpp
index 1cc1ce2..21e99a8 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -1,28 +1,140 @@
-#include <stdio.h>
+#include <fstream>
+#include <iostream>
+#include <cstdio>
+#include <string>
+#include <vector>
 #include <sdbusplus/vtable.hpp>
 #include <systemd/sd-bus.h>
+#include <systemd/sd-journal.h>
 #include "log.hpp"
 
+using namespace phosphor;
+
 /*
  * @fn commit()
- * @brief Create an error/event log based on a message id
- * @param[in] msg - dbus message
- * @param[in] user_data - user data
- * @param[in] error - dbus error
- * @return Commit id
+ * @brief Create an error/event log based on specified id and metadata variable
+ *        names that includes the journal message and the metadata values.
  */
-auto commit(sd_bus_message *msg, void *user_data, sd_bus_error *error)
+auto commit(sd_bus_message *msg, void *userdata, sd_bus_error *error)
 {
-    int rc = 0;
+    // TODO Change /tmp path to a permanent location on flash
+    constexpr const auto path = "/tmp/elog";
+    constexpr const auto msgIdStr = "_PID";
 
-    return rc;
+    // Read PID
+    int rc = -1;
+    char *msgId = nullptr;
+    rc = sd_bus_message_read(msg, "s", &msgId);
+    if (rc < 0)
+    {
+        logging::log<logging::level::ERR>("Failed to read msg id",
+                           logging::entry("DESCRIPTION=%s", strerror(-rc)));
+        return sd_bus_reply_method_return(msg, "i", rc);
+    }
+
+    // Create log file
+    std::string filename{};
+    filename.append(path);
+    // TODO Create error logs in their own separate dir once permanent location
+    // on flash is determined. Ex: ../msgId/1
+    filename.append(msgId);
+    std::ofstream efile;
+    efile.open(filename);
+    efile << "{" << std::endl;
+
+    // Read metadata variables passed as array of strings and store in vector
+    // TODO Read required metadata fields from header file instead
+    rc = sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "s");
+    if (rc < 0)
+    {
+        logging::log<logging::level::ERR>("Failed to read metadata vars",
+                           logging::entry("DESCRIPTION=%s", strerror(-rc)));
+        return sd_bus_reply_method_return(msg, nullptr);
+    }
+    const char* metaVar = nullptr;
+    std::vector<const char*> metaList;
+    while ((rc = sd_bus_message_read_basic(msg, 's', &metaVar)) > 0)
+    {
+        metaList.push_back(metaVar);
+    }
+    sd_bus_message_exit_container(msg);
+
+    sd_journal *j = nullptr;
+    rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+    if (rc < 0)
+    {
+        logging::log<logging::level::ERR>("Failed to open journal",
+                           logging::entry("DESCRIPTION=%s", strerror(-rc)));
+        return sd_bus_reply_method_return(msg, nullptr);
+    }
+
+    // Read the journal from the end to get the most recent entry first.
+    // The result from the sd_journal_get_data() is of the form VARIABLE=value.
+    SD_JOURNAL_FOREACH_BACKWARDS(j)
+    {
+        const char *data = nullptr;
+        size_t length = 0;
+
+        // Search for the msg id
+        rc = sd_journal_get_data(j, msgIdStr, (const void **)&data, &length);
+        if (rc < 0)
+        {
+            // Instance not found, continue to next journal entry
+            continue;
+        }
+        std::string result(data);
+        if (result.find(msgId) == std::string::npos)
+        {
+            // Match not found, continue to next journal entry
+            continue;
+        }
+
+        // Match found, write to file
+        // TODO This is a draft format based on the redfish event logs written
+        // in json, the final openbmc format is to be determined
+        efile << "\t{" << std::endl;
+        efile << "\t\"@" << data << "\"," << std::endl;
+
+        // Include the journal message
+        rc = sd_journal_get_data(j, "MESSAGE", (const void **)&data, &length);
+        if (rc < 0)
+        {
+            continue;
+        }
+        efile << "\t\"@" << data << "\"," << std::endl;
+
+        // Search for the metadata variables in the current journal entry
+        for (auto i : metaList)
+        {
+            rc = sd_journal_get_data(j, i, (const void **)&data, &length);
+            if (rc < 0)
+            {
+                // Not found, continue to next metadata variable
+                logging::log<logging::level::INFO>("Failed to find metadata",
+                                    logging::entry("META_FIELD=%s", i));
+                continue;
+            }
+
+            // Metatdata variable found, write to file
+            efile << "\t\"@" << data << "\"," << std::endl;
+        }
+        efile << "\t}" << std::endl;
+
+        // TODO Break only once all metadata fields have been found. Implement
+        // once this function reads the metadata fields from the header file.
+        break;
+    }
+    sd_journal_close(j);
+
+    efile << "}" << std::endl;
+    efile.close();
+    return sd_bus_reply_method_return(msg, nullptr);
 }
 
 constexpr sdbusplus::vtable::vtable_t log_vtable[] =
 {
     sdbusplus::vtable::start(),
-
-    sdbusplus::vtable::method("Commit", "i", "i", commit),
+    sdbusplus::vtable::method("Commit", "sas", "", commit),
     sdbusplus::vtable::end()
 };