1b0983db2SEd Tanous #pragma once 2b0983db2SEd Tanous 3b0983db2SEd Tanous #include "app.hpp" 4b0983db2SEd Tanous #include "error_messages.hpp" 5b0983db2SEd Tanous #include "generated/enums/log_entry.hpp" 6b0983db2SEd Tanous #include "query.hpp" 7b0983db2SEd Tanous #include "registries/base_message_registry.hpp" 8b0983db2SEd Tanous #include "registries/privilege_registry.hpp" 9b0983db2SEd Tanous #include "utils/time_utils.hpp" 10b0983db2SEd Tanous 11b0983db2SEd Tanous #include <systemd/sd-journal.h> 12b0983db2SEd Tanous 13b0983db2SEd Tanous #include <boost/beast/http/verb.hpp> 14b0983db2SEd Tanous 15b0983db2SEd Tanous #include <array> 16b0983db2SEd Tanous #include <memory> 17b0983db2SEd Tanous #include <string> 18b0983db2SEd Tanous #include <string_view> 19b0983db2SEd Tanous 20b0983db2SEd Tanous namespace redfish 21b0983db2SEd Tanous { 22b0983db2SEd Tanous 23b0983db2SEd Tanous inline int getJournalMetadata(sd_journal* journal, std::string_view field, 24b0983db2SEd Tanous std::string_view& contents) 25b0983db2SEd Tanous { 26b0983db2SEd Tanous const char* data = nullptr; 27b0983db2SEd Tanous size_t length = 0; 28b0983db2SEd Tanous int ret = 0; 29b0983db2SEd Tanous // Get the metadata from the requested field of the journal entry 30b0983db2SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 31b0983db2SEd Tanous const void** dataVoid = reinterpret_cast<const void**>(&data); 32b0983db2SEd Tanous 33b0983db2SEd Tanous ret = sd_journal_get_data(journal, field.data(), dataVoid, &length); 34b0983db2SEd Tanous if (ret < 0) 35b0983db2SEd Tanous { 36b0983db2SEd Tanous return ret; 37b0983db2SEd Tanous } 38b0983db2SEd Tanous contents = std::string_view(data, length); 39b0983db2SEd Tanous // Only use the content after the "=" character. 40b0983db2SEd Tanous contents.remove_prefix(std::min(contents.find('=') + 1, contents.size())); 41b0983db2SEd Tanous return ret; 42b0983db2SEd Tanous } 43b0983db2SEd Tanous 44b0983db2SEd Tanous inline int getJournalMetadataInt(sd_journal* journal, std::string_view field, 45b0983db2SEd Tanous const int& base, long int& contents) 46b0983db2SEd Tanous { 47b0983db2SEd Tanous int ret = 0; 48b0983db2SEd Tanous std::string_view metadata; 49b0983db2SEd Tanous // Get the metadata from the requested field of the journal entry 50b0983db2SEd Tanous ret = getJournalMetadata(journal, field, metadata); 51b0983db2SEd Tanous if (ret < 0) 52b0983db2SEd Tanous { 53b0983db2SEd Tanous return ret; 54b0983db2SEd Tanous } 55b0983db2SEd Tanous contents = strtol(metadata.data(), nullptr, base); 56b0983db2SEd Tanous return ret; 57b0983db2SEd Tanous } 58b0983db2SEd Tanous 59b0983db2SEd Tanous inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp) 60b0983db2SEd Tanous { 61b0983db2SEd Tanous int ret = 0; 62b0983db2SEd Tanous uint64_t timestamp = 0; 63b0983db2SEd Tanous ret = sd_journal_get_realtime_usec(journal, ×tamp); 64b0983db2SEd Tanous if (ret < 0) 65b0983db2SEd Tanous { 66b0983db2SEd Tanous BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", strerror(-ret)); 67b0983db2SEd Tanous return false; 68b0983db2SEd Tanous } 69b0983db2SEd Tanous entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp); 70b0983db2SEd Tanous return true; 71b0983db2SEd Tanous } 72b0983db2SEd Tanous 73bd79bce8SPatrick Williams inline bool fillBMCJournalLogEntryJson( 74*8274eb11SEd Tanous sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson) 75b0983db2SEd Tanous { 76*8274eb11SEd Tanous char* cursor = nullptr; 77*8274eb11SEd Tanous int ret = sd_journal_get_cursor(journal, &cursor); 78*8274eb11SEd Tanous if (ret < 0) 79*8274eb11SEd Tanous { 80*8274eb11SEd Tanous return false; 81*8274eb11SEd Tanous } 82*8274eb11SEd Tanous std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor); 83*8274eb11SEd Tanous std::string bmcJournalLogEntryID(cursor); 84*8274eb11SEd Tanous 85b0983db2SEd Tanous // Get the Log Entry contents 86b0983db2SEd Tanous std::string message; 87b0983db2SEd Tanous std::string_view syslogID; 88*8274eb11SEd Tanous ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID); 89b0983db2SEd Tanous if (ret < 0) 90b0983db2SEd Tanous { 91b0983db2SEd Tanous BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", 92b0983db2SEd Tanous strerror(-ret)); 93b0983db2SEd Tanous } 94b0983db2SEd Tanous if (!syslogID.empty()) 95b0983db2SEd Tanous { 96b0983db2SEd Tanous message += std::string(syslogID) + ": "; 97b0983db2SEd Tanous } 98b0983db2SEd Tanous 99b0983db2SEd Tanous std::string_view msg; 100b0983db2SEd Tanous ret = getJournalMetadata(journal, "MESSAGE", msg); 101b0983db2SEd Tanous if (ret < 0) 102b0983db2SEd Tanous { 103b0983db2SEd Tanous BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", strerror(-ret)); 104055713e4SEd Tanous return false; 105b0983db2SEd Tanous } 106b0983db2SEd Tanous message += std::string(msg); 107b0983db2SEd Tanous 108b0983db2SEd Tanous // Get the severity from the PRIORITY field 109b0983db2SEd Tanous long int severity = 8; // Default to an invalid priority 110b0983db2SEd Tanous ret = getJournalMetadataInt(journal, "PRIORITY", 10, severity); 111b0983db2SEd Tanous if (ret < 0) 112b0983db2SEd Tanous { 113b0983db2SEd Tanous BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", strerror(-ret)); 114b0983db2SEd Tanous } 115b0983db2SEd Tanous 116b0983db2SEd Tanous // Get the Created time from the timestamp 117b0983db2SEd Tanous std::string entryTimeStr; 118b0983db2SEd Tanous if (!getEntryTimestamp(journal, entryTimeStr)) 119b0983db2SEd Tanous { 120055713e4SEd Tanous return false; 121b0983db2SEd Tanous } 122b0983db2SEd Tanous 123b0983db2SEd Tanous // Fill in the log entry with the gathered data 124b0983db2SEd Tanous bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 125*8274eb11SEd Tanous 126*8274eb11SEd Tanous std::string entryIdBase64 = 127*8274eb11SEd Tanous crow::utility::base64encode(bmcJournalLogEntryID); 128*8274eb11SEd Tanous 129b0983db2SEd Tanous bmcJournalLogEntryJson["@odata.id"] = boost::urls::format( 130b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}", 131*8274eb11SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64); 132b0983db2SEd Tanous bmcJournalLogEntryJson["Name"] = "BMC Journal Entry"; 133*8274eb11SEd Tanous bmcJournalLogEntryJson["Id"] = entryIdBase64; 134b0983db2SEd Tanous bmcJournalLogEntryJson["Message"] = std::move(message); 135539d8c6bSEd Tanous bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem; 136b0983db2SEd Tanous log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK; 137b0983db2SEd Tanous if (severity <= 2) 138b0983db2SEd Tanous { 139b0983db2SEd Tanous severityEnum = log_entry::EventSeverity::Critical; 140b0983db2SEd Tanous } 141b0983db2SEd Tanous else if (severity <= 4) 142b0983db2SEd Tanous { 143b0983db2SEd Tanous severityEnum = log_entry::EventSeverity::Warning; 144b0983db2SEd Tanous } 145b0983db2SEd Tanous 146b0983db2SEd Tanous bmcJournalLogEntryJson["Severity"] = severityEnum; 147b0983db2SEd Tanous bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry"; 148b0983db2SEd Tanous bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr); 149055713e4SEd Tanous return true; 150b0983db2SEd Tanous } 151b0983db2SEd Tanous 15284177a2fSEd Tanous inline void handleManagersLogServiceJournalGet( 15384177a2fSEd Tanous App& app, const crow::Request& req, 154b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 15584177a2fSEd Tanous const std::string& managerId) 15684177a2fSEd Tanous { 157b0983db2SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 158b0983db2SEd Tanous { 159b0983db2SEd Tanous return; 160b0983db2SEd Tanous } 161b0983db2SEd Tanous 162b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 163b0983db2SEd Tanous { 164b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId); 165b0983db2SEd Tanous return; 166b0983db2SEd Tanous } 167b0983db2SEd Tanous 16884177a2fSEd Tanous asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 169b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 170b0983db2SEd Tanous boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", 171b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME); 172b0983db2SEd Tanous asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 173b0983db2SEd Tanous asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 174b0983db2SEd Tanous asyncResp->res.jsonValue["Id"] = "Journal"; 175b0983db2SEd Tanous asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 176b0983db2SEd Tanous 177b0983db2SEd Tanous std::pair<std::string, std::string> redfishDateTimeOffset = 178b0983db2SEd Tanous redfish::time_utils::getDateTimeOffsetNow(); 179b0983db2SEd Tanous asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 180b0983db2SEd Tanous asyncResp->res.jsonValue["DateTimeLocalOffset"] = 181b0983db2SEd Tanous redfishDateTimeOffset.second; 182b0983db2SEd Tanous 183b0983db2SEd Tanous asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( 184b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries", 185b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME); 186b0983db2SEd Tanous } 187b0983db2SEd Tanous 188055713e4SEd Tanous struct JournalReadState 189055713e4SEd Tanous { 190055713e4SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal; 191055713e4SEd Tanous }; 192055713e4SEd Tanous 193bd79bce8SPatrick Williams inline void readJournalEntries( 194bd79bce8SPatrick Williams uint64_t topEntryCount, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 195055713e4SEd Tanous JournalReadState&& readState) 196055713e4SEd Tanous { 197055713e4SEd Tanous nlohmann::json& logEntry = asyncResp->res.jsonValue["Members"]; 198055713e4SEd Tanous nlohmann::json::array_t* logEntryArray = 199055713e4SEd Tanous logEntry.get_ptr<nlohmann::json::array_t*>(); 200055713e4SEd Tanous if (logEntryArray == nullptr) 201055713e4SEd Tanous { 202055713e4SEd Tanous messages::internalError(asyncResp->res); 203055713e4SEd Tanous return; 204055713e4SEd Tanous } 205055713e4SEd Tanous 206055713e4SEd Tanous // The Journal APIs unfortunately do blocking calls to the filesystem, and 207055713e4SEd Tanous // can be somewhat expensive. Short of creating our own io_uring based 208055713e4SEd Tanous // implementation of sd-journal, which would be difficult, the best thing we 209055713e4SEd Tanous // can do is to only parse a certain number of entries at a time. The 210055713e4SEd Tanous // current chunk size is selected arbitrarily to ensure that we're not 211055713e4SEd Tanous // trying to process thousands of entries at the same time. 212055713e4SEd Tanous // The implementation will process the number of entries, then return 213055713e4SEd Tanous // control to the io_context to let other operations continue. 214055713e4SEd Tanous size_t segmentCountRemaining = 10; 215055713e4SEd Tanous 216055713e4SEd Tanous // Reset the unique ID on the first entry 217055713e4SEd Tanous for (uint64_t entryCount = logEntryArray->size(); 218055713e4SEd Tanous entryCount < topEntryCount; entryCount++) 219055713e4SEd Tanous { 220055713e4SEd Tanous if (segmentCountRemaining == 0) 221055713e4SEd Tanous { 222055713e4SEd Tanous boost::asio::post(crow::connections::systemBus->get_io_context(), 223055713e4SEd Tanous [asyncResp, topEntryCount, 224055713e4SEd Tanous readState = std::move(readState)]() mutable { 225055713e4SEd Tanous readJournalEntries(topEntryCount, asyncResp, 226055713e4SEd Tanous std::move(readState)); 227055713e4SEd Tanous }); 228055713e4SEd Tanous return; 229055713e4SEd Tanous } 230055713e4SEd Tanous 231055713e4SEd Tanous nlohmann::json::object_t bmcJournalLogEntry; 232*8274eb11SEd Tanous if (!fillBMCJournalLogEntryJson(readState.journal.get(), 233055713e4SEd Tanous bmcJournalLogEntry)) 234055713e4SEd Tanous { 235055713e4SEd Tanous messages::internalError(asyncResp->res); 236055713e4SEd Tanous return; 237055713e4SEd Tanous } 238055713e4SEd Tanous logEntryArray->emplace_back(std::move(bmcJournalLogEntry)); 239055713e4SEd Tanous 240*8274eb11SEd Tanous int ret = sd_journal_next(readState.journal.get()); 241055713e4SEd Tanous if (ret < 0) 242055713e4SEd Tanous { 243055713e4SEd Tanous messages::internalError(asyncResp->res); 244055713e4SEd Tanous return; 245055713e4SEd Tanous } 246055713e4SEd Tanous if (ret == 0) 247055713e4SEd Tanous { 248055713e4SEd Tanous break; 249055713e4SEd Tanous } 250055713e4SEd Tanous segmentCountRemaining--; 251055713e4SEd Tanous } 252055713e4SEd Tanous } 253055713e4SEd Tanous 25484177a2fSEd Tanous inline void handleManagersJournalLogEntryCollectionGet( 25584177a2fSEd Tanous App& app, const crow::Request& req, 256b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 25784177a2fSEd Tanous const std::string& managerId) 25884177a2fSEd Tanous { 259b0983db2SEd Tanous query_param::QueryCapabilities capabilities = { 260b0983db2SEd Tanous .canDelegateTop = true, 261b0983db2SEd Tanous .canDelegateSkip = true, 262b0983db2SEd Tanous }; 263b0983db2SEd Tanous query_param::Query delegatedQuery; 26484177a2fSEd Tanous if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 26584177a2fSEd Tanous delegatedQuery, capabilities)) 266b0983db2SEd Tanous { 267b0983db2SEd Tanous return; 268b0983db2SEd Tanous } 269b0983db2SEd Tanous 270b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 271b0983db2SEd Tanous { 272b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId); 273b0983db2SEd Tanous return; 274b0983db2SEd Tanous } 275b0983db2SEd Tanous 276b0983db2SEd Tanous size_t skip = delegatedQuery.skip.value_or(0); 277b0983db2SEd Tanous size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 278b0983db2SEd Tanous 279b0983db2SEd Tanous // Collections don't include the static data added by SubRoute 280b0983db2SEd Tanous // because it has a duplicate entry for members 281b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.type"] = 282b0983db2SEd Tanous "#LogEntryCollection.LogEntryCollection"; 283b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 284b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries", 285b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME); 286b0983db2SEd Tanous asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 287b0983db2SEd Tanous asyncResp->res.jsonValue["Description"] = 288b0983db2SEd Tanous "Collection of BMC Journal Entries"; 289055713e4SEd Tanous asyncResp->res.jsonValue["Members"] = nlohmann::json::array_t(); 290b0983db2SEd Tanous 291b0983db2SEd Tanous // Go through the journal and use the timestamp to create a 292b0983db2SEd Tanous // unique ID for each entry 293b0983db2SEd Tanous sd_journal* journalTmp = nullptr; 294b0983db2SEd Tanous int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 295b0983db2SEd Tanous if (ret < 0) 296b0983db2SEd Tanous { 297b0983db2SEd Tanous BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret)); 298b0983db2SEd Tanous messages::internalError(asyncResp->res); 299b0983db2SEd Tanous return; 300b0983db2SEd Tanous } 301055713e4SEd Tanous 302b0983db2SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 303b0983db2SEd Tanous journalTmp, sd_journal_close); 304b0983db2SEd Tanous journalTmp = nullptr; 305b0983db2SEd Tanous 306055713e4SEd Tanous // Seek to the end 307055713e4SEd Tanous if (sd_journal_seek_tail(journal.get()) < 0) 308b0983db2SEd Tanous { 309b0983db2SEd Tanous messages::internalError(asyncResp->res); 310b0983db2SEd Tanous return; 311b0983db2SEd Tanous } 312055713e4SEd Tanous 313055713e4SEd Tanous // Get the last entry 314055713e4SEd Tanous if (sd_journal_previous(journal.get()) < 0) 315055713e4SEd Tanous { 316055713e4SEd Tanous messages::internalError(asyncResp->res); 317055713e4SEd Tanous return; 318b0983db2SEd Tanous } 319055713e4SEd Tanous 320055713e4SEd Tanous // Get the last sequence number 321055713e4SEd Tanous uint64_t endSeqNum = 0; 322058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254 323055713e4SEd Tanous { 324055713e4SEd Tanous if (sd_journal_get_seqnum(journal.get(), &endSeqNum, nullptr) < 0) 325055713e4SEd Tanous { 326055713e4SEd Tanous messages::internalError(asyncResp->res); 327055713e4SEd Tanous return; 328055713e4SEd Tanous } 329055713e4SEd Tanous } 330055713e4SEd Tanous #endif 331055713e4SEd Tanous 332055713e4SEd Tanous // Seek to the beginning 333055713e4SEd Tanous if (sd_journal_seek_head(journal.get()) < 0) 334055713e4SEd Tanous { 335055713e4SEd Tanous messages::internalError(asyncResp->res); 336055713e4SEd Tanous return; 337055713e4SEd Tanous } 338055713e4SEd Tanous 339055713e4SEd Tanous // Get the first entry 340055713e4SEd Tanous if (sd_journal_next(journal.get()) < 0) 341055713e4SEd Tanous { 342055713e4SEd Tanous messages::internalError(asyncResp->res); 343055713e4SEd Tanous return; 344055713e4SEd Tanous } 345055713e4SEd Tanous 346055713e4SEd Tanous // Get the first sequence number 347055713e4SEd Tanous uint64_t startSeqNum = 0; 348058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254 349055713e4SEd Tanous { 350055713e4SEd Tanous if (sd_journal_get_seqnum(journal.get(), &startSeqNum, nullptr) < 0) 351055713e4SEd Tanous { 352055713e4SEd Tanous messages::internalError(asyncResp->res); 353055713e4SEd Tanous return; 354055713e4SEd Tanous } 355055713e4SEd Tanous } 356055713e4SEd Tanous #endif 357055713e4SEd Tanous 358055713e4SEd Tanous BMCWEB_LOG_DEBUG("journal Sequence IDs start:{} end:{}", startSeqNum, 359055713e4SEd Tanous endSeqNum); 360055713e4SEd Tanous 361055713e4SEd Tanous // Add 1 to account for the last entry 362055713e4SEd Tanous uint64_t totalEntries = endSeqNum - startSeqNum + 1; 363055713e4SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = totalEntries; 364055713e4SEd Tanous if (skip + top < totalEntries) 365b0983db2SEd Tanous { 36684177a2fSEd Tanous asyncResp->res.jsonValue["Members@odata.nextLink"] = 36784177a2fSEd Tanous boost::urls::format( 368b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries?$skip={}", 369b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, std::to_string(skip + top)); 370b0983db2SEd Tanous } 371055713e4SEd Tanous uint64_t index = 0; 372055713e4SEd Tanous if (skip > 0) 373055713e4SEd Tanous { 374055713e4SEd Tanous if (sd_journal_next_skip(journal.get(), skip) < 0) 375055713e4SEd Tanous { 376055713e4SEd Tanous messages::internalError(asyncResp->res); 377055713e4SEd Tanous return; 378055713e4SEd Tanous } 379055713e4SEd Tanous } 380055713e4SEd Tanous BMCWEB_LOG_DEBUG("Index was {}", index); 381*8274eb11SEd Tanous readJournalEntries(top, asyncResp, {std::move(journal)}); 382b0983db2SEd Tanous } 383b0983db2SEd Tanous 38484177a2fSEd Tanous inline void handleManagersJournalEntriesLogEntryGet( 38584177a2fSEd Tanous App& app, const crow::Request& req, 386b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 38784177a2fSEd Tanous const std::string& managerId, const std::string& entryID) 38884177a2fSEd Tanous { 389b0983db2SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 390b0983db2SEd Tanous { 391b0983db2SEd Tanous return; 392b0983db2SEd Tanous } 393b0983db2SEd Tanous 394b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 395b0983db2SEd Tanous { 396b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId); 397b0983db2SEd Tanous return; 398b0983db2SEd Tanous } 399b0983db2SEd Tanous 400b0983db2SEd Tanous sd_journal* journalTmp = nullptr; 401b0983db2SEd Tanous int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 402b0983db2SEd Tanous if (ret < 0) 403b0983db2SEd Tanous { 404b0983db2SEd Tanous BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret)); 405b0983db2SEd Tanous messages::internalError(asyncResp->res); 406b0983db2SEd Tanous return; 407b0983db2SEd Tanous } 408b0983db2SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 409b0983db2SEd Tanous journalTmp, sd_journal_close); 410b0983db2SEd Tanous journalTmp = nullptr; 411*8274eb11SEd Tanous 412*8274eb11SEd Tanous std::string cursor; 413*8274eb11SEd Tanous if (!crow::utility::base64Decode(entryID, cursor)) 414*8274eb11SEd Tanous { 415*8274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 416*8274eb11SEd Tanous return; 417*8274eb11SEd Tanous } 418*8274eb11SEd Tanous 419*8274eb11SEd Tanous // Go to the cursor in the log 420*8274eb11SEd Tanous ret = sd_journal_seek_cursor(journal.get(), cursor.c_str()); 421b0983db2SEd Tanous if (ret < 0) 422b0983db2SEd Tanous { 423*8274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 424*8274eb11SEd Tanous return; 425*8274eb11SEd Tanous } 426*8274eb11SEd Tanous 427*8274eb11SEd Tanous if (sd_journal_next(journal.get()) < 0) 428*8274eb11SEd Tanous { 429b0983db2SEd Tanous messages::internalError(asyncResp->res); 430b0983db2SEd Tanous return; 431b0983db2SEd Tanous } 432055713e4SEd Tanous 433*8274eb11SEd Tanous ret = sd_journal_test_cursor(journal.get(), cursor.c_str()); 434*8274eb11SEd Tanous if (ret == 0) 435*8274eb11SEd Tanous { 436*8274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 437*8274eb11SEd Tanous return; 438*8274eb11SEd Tanous } 439*8274eb11SEd Tanous if (ret < 0) 440b0983db2SEd Tanous { 441b0983db2SEd Tanous messages::internalError(asyncResp->res); 442b0983db2SEd Tanous return; 443b0983db2SEd Tanous } 444055713e4SEd Tanous 445b0983db2SEd Tanous nlohmann::json::object_t bmcJournalLogEntry; 446*8274eb11SEd Tanous if (!fillBMCJournalLogEntryJson(journal.get(), bmcJournalLogEntry)) 447b0983db2SEd Tanous { 448b0983db2SEd Tanous messages::internalError(asyncResp->res); 449b0983db2SEd Tanous return; 450b0983db2SEd Tanous } 451b0983db2SEd Tanous asyncResp->res.jsonValue.update(bmcJournalLogEntry); 452055713e4SEd Tanous } 45384177a2fSEd Tanous 45484177a2fSEd Tanous inline void requestRoutesBMCJournalLogService(App& app) 45584177a2fSEd Tanous { 45684177a2fSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/") 45784177a2fSEd Tanous .privileges(redfish::privileges::getLogService) 45884177a2fSEd Tanous .methods(boost::beast::http::verb::get)( 45984177a2fSEd Tanous std::bind_front(handleManagersLogServiceJournalGet, std::ref(app))); 46084177a2fSEd Tanous 46184177a2fSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/") 46284177a2fSEd Tanous .privileges(redfish::privileges::getLogEntryCollection) 46384177a2fSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 46484177a2fSEd Tanous handleManagersJournalLogEntryCollectionGet, std::ref(app))); 46584177a2fSEd Tanous 46684177a2fSEd Tanous BMCWEB_ROUTE( 46784177a2fSEd Tanous app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/<str>/") 46884177a2fSEd Tanous .privileges(redfish::privileges::getLogEntry) 46984177a2fSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 47084177a2fSEd Tanous handleManagersJournalEntriesLogEntryGet, std::ref(app))); 471b0983db2SEd Tanous } 472b0983db2SEd Tanous } // namespace redfish 473