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 23*7da633f0SEd Tanous inline int getJournalMetadata(sd_journal* journal, const char* 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 33*7da633f0SEd Tanous ret = sd_journal_get_data(journal, field, 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 44*7da633f0SEd Tanous inline int getJournalMetadataInt(sd_journal* journal, const char* field, 45*7da633f0SEd Tanous const int base, long int& contents) 46b0983db2SEd Tanous { 47b0983db2SEd Tanous std::string_view metadata; 48b0983db2SEd Tanous // Get the metadata from the requested field of the journal entry 49*7da633f0SEd Tanous int ret = getJournalMetadata(journal, field, metadata); 50b0983db2SEd Tanous if (ret < 0) 51b0983db2SEd Tanous { 52b0983db2SEd Tanous return ret; 53b0983db2SEd Tanous } 54*7da633f0SEd Tanous std::from_chars_result res = 55*7da633f0SEd Tanous std::from_chars(&*metadata.begin(), &*metadata.end(), contents, base); 56*7da633f0SEd Tanous if (res.ec != std::error_code{} || res.ptr != &*metadata.end()) 57*7da633f0SEd Tanous { 58*7da633f0SEd Tanous return -1; 59*7da633f0SEd Tanous } 60*7da633f0SEd Tanous return 0; 61b0983db2SEd Tanous } 62b0983db2SEd Tanous 63b0983db2SEd Tanous inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp) 64b0983db2SEd Tanous { 65b0983db2SEd Tanous int ret = 0; 66b0983db2SEd Tanous uint64_t timestamp = 0; 67b0983db2SEd Tanous ret = sd_journal_get_realtime_usec(journal, ×tamp); 68b0983db2SEd Tanous if (ret < 0) 69b0983db2SEd Tanous { 70*7da633f0SEd Tanous BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", ret); 71b0983db2SEd Tanous return false; 72b0983db2SEd Tanous } 73b0983db2SEd Tanous entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp); 74b0983db2SEd Tanous return true; 75b0983db2SEd Tanous } 76b0983db2SEd Tanous 77bd79bce8SPatrick Williams inline bool fillBMCJournalLogEntryJson( 788274eb11SEd Tanous sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson) 79b0983db2SEd Tanous { 808274eb11SEd Tanous char* cursor = nullptr; 818274eb11SEd Tanous int ret = sd_journal_get_cursor(journal, &cursor); 828274eb11SEd Tanous if (ret < 0) 838274eb11SEd Tanous { 848274eb11SEd Tanous return false; 858274eb11SEd Tanous } 868274eb11SEd Tanous std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor); 878274eb11SEd Tanous std::string bmcJournalLogEntryID(cursor); 888274eb11SEd Tanous 89b0983db2SEd Tanous // Get the Log Entry contents 90b0983db2SEd Tanous std::string message; 91b0983db2SEd Tanous std::string_view syslogID; 928274eb11SEd Tanous ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID); 93b0983db2SEd Tanous if (ret < 0) 94b0983db2SEd Tanous { 95*7da633f0SEd Tanous BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", ret); 96b0983db2SEd Tanous } 97b0983db2SEd Tanous if (!syslogID.empty()) 98b0983db2SEd Tanous { 99b0983db2SEd Tanous message += std::string(syslogID) + ": "; 100b0983db2SEd Tanous } 101b0983db2SEd Tanous 102b0983db2SEd Tanous std::string_view msg; 103b0983db2SEd Tanous ret = getJournalMetadata(journal, "MESSAGE", msg); 104b0983db2SEd Tanous if (ret < 0) 105b0983db2SEd Tanous { 106*7da633f0SEd Tanous BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", ret); 107055713e4SEd Tanous return false; 108b0983db2SEd Tanous } 109b0983db2SEd Tanous message += std::string(msg); 110b0983db2SEd Tanous 111b0983db2SEd Tanous // Get the severity from the PRIORITY field 112b0983db2SEd Tanous long int severity = 8; // Default to an invalid priority 113b0983db2SEd Tanous ret = getJournalMetadataInt(journal, "PRIORITY", 10, severity); 114b0983db2SEd Tanous if (ret < 0) 115b0983db2SEd Tanous { 116*7da633f0SEd Tanous BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", ret); 117b0983db2SEd Tanous } 118b0983db2SEd Tanous 119b0983db2SEd Tanous // Get the Created time from the timestamp 120b0983db2SEd Tanous std::string entryTimeStr; 121b0983db2SEd Tanous if (!getEntryTimestamp(journal, entryTimeStr)) 122b0983db2SEd Tanous { 123055713e4SEd Tanous return false; 124b0983db2SEd Tanous } 125b0983db2SEd Tanous 126b0983db2SEd Tanous // Fill in the log entry with the gathered data 127b0983db2SEd Tanous bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 1288274eb11SEd Tanous 1298274eb11SEd Tanous std::string entryIdBase64 = 1308274eb11SEd Tanous crow::utility::base64encode(bmcJournalLogEntryID); 1318274eb11SEd Tanous 132b0983db2SEd Tanous bmcJournalLogEntryJson["@odata.id"] = boost::urls::format( 133b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}", 1348274eb11SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64); 135b0983db2SEd Tanous bmcJournalLogEntryJson["Name"] = "BMC Journal Entry"; 1368274eb11SEd Tanous bmcJournalLogEntryJson["Id"] = entryIdBase64; 137b0983db2SEd Tanous bmcJournalLogEntryJson["Message"] = std::move(message); 138539d8c6bSEd Tanous bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem; 139b0983db2SEd Tanous log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK; 140b0983db2SEd Tanous if (severity <= 2) 141b0983db2SEd Tanous { 142b0983db2SEd Tanous severityEnum = log_entry::EventSeverity::Critical; 143b0983db2SEd Tanous } 144b0983db2SEd Tanous else if (severity <= 4) 145b0983db2SEd Tanous { 146b0983db2SEd Tanous severityEnum = log_entry::EventSeverity::Warning; 147b0983db2SEd Tanous } 148b0983db2SEd Tanous 149b0983db2SEd Tanous bmcJournalLogEntryJson["Severity"] = severityEnum; 150b0983db2SEd Tanous bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry"; 151b0983db2SEd Tanous bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr); 152055713e4SEd Tanous return true; 153b0983db2SEd Tanous } 154b0983db2SEd Tanous 15584177a2fSEd Tanous inline void handleManagersLogServiceJournalGet( 15684177a2fSEd Tanous App& app, const crow::Request& req, 157b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 15884177a2fSEd Tanous const std::string& managerId) 15984177a2fSEd Tanous { 160b0983db2SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 161b0983db2SEd Tanous { 162b0983db2SEd Tanous return; 163b0983db2SEd Tanous } 164b0983db2SEd Tanous 165b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 166b0983db2SEd Tanous { 167b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId); 168b0983db2SEd Tanous return; 169b0983db2SEd Tanous } 170b0983db2SEd Tanous 17184177a2fSEd Tanous asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 172b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 173b0983db2SEd Tanous boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", 174b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME); 175b0983db2SEd Tanous asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 176b0983db2SEd Tanous asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 177b0983db2SEd Tanous asyncResp->res.jsonValue["Id"] = "Journal"; 178b0983db2SEd Tanous asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 179b0983db2SEd Tanous 180b0983db2SEd Tanous std::pair<std::string, std::string> redfishDateTimeOffset = 181b0983db2SEd Tanous redfish::time_utils::getDateTimeOffsetNow(); 182b0983db2SEd Tanous asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 183b0983db2SEd Tanous asyncResp->res.jsonValue["DateTimeLocalOffset"] = 184b0983db2SEd Tanous redfishDateTimeOffset.second; 185b0983db2SEd Tanous 186b0983db2SEd Tanous asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( 187b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries", 188b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME); 189b0983db2SEd Tanous } 190b0983db2SEd Tanous 191055713e4SEd Tanous struct JournalReadState 192055713e4SEd Tanous { 193055713e4SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal; 194055713e4SEd Tanous }; 195055713e4SEd Tanous 196bd79bce8SPatrick Williams inline void readJournalEntries( 197bd79bce8SPatrick Williams uint64_t topEntryCount, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 198055713e4SEd Tanous JournalReadState&& readState) 199055713e4SEd Tanous { 200055713e4SEd Tanous nlohmann::json& logEntry = asyncResp->res.jsonValue["Members"]; 201055713e4SEd Tanous nlohmann::json::array_t* logEntryArray = 202055713e4SEd Tanous logEntry.get_ptr<nlohmann::json::array_t*>(); 203055713e4SEd Tanous if (logEntryArray == nullptr) 204055713e4SEd Tanous { 205055713e4SEd Tanous messages::internalError(asyncResp->res); 206055713e4SEd Tanous return; 207055713e4SEd Tanous } 208055713e4SEd Tanous 209055713e4SEd Tanous // The Journal APIs unfortunately do blocking calls to the filesystem, and 210055713e4SEd Tanous // can be somewhat expensive. Short of creating our own io_uring based 211055713e4SEd Tanous // implementation of sd-journal, which would be difficult, the best thing we 212055713e4SEd Tanous // can do is to only parse a certain number of entries at a time. The 213055713e4SEd Tanous // current chunk size is selected arbitrarily to ensure that we're not 214055713e4SEd Tanous // trying to process thousands of entries at the same time. 215055713e4SEd Tanous // The implementation will process the number of entries, then return 216055713e4SEd Tanous // control to the io_context to let other operations continue. 217055713e4SEd Tanous size_t segmentCountRemaining = 10; 218055713e4SEd Tanous 219055713e4SEd Tanous // Reset the unique ID on the first entry 220055713e4SEd Tanous for (uint64_t entryCount = logEntryArray->size(); 221055713e4SEd Tanous entryCount < topEntryCount; entryCount++) 222055713e4SEd Tanous { 223055713e4SEd Tanous if (segmentCountRemaining == 0) 224055713e4SEd Tanous { 225055713e4SEd Tanous boost::asio::post(crow::connections::systemBus->get_io_context(), 226055713e4SEd Tanous [asyncResp, topEntryCount, 227055713e4SEd Tanous readState = std::move(readState)]() mutable { 228055713e4SEd Tanous readJournalEntries(topEntryCount, asyncResp, 229055713e4SEd Tanous std::move(readState)); 230055713e4SEd Tanous }); 231055713e4SEd Tanous return; 232055713e4SEd Tanous } 233055713e4SEd Tanous 234055713e4SEd Tanous nlohmann::json::object_t bmcJournalLogEntry; 2358274eb11SEd Tanous if (!fillBMCJournalLogEntryJson(readState.journal.get(), 236055713e4SEd Tanous bmcJournalLogEntry)) 237055713e4SEd Tanous { 238055713e4SEd Tanous messages::internalError(asyncResp->res); 239055713e4SEd Tanous return; 240055713e4SEd Tanous } 241055713e4SEd Tanous logEntryArray->emplace_back(std::move(bmcJournalLogEntry)); 242055713e4SEd Tanous 2438274eb11SEd Tanous int ret = sd_journal_next(readState.journal.get()); 244055713e4SEd Tanous if (ret < 0) 245055713e4SEd Tanous { 246055713e4SEd Tanous messages::internalError(asyncResp->res); 247055713e4SEd Tanous return; 248055713e4SEd Tanous } 249055713e4SEd Tanous if (ret == 0) 250055713e4SEd Tanous { 251055713e4SEd Tanous break; 252055713e4SEd Tanous } 253055713e4SEd Tanous segmentCountRemaining--; 254055713e4SEd Tanous } 255055713e4SEd Tanous } 256055713e4SEd Tanous 25784177a2fSEd Tanous inline void handleManagersJournalLogEntryCollectionGet( 25884177a2fSEd Tanous App& app, const crow::Request& req, 259b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26084177a2fSEd Tanous const std::string& managerId) 26184177a2fSEd Tanous { 262b0983db2SEd Tanous query_param::QueryCapabilities capabilities = { 263b0983db2SEd Tanous .canDelegateTop = true, 264b0983db2SEd Tanous .canDelegateSkip = true, 265b0983db2SEd Tanous }; 266b0983db2SEd Tanous query_param::Query delegatedQuery; 26784177a2fSEd Tanous if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 26884177a2fSEd Tanous delegatedQuery, capabilities)) 269b0983db2SEd Tanous { 270b0983db2SEd Tanous return; 271b0983db2SEd Tanous } 272b0983db2SEd Tanous 273b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 274b0983db2SEd Tanous { 275b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId); 276b0983db2SEd Tanous return; 277b0983db2SEd Tanous } 278b0983db2SEd Tanous 279b0983db2SEd Tanous size_t skip = delegatedQuery.skip.value_or(0); 280b0983db2SEd Tanous size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 281b0983db2SEd Tanous 282b0983db2SEd Tanous // Collections don't include the static data added by SubRoute 283b0983db2SEd Tanous // because it has a duplicate entry for members 284b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.type"] = 285b0983db2SEd Tanous "#LogEntryCollection.LogEntryCollection"; 286b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 287b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries", 288b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME); 289b0983db2SEd Tanous asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 290b0983db2SEd Tanous asyncResp->res.jsonValue["Description"] = 291b0983db2SEd Tanous "Collection of BMC Journal Entries"; 292055713e4SEd Tanous asyncResp->res.jsonValue["Members"] = nlohmann::json::array_t(); 293b0983db2SEd Tanous 294b0983db2SEd Tanous // Go through the journal and use the timestamp to create a 295b0983db2SEd Tanous // unique ID for each entry 296b0983db2SEd Tanous sd_journal* journalTmp = nullptr; 297b0983db2SEd Tanous int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 298b0983db2SEd Tanous if (ret < 0) 299b0983db2SEd Tanous { 300*7da633f0SEd Tanous BMCWEB_LOG_ERROR("failed to open journal: {}", ret); 301b0983db2SEd Tanous messages::internalError(asyncResp->res); 302b0983db2SEd Tanous return; 303b0983db2SEd Tanous } 304055713e4SEd Tanous 305b0983db2SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 306b0983db2SEd Tanous journalTmp, sd_journal_close); 307b0983db2SEd Tanous journalTmp = nullptr; 308b0983db2SEd Tanous 309055713e4SEd Tanous // Seek to the end 310055713e4SEd Tanous if (sd_journal_seek_tail(journal.get()) < 0) 311b0983db2SEd Tanous { 312b0983db2SEd Tanous messages::internalError(asyncResp->res); 313b0983db2SEd Tanous return; 314b0983db2SEd Tanous } 315055713e4SEd Tanous 316055713e4SEd Tanous // Get the last entry 317055713e4SEd Tanous if (sd_journal_previous(journal.get()) < 0) 318055713e4SEd Tanous { 319055713e4SEd Tanous messages::internalError(asyncResp->res); 320055713e4SEd Tanous return; 321b0983db2SEd Tanous } 322055713e4SEd Tanous 323055713e4SEd Tanous // Get the last sequence number 324055713e4SEd Tanous uint64_t endSeqNum = 0; 325058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254 326055713e4SEd Tanous { 327055713e4SEd Tanous if (sd_journal_get_seqnum(journal.get(), &endSeqNum, nullptr) < 0) 328055713e4SEd Tanous { 329055713e4SEd Tanous messages::internalError(asyncResp->res); 330055713e4SEd Tanous return; 331055713e4SEd Tanous } 332055713e4SEd Tanous } 333055713e4SEd Tanous #endif 334055713e4SEd Tanous 335055713e4SEd Tanous // Seek to the beginning 336055713e4SEd Tanous if (sd_journal_seek_head(journal.get()) < 0) 337055713e4SEd Tanous { 338055713e4SEd Tanous messages::internalError(asyncResp->res); 339055713e4SEd Tanous return; 340055713e4SEd Tanous } 341055713e4SEd Tanous 342055713e4SEd Tanous // Get the first entry 343055713e4SEd Tanous if (sd_journal_next(journal.get()) < 0) 344055713e4SEd Tanous { 345055713e4SEd Tanous messages::internalError(asyncResp->res); 346055713e4SEd Tanous return; 347055713e4SEd Tanous } 348055713e4SEd Tanous 349055713e4SEd Tanous // Get the first sequence number 350055713e4SEd Tanous uint64_t startSeqNum = 0; 351058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254 352055713e4SEd Tanous { 353055713e4SEd Tanous if (sd_journal_get_seqnum(journal.get(), &startSeqNum, nullptr) < 0) 354055713e4SEd Tanous { 355055713e4SEd Tanous messages::internalError(asyncResp->res); 356055713e4SEd Tanous return; 357055713e4SEd Tanous } 358055713e4SEd Tanous } 359055713e4SEd Tanous #endif 360055713e4SEd Tanous 361055713e4SEd Tanous BMCWEB_LOG_DEBUG("journal Sequence IDs start:{} end:{}", startSeqNum, 362055713e4SEd Tanous endSeqNum); 363055713e4SEd Tanous 364055713e4SEd Tanous // Add 1 to account for the last entry 365055713e4SEd Tanous uint64_t totalEntries = endSeqNum - startSeqNum + 1; 366055713e4SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = totalEntries; 367055713e4SEd Tanous if (skip + top < totalEntries) 368b0983db2SEd Tanous { 36984177a2fSEd Tanous asyncResp->res.jsonValue["Members@odata.nextLink"] = 37084177a2fSEd Tanous boost::urls::format( 371b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries?$skip={}", 372b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, std::to_string(skip + top)); 373b0983db2SEd Tanous } 374055713e4SEd Tanous uint64_t index = 0; 375055713e4SEd Tanous if (skip > 0) 376055713e4SEd Tanous { 377055713e4SEd Tanous if (sd_journal_next_skip(journal.get(), skip) < 0) 378055713e4SEd Tanous { 379055713e4SEd Tanous messages::internalError(asyncResp->res); 380055713e4SEd Tanous return; 381055713e4SEd Tanous } 382055713e4SEd Tanous } 383055713e4SEd Tanous BMCWEB_LOG_DEBUG("Index was {}", index); 3848274eb11SEd Tanous readJournalEntries(top, asyncResp, {std::move(journal)}); 385b0983db2SEd Tanous } 386b0983db2SEd Tanous 38784177a2fSEd Tanous inline void handleManagersJournalEntriesLogEntryGet( 38884177a2fSEd Tanous App& app, const crow::Request& req, 389b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 39084177a2fSEd Tanous const std::string& managerId, const std::string& entryID) 39184177a2fSEd Tanous { 392b0983db2SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 393b0983db2SEd Tanous { 394b0983db2SEd Tanous return; 395b0983db2SEd Tanous } 396b0983db2SEd Tanous 397b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 398b0983db2SEd Tanous { 399b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId); 400b0983db2SEd Tanous return; 401b0983db2SEd Tanous } 402b0983db2SEd Tanous 403b0983db2SEd Tanous sd_journal* journalTmp = nullptr; 404b0983db2SEd Tanous int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 405b0983db2SEd Tanous if (ret < 0) 406b0983db2SEd Tanous { 407*7da633f0SEd Tanous BMCWEB_LOG_ERROR("failed to open journal: {}", ret); 408b0983db2SEd Tanous messages::internalError(asyncResp->res); 409b0983db2SEd Tanous return; 410b0983db2SEd Tanous } 411b0983db2SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 412b0983db2SEd Tanous journalTmp, sd_journal_close); 413b0983db2SEd Tanous journalTmp = nullptr; 4148274eb11SEd Tanous 4158274eb11SEd Tanous std::string cursor; 4168274eb11SEd Tanous if (!crow::utility::base64Decode(entryID, cursor)) 4178274eb11SEd Tanous { 4188274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 4198274eb11SEd Tanous return; 4208274eb11SEd Tanous } 4218274eb11SEd Tanous 4228274eb11SEd Tanous // Go to the cursor in the log 4238274eb11SEd Tanous ret = sd_journal_seek_cursor(journal.get(), cursor.c_str()); 424b0983db2SEd Tanous if (ret < 0) 425b0983db2SEd Tanous { 4268274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 4278274eb11SEd Tanous return; 4288274eb11SEd Tanous } 4298274eb11SEd Tanous 4308274eb11SEd Tanous if (sd_journal_next(journal.get()) < 0) 4318274eb11SEd Tanous { 432b0983db2SEd Tanous messages::internalError(asyncResp->res); 433b0983db2SEd Tanous return; 434b0983db2SEd Tanous } 435055713e4SEd Tanous 4368274eb11SEd Tanous ret = sd_journal_test_cursor(journal.get(), cursor.c_str()); 4378274eb11SEd Tanous if (ret == 0) 4388274eb11SEd Tanous { 4398274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 4408274eb11SEd Tanous return; 4418274eb11SEd Tanous } 4428274eb11SEd Tanous if (ret < 0) 443b0983db2SEd Tanous { 444b0983db2SEd Tanous messages::internalError(asyncResp->res); 445b0983db2SEd Tanous return; 446b0983db2SEd Tanous } 447055713e4SEd Tanous 448b0983db2SEd Tanous nlohmann::json::object_t bmcJournalLogEntry; 4498274eb11SEd Tanous if (!fillBMCJournalLogEntryJson(journal.get(), bmcJournalLogEntry)) 450b0983db2SEd Tanous { 451b0983db2SEd Tanous messages::internalError(asyncResp->res); 452b0983db2SEd Tanous return; 453b0983db2SEd Tanous } 454b0983db2SEd Tanous asyncResp->res.jsonValue.update(bmcJournalLogEntry); 455055713e4SEd Tanous } 45684177a2fSEd Tanous 45784177a2fSEd Tanous inline void requestRoutesBMCJournalLogService(App& app) 45884177a2fSEd Tanous { 45984177a2fSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/") 46084177a2fSEd Tanous .privileges(redfish::privileges::getLogService) 46184177a2fSEd Tanous .methods(boost::beast::http::verb::get)( 46284177a2fSEd Tanous std::bind_front(handleManagersLogServiceJournalGet, std::ref(app))); 46384177a2fSEd Tanous 46484177a2fSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/") 46584177a2fSEd Tanous .privileges(redfish::privileges::getLogEntryCollection) 46684177a2fSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 46784177a2fSEd Tanous handleManagersJournalLogEntryCollectionGet, std::ref(app))); 46884177a2fSEd Tanous 46984177a2fSEd Tanous BMCWEB_ROUTE( 47084177a2fSEd Tanous app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/<str>/") 47184177a2fSEd Tanous .privileges(redfish::privileges::getLogEntry) 47284177a2fSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 47384177a2fSEd Tanous handleManagersJournalEntriesLogEntryGet, std::ref(app))); 474b0983db2SEd Tanous } 475b0983db2SEd Tanous } // namespace redfish 476