summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meson.build1
-rw-r--r--redfish-core/lib/log_services.hpp78
-rw-r--r--test/redfish-core/lib/log_services_test.cpp69
3 files changed, 131 insertions, 17 deletions
diff --git a/meson.build b/meson.build
index 207d59b025..b79e849b32 100644
--- a/meson.build
+++ b/meson.build
@@ -454,6 +454,7 @@ srcfiles_unittest = files(
'test/redfish-core/lib/chassis_test.cpp',
'test/redfish-core/lib/sensors_test.cpp',
'test/redfish-core/lib/log_services_dump_test.cpp',
+ 'test/redfish-core/lib/log_services_test.cpp',
'test/redfish-core/lib/service_root_test.cpp',
'test/redfish-core/lib/thermal_subsystem_test.cpp',
'test/redfish-core/lib/power_subsystem_test.cpp',
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 5354f0a4c9..e634191151 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -31,6 +31,7 @@
#include "utils/dbus_utils.hpp"
#include "utils/time_utils.hpp"
+#include <systemd/sd-id128.h>
#include <systemd/sd-journal.h>
#include <tinyxml2.h>
#include <unistd.h>
@@ -171,23 +172,27 @@ inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
const bool firstEntry = true)
{
int ret = 0;
+ static sd_id128_t prevBootID{};
static uint64_t prevTs = 0;
static int index = 0;
if (firstEntry)
{
+ prevBootID = {};
prevTs = 0;
}
// Get the entry timestamp
uint64_t curTs = 0;
- ret = sd_journal_get_realtime_usec(journal, &curTs);
+ sd_id128_t curBootID{};
+ ret = sd_journal_get_monotonic_usec(journal, &curTs, &curBootID);
if (ret < 0)
{
BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", strerror(-ret));
return false;
}
- // If the timestamp isn't unique, increment the index
- if (curTs == prevTs)
+ // If the timestamp isn't unique on the same boot, increment the index
+ bool sameBootIDs = sd_id128_equal(curBootID, prevBootID) != 0;
+ if (sameBootIDs && (curTs == prevTs))
{
index++;
}
@@ -196,10 +201,19 @@ inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
// Otherwise, reset it
index = 0;
}
+
+ if (!sameBootIDs)
+ {
+ // Save the bootID
+ prevBootID = curBootID;
+ }
// Save the timestamp
prevTs = curTs;
- entryID = std::to_string(curTs);
+ // make entryID as <bootID>_<timestamp>[_<index>]
+ std::array<char, SD_ID128_STRING_MAX> bootIDStr{};
+ sd_id128_to_string(curBootID, bootIDStr.data());
+ entryID = std::format("{}_{}", bootIDStr.data(), curTs);
if (index > 0)
{
entryID += "_" + std::to_string(index);
@@ -246,25 +260,52 @@ static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
return true;
}
+// Entry is formed like "BootID_timestamp" or "BootID_timestamp_index"
inline static bool
getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::string& entryID, uint64_t& timestamp,
- uint64_t& index)
+ const std::string& entryID, sd_id128_t& bootID,
+ uint64_t& timestamp, uint64_t& index)
{
if (entryID.empty())
{
return false;
}
- // Convert the unique ID back to a timestamp to find the entry
- std::string_view tsStr(entryID);
- auto underscorePos = tsStr.find('_');
- if (underscorePos != std::string_view::npos)
+ // Convert the unique ID back to a bootID + timestamp to find the entry
+ std::string_view entryIDStrView(entryID);
+ auto underscore1Pos = entryIDStrView.find('_');
+ if (underscore1Pos == std::string_view::npos)
+ {
+ // EntryID has no bootID or timestamp
+ messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
+ return false;
+ }
+
+ // EntryID has bootID + timestamp
+
+ // Convert entryIDViewString to BootID
+ // NOTE: bootID string which needs to be null-terminated for
+ // sd_id128_from_string()
+ std::string bootIDStr(entryID, 0, underscore1Pos);
+ if (sd_id128_from_string(bootIDStr.c_str(), &bootID) < 0)
+ {
+ messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
+ return false;
+ }
+
+ // Get the timestamp from entryID
+ std::string_view timestampStrView = entryIDStrView;
+ timestampStrView.remove_prefix(underscore1Pos + 1);
+
+ // Check the index in timestamp
+ auto underscore2Pos = timestampStrView.find('_');
+ if (underscore2Pos != std::string_view::npos)
{
// Timestamp has an index
- tsStr.remove_suffix(tsStr.size() - underscorePos);
- std::string_view indexStr(entryID);
- indexStr.remove_prefix(underscorePos + 1);
+ timestampStrView.remove_suffix(timestampStrView.size() -
+ underscore2Pos);
+ std::string_view indexStr(timestampStrView);
+ indexStr.remove_prefix(underscore2Pos + 1);
auto [ptr, ec] = std::from_chars(indexStr.begin(), indexStr.end(),
index);
if (ec != std::errc())
@@ -273,8 +314,10 @@ inline static bool
return false;
}
}
- // Timestamp has no index
- auto [ptr, ec] = std::from_chars(tsStr.begin(), tsStr.end(), timestamp);
+
+ // Now timestamp has no index
+ auto [ptr, ec] = std::from_chars(timestampStrView.begin(),
+ timestampStrView.end(), timestamp);
if (ec != std::errc())
{
messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
@@ -2665,9 +2708,10 @@ inline void requestRoutesBMCJournalLogEntry(App& app)
return;
}
// Convert the unique ID back to a timestamp to find the entry
+ sd_id128_t bootID{};
uint64_t ts = 0;
uint64_t index = 0;
- if (!getTimestampFromID(asyncResp, entryID, ts, index))
+ if (!getTimestampFromID(asyncResp, entryID, bootID, ts, index))
{
return;
}
@@ -2687,7 +2731,7 @@ inline void requestRoutesBMCJournalLogEntry(App& app)
// index tracking the unique ID
std::string idStr;
bool firstEntry = true;
- ret = sd_journal_seek_realtime_usec(journal.get(), ts);
+ ret = sd_journal_seek_monotonic_usec(journal.get(), bootID, ts);
if (ret < 0)
{
BMCWEB_LOG_ERROR("failed to seek to an entry in journal{}",
diff --git a/test/redfish-core/lib/log_services_test.cpp b/test/redfish-core/lib/log_services_test.cpp
new file mode 100644
index 0000000000..4f7bedb72f
--- /dev/null
+++ b/test/redfish-core/lib/log_services_test.cpp
@@ -0,0 +1,69 @@
+#include "app.hpp"
+#include "async_resp.hpp"
+#include "log_services.hpp"
+
+#include <systemd/sd-id128.h>
+
+#include <nlohmann/json.hpp>
+
+#include <format>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace redfish
+{
+namespace
+{
+
+TEST(LogServicesBMCJouralTest, LogServicesBMCJouralGetReturnsError)
+{
+ auto shareAsyncResp = std::make_shared<bmcweb::AsyncResp>();
+ sd_id128_t bootIDOut{};
+ uint64_t timestampOut = 0;
+ uint64_t indexOut = 0;
+ uint64_t timestampIn = 1740970301UL;
+ std::string badBootIDStr = "78770392794344a29f81507f3ce5e";
+ std::string goodBootIDStr = "78770392794344a29f81507f3ce5e78c";
+ sd_id128_t goodBootID{};
+
+ // invalid test cases
+ EXPECT_FALSE(getTimestampFromID(shareAsyncResp, "", bootIDOut, timestampOut,
+ indexOut));
+ EXPECT_FALSE(getTimestampFromID(shareAsyncResp, badBootIDStr, bootIDOut,
+ timestampOut, indexOut));
+ EXPECT_FALSE(getTimestampFromID(
+ shareAsyncResp, std::format("{}_{}", badBootIDStr, timestampIn),
+ bootIDOut, timestampOut, indexOut));
+ EXPECT_FALSE(getTimestampFromID(
+ shareAsyncResp, std::format("{}_{}", badBootIDStr, timestampIn),
+ bootIDOut, timestampOut, indexOut));
+
+ // obtain a goodBootID
+ EXPECT_GE(sd_id128_from_string(goodBootIDStr.c_str(), &goodBootID), 0);
+
+ EXPECT_FALSE(getTimestampFromID(
+ shareAsyncResp, std::format("{}_{}", goodBootIDStr, "InvalidNum"),
+ bootIDOut, timestampOut, indexOut));
+
+ // Success cases
+ EXPECT_TRUE(getTimestampFromID(
+ shareAsyncResp, std::format("{}_{}", goodBootIDStr, timestampIn),
+ bootIDOut, timestampOut, indexOut));
+ EXPECT_NE(sd_id128_equal(goodBootID, bootIDOut), 0);
+ EXPECT_EQ(timestampIn, timestampOut);
+ EXPECT_EQ(indexOut, 0);
+
+ uint64_t indexIn = 1;
+ EXPECT_TRUE(getTimestampFromID(
+ shareAsyncResp,
+ std::format("{}_{}_{}", goodBootIDStr, timestampIn, indexIn), bootIDOut,
+ timestampOut, indexOut));
+ EXPECT_NE(sd_id128_equal(goodBootID, bootIDOut), 0);
+ EXPECT_EQ(timestampIn, timestampOut);
+ EXPECT_EQ(indexOut, indexIn);
+}
+
+} // namespace
+} // namespace redfish