17681b8a1SOliver Brewka // SPDX-License-Identifier: Apache-2.0 27681b8a1SOliver Brewka // SPDX-FileCopyrightText: Copyright OpenBMC Authors 37681b8a1SOliver Brewka // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 47681b8a1SOliver Brewka #pragma once 57681b8a1SOliver Brewka 67681b8a1SOliver Brewka #include "async_resp.hpp" 77681b8a1SOliver Brewka #include "dbus_utility.hpp" 87681b8a1SOliver Brewka #include "error_messages.hpp" 99d6459e8SOliver Brewka #include "generated/enums/log_service.hpp" 107681b8a1SOliver Brewka #include "http_response.hpp" 117681b8a1SOliver Brewka #include "logging.hpp" 127681b8a1SOliver Brewka #include "registries.hpp" 137681b8a1SOliver Brewka #include "str_utility.hpp" 149d6459e8SOliver Brewka #include "utils/etag_utils.hpp" 157681b8a1SOliver Brewka #include "utils/query_param.hpp" 169d6459e8SOliver Brewka #include "utils/time_utils.hpp" 177681b8a1SOliver Brewka 187681b8a1SOliver Brewka #include <boost/beast/http/field.hpp> 197681b8a1SOliver Brewka #include <boost/beast/http/status.hpp> 207681b8a1SOliver Brewka #include <boost/beast/http/verb.hpp> 217681b8a1SOliver Brewka #include <boost/system/linux_error.hpp> 227681b8a1SOliver Brewka #include <boost/url/format.hpp> 237681b8a1SOliver Brewka #include <boost/url/url.hpp> 247681b8a1SOliver Brewka #include <sdbusplus/message.hpp> 257681b8a1SOliver Brewka #include <sdbusplus/message/native_types.hpp> 267681b8a1SOliver Brewka #include <sdbusplus/unpack_properties.hpp> 277681b8a1SOliver Brewka 287681b8a1SOliver Brewka #include <algorithm> 297681b8a1SOliver Brewka #include <cstddef> 307681b8a1SOliver Brewka #include <cstdint> 317681b8a1SOliver Brewka #include <cstdio> 327681b8a1SOliver Brewka #include <ctime> 337681b8a1SOliver Brewka #include <fstream> 347681b8a1SOliver Brewka #include <iomanip> 357681b8a1SOliver Brewka #include <sstream> 367681b8a1SOliver Brewka #include <string> 377681b8a1SOliver Brewka #include <utility> 387681b8a1SOliver Brewka 397681b8a1SOliver Brewka namespace redfish 407681b8a1SOliver Brewka { 417681b8a1SOliver Brewka namespace eventlog_utils 427681b8a1SOliver Brewka { 437681b8a1SOliver Brewka 443af76e15SOliver Brewka constexpr const char* rfSystemsStr = "Systems"; 453af76e15SOliver Brewka constexpr const char* rfManagersStr = "Managers"; 463af76e15SOliver Brewka 473af76e15SOliver Brewka enum class LogServiceParent 483af76e15SOliver Brewka { 493af76e15SOliver Brewka Systems, 503af76e15SOliver Brewka Managers 513af76e15SOliver Brewka }; 523af76e15SOliver Brewka 533af76e15SOliver Brewka inline std::string logServiceParentToString(LogServiceParent parent) 543af76e15SOliver Brewka { 553af76e15SOliver Brewka std::string parentStr; 563af76e15SOliver Brewka switch (parent) 573af76e15SOliver Brewka { 583af76e15SOliver Brewka case LogServiceParent::Managers: 593af76e15SOliver Brewka parentStr = rfManagersStr; 603af76e15SOliver Brewka break; 613af76e15SOliver Brewka case LogServiceParent::Systems: 623af76e15SOliver Brewka parentStr = rfSystemsStr; 633af76e15SOliver Brewka break; 643af76e15SOliver Brewka default: 653af76e15SOliver Brewka BMCWEB_LOG_ERROR("Unable to stringify bmcweb eventlog location"); 663af76e15SOliver Brewka break; 673af76e15SOliver Brewka } 683af76e15SOliver Brewka return parentStr; 693af76e15SOliver Brewka } 703af76e15SOliver Brewka 713af76e15SOliver Brewka inline std::string_view getChildIdFromParent(LogServiceParent parent) 723af76e15SOliver Brewka { 733af76e15SOliver Brewka std::string_view childId; 743af76e15SOliver Brewka 753af76e15SOliver Brewka switch (parent) 763af76e15SOliver Brewka { 773af76e15SOliver Brewka case LogServiceParent::Managers: 783af76e15SOliver Brewka childId = BMCWEB_REDFISH_MANAGER_URI_NAME; 793af76e15SOliver Brewka break; 803af76e15SOliver Brewka case LogServiceParent::Systems: 813af76e15SOliver Brewka childId = BMCWEB_REDFISH_SYSTEM_URI_NAME; 823af76e15SOliver Brewka break; 833af76e15SOliver Brewka default: 843af76e15SOliver Brewka BMCWEB_LOG_ERROR( 853af76e15SOliver Brewka "Unable to stringify bmcweb eventlog location childId"); 863af76e15SOliver Brewka break; 873af76e15SOliver Brewka } 883af76e15SOliver Brewka return childId; 893af76e15SOliver Brewka } 903af76e15SOliver Brewka 913af76e15SOliver Brewka inline std::string getLogEntryDescriptor(LogServiceParent parent) 923af76e15SOliver Brewka { 933af76e15SOliver Brewka std::string descriptor; 943af76e15SOliver Brewka switch (parent) 953af76e15SOliver Brewka { 963af76e15SOliver Brewka case LogServiceParent::Managers: 973af76e15SOliver Brewka descriptor = "Manager"; 983af76e15SOliver Brewka break; 993af76e15SOliver Brewka case LogServiceParent::Systems: 1003af76e15SOliver Brewka descriptor = "System"; 1013af76e15SOliver Brewka break; 1023af76e15SOliver Brewka default: 1033af76e15SOliver Brewka BMCWEB_LOG_ERROR("Unable to get Log Entry descriptor"); 1043af76e15SOliver Brewka break; 1053af76e15SOliver Brewka } 1063af76e15SOliver Brewka return descriptor; 1073af76e15SOliver Brewka } 1083af76e15SOliver Brewka 1099d6459e8SOliver Brewka inline void handleSystemsAndManagersEventLogServiceGet( 1109d6459e8SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1119d6459e8SOliver Brewka LogServiceParent parent) 1129d6459e8SOliver Brewka { 1139d6459e8SOliver Brewka const std::string parentStr = logServiceParentToString(parent); 1149d6459e8SOliver Brewka const std::string_view childId = getChildIdFromParent(parent); 1159d6459e8SOliver Brewka const std::string logEntryDescriptor = getLogEntryDescriptor(parent); 1169d6459e8SOliver Brewka 1179d6459e8SOliver Brewka if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty()) 1189d6459e8SOliver Brewka { 1199d6459e8SOliver Brewka messages::internalError(asyncResp->res); 1209d6459e8SOliver Brewka return; 1219d6459e8SOliver Brewka } 1229d6459e8SOliver Brewka 1239d6459e8SOliver Brewka asyncResp->res.jsonValue["@odata.id"] = std::format( 1249d6459e8SOliver Brewka "/redfish/v1/{}/{}/LogServices/EventLog", parentStr, childId); 1259d6459e8SOliver Brewka asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 1269d6459e8SOliver Brewka asyncResp->res.jsonValue["Name"] = "Event Log Service"; 1279d6459e8SOliver Brewka asyncResp->res.jsonValue["Description"] = 1289d6459e8SOliver Brewka std::format("{} Event Log Service", logEntryDescriptor); 1299d6459e8SOliver Brewka asyncResp->res.jsonValue["Id"] = "EventLog"; 1309d6459e8SOliver Brewka asyncResp->res.jsonValue["OverWritePolicy"] = 1319d6459e8SOliver Brewka log_service::OverWritePolicy::WrapsWhenFull; 1329d6459e8SOliver Brewka 1339d6459e8SOliver Brewka std::pair<std::string, std::string> redfishDateTimeOffset = 1349d6459e8SOliver Brewka redfish::time_utils::getDateTimeOffsetNow(); 1359d6459e8SOliver Brewka 1369d6459e8SOliver Brewka asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1379d6459e8SOliver Brewka asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1389d6459e8SOliver Brewka redfishDateTimeOffset.second; 1399d6459e8SOliver Brewka 1409d6459e8SOliver Brewka asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 1419d6459e8SOliver Brewka "/redfish/v1/{}/{}/LogServices/EventLog/Entries", parentStr, childId); 1429d6459e8SOliver Brewka asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] 1439d6459e8SOliver Brewka 1449d6459e8SOliver Brewka = std::format( 1459d6459e8SOliver Brewka "/redfish/v1/{}/{}/LogServices/EventLog/Actions/LogService.ClearLog", 1469d6459e8SOliver Brewka parentStr, childId); 1479d6459e8SOliver Brewka etag_utils::setEtagOmitDateTimeHandler(asyncResp); 1489d6459e8SOliver Brewka } 1499d6459e8SOliver Brewka 1507681b8a1SOliver Brewka /* 1517681b8a1SOliver Brewka * Journal EventLog utilities 1527681b8a1SOliver Brewka * */ 1537681b8a1SOliver Brewka 1547681b8a1SOliver Brewka inline bool getRedfishLogFiles( 1557681b8a1SOliver Brewka std::vector<std::filesystem::path>& redfishLogFiles) 1567681b8a1SOliver Brewka { 1577681b8a1SOliver Brewka static const std::filesystem::path redfishLogDir = "/var/log"; 1587681b8a1SOliver Brewka static const std::string redfishLogFilename = "redfish"; 1597681b8a1SOliver Brewka 1607681b8a1SOliver Brewka // Loop through the directory looking for redfish log files 1617681b8a1SOliver Brewka for (const std::filesystem::directory_entry& dirEnt : 1627681b8a1SOliver Brewka std::filesystem::directory_iterator(redfishLogDir)) 1637681b8a1SOliver Brewka { 1647681b8a1SOliver Brewka // If we find a redfish log file, save the path 1657681b8a1SOliver Brewka std::string filename = dirEnt.path().filename(); 1667681b8a1SOliver Brewka if (filename.starts_with(redfishLogFilename)) 1677681b8a1SOliver Brewka { 1687681b8a1SOliver Brewka redfishLogFiles.emplace_back(redfishLogDir / filename); 1697681b8a1SOliver Brewka } 1707681b8a1SOliver Brewka } 1717681b8a1SOliver Brewka // As the log files rotate, they are appended with a ".#" that is higher for 1727681b8a1SOliver Brewka // the older logs. Since we don't expect more than 10 log files, we 1737681b8a1SOliver Brewka // can just sort the list to get them in order from newest to oldest 1747681b8a1SOliver Brewka std::ranges::sort(redfishLogFiles); 1757681b8a1SOliver Brewka 1767681b8a1SOliver Brewka return !redfishLogFiles.empty(); 1777681b8a1SOliver Brewka } 1787681b8a1SOliver Brewka 1797681b8a1SOliver Brewka inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, 1807681b8a1SOliver Brewka const bool firstEntry = true) 1817681b8a1SOliver Brewka { 1827681b8a1SOliver Brewka static time_t prevTs = 0; 1837681b8a1SOliver Brewka static int index = 0; 1847681b8a1SOliver Brewka if (firstEntry) 1857681b8a1SOliver Brewka { 1867681b8a1SOliver Brewka prevTs = 0; 1877681b8a1SOliver Brewka } 1887681b8a1SOliver Brewka 1897681b8a1SOliver Brewka // Get the entry timestamp 1907681b8a1SOliver Brewka std::time_t curTs = 0; 1917681b8a1SOliver Brewka std::tm timeStruct = {}; 1927681b8a1SOliver Brewka std::istringstream entryStream(logEntry); 1937681b8a1SOliver Brewka if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 1947681b8a1SOliver Brewka { 1957681b8a1SOliver Brewka curTs = std::mktime(&timeStruct); 1967681b8a1SOliver Brewka } 1977681b8a1SOliver Brewka // If the timestamp isn't unique, increment the index 1987681b8a1SOliver Brewka if (curTs == prevTs) 1997681b8a1SOliver Brewka { 2007681b8a1SOliver Brewka index++; 2017681b8a1SOliver Brewka } 2027681b8a1SOliver Brewka else 2037681b8a1SOliver Brewka { 2047681b8a1SOliver Brewka // Otherwise, reset it 2057681b8a1SOliver Brewka index = 0; 2067681b8a1SOliver Brewka } 2077681b8a1SOliver Brewka // Save the timestamp 2087681b8a1SOliver Brewka prevTs = curTs; 2097681b8a1SOliver Brewka 2107681b8a1SOliver Brewka entryID = std::to_string(curTs); 2117681b8a1SOliver Brewka if (index > 0) 2127681b8a1SOliver Brewka { 2137681b8a1SOliver Brewka entryID += "_" + std::to_string(index); 2147681b8a1SOliver Brewka } 2157681b8a1SOliver Brewka return true; 2167681b8a1SOliver Brewka } 2177681b8a1SOliver Brewka 2187681b8a1SOliver Brewka enum class LogParseError 2197681b8a1SOliver Brewka { 2207681b8a1SOliver Brewka success, 2217681b8a1SOliver Brewka parseFailed, 2227681b8a1SOliver Brewka messageIdNotInRegistry, 2237681b8a1SOliver Brewka }; 2247681b8a1SOliver Brewka 2257681b8a1SOliver Brewka static LogParseError fillEventLogEntryJson( 2267681b8a1SOliver Brewka const std::string& logEntryID, const std::string& logEntry, 2273af76e15SOliver Brewka nlohmann::json::object_t& logEntryJson, const std::string& parentStr, 2283af76e15SOliver Brewka const std::string_view childId, const std::string& logEntryDescriptor) 2297681b8a1SOliver Brewka { 2307681b8a1SOliver Brewka // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 2317681b8a1SOliver Brewka // First get the Timestamp 2327681b8a1SOliver Brewka size_t space = logEntry.find_first_of(' '); 2337681b8a1SOliver Brewka if (space == std::string::npos) 2347681b8a1SOliver Brewka { 2357681b8a1SOliver Brewka return LogParseError::parseFailed; 2367681b8a1SOliver Brewka } 2377681b8a1SOliver Brewka std::string timestamp = logEntry.substr(0, space); 2387681b8a1SOliver Brewka // Then get the log contents 2397681b8a1SOliver Brewka size_t entryStart = logEntry.find_first_not_of(' ', space); 2407681b8a1SOliver Brewka if (entryStart == std::string::npos) 2417681b8a1SOliver Brewka { 2427681b8a1SOliver Brewka return LogParseError::parseFailed; 2437681b8a1SOliver Brewka } 2447681b8a1SOliver Brewka std::string_view entry(logEntry); 2457681b8a1SOliver Brewka entry.remove_prefix(entryStart); 2467681b8a1SOliver Brewka // Use split to separate the entry into its fields 2477681b8a1SOliver Brewka std::vector<std::string> logEntryFields; 2487681b8a1SOliver Brewka bmcweb::split(logEntryFields, entry, ','); 2497681b8a1SOliver Brewka // We need at least a MessageId to be valid 2507681b8a1SOliver Brewka auto logEntryIter = logEntryFields.begin(); 2517681b8a1SOliver Brewka if (logEntryIter == logEntryFields.end()) 2527681b8a1SOliver Brewka { 2537681b8a1SOliver Brewka return LogParseError::parseFailed; 2547681b8a1SOliver Brewka } 2557681b8a1SOliver Brewka std::string& messageID = *logEntryIter; 2567681b8a1SOliver Brewka // Get the Message from the MessageRegistry 2577681b8a1SOliver Brewka const registries::Message* message = registries::getMessage(messageID); 2587681b8a1SOliver Brewka 2597681b8a1SOliver Brewka logEntryIter++; 2607681b8a1SOliver Brewka if (message == nullptr) 2617681b8a1SOliver Brewka { 2627681b8a1SOliver Brewka BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry); 2637681b8a1SOliver Brewka return LogParseError::messageIdNotInRegistry; 2647681b8a1SOliver Brewka } 2657681b8a1SOliver Brewka 2667681b8a1SOliver Brewka std::vector<std::string_view> messageArgs(logEntryIter, 2677681b8a1SOliver Brewka logEntryFields.end()); 2687681b8a1SOliver Brewka messageArgs.resize(message->numberOfArgs); 2697681b8a1SOliver Brewka 2707681b8a1SOliver Brewka std::string msg = 2717681b8a1SOliver Brewka redfish::registries::fillMessageArgs(messageArgs, message->message); 2727681b8a1SOliver Brewka if (msg.empty()) 2737681b8a1SOliver Brewka { 2747681b8a1SOliver Brewka return LogParseError::parseFailed; 2757681b8a1SOliver Brewka } 2767681b8a1SOliver Brewka 2777681b8a1SOliver Brewka // Get the Created time from the timestamp. The log timestamp is in RFC3339 2789d6459e8SOliver Brewka // format which matches the Redfish format except for the 2799d6459e8SOliver Brewka // fractional seconds between the '.' and the '+', so just remove them. 2807681b8a1SOliver Brewka std::size_t dot = timestamp.find_first_of('.'); 2817681b8a1SOliver Brewka std::size_t plus = timestamp.find_first_of('+'); 2827681b8a1SOliver Brewka if (dot != std::string::npos && plus != std::string::npos) 2837681b8a1SOliver Brewka { 2847681b8a1SOliver Brewka timestamp.erase(dot, plus - dot); 2857681b8a1SOliver Brewka } 2867681b8a1SOliver Brewka 2877681b8a1SOliver Brewka // Fill in the log entry with the gathered data 2887681b8a1SOliver Brewka logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 2893af76e15SOliver Brewka logEntryJson["@odata.id"] = 2903af76e15SOliver Brewka boost::urls::format("/redfish/v1/{}/{}/LogServices/EventLog/Entries/{}", 2913af76e15SOliver Brewka parentStr, childId, logEntryID); 2923af76e15SOliver Brewka logEntryJson["Name"] = 2933af76e15SOliver Brewka std::format("{} Event Log Entry", logEntryDescriptor); 2947681b8a1SOliver Brewka logEntryJson["Id"] = logEntryID; 2957681b8a1SOliver Brewka logEntryJson["Message"] = std::move(msg); 2967681b8a1SOliver Brewka logEntryJson["MessageId"] = std::move(messageID); 2977681b8a1SOliver Brewka logEntryJson["MessageArgs"] = messageArgs; 2987681b8a1SOliver Brewka logEntryJson["EntryType"] = "Event"; 2997681b8a1SOliver Brewka logEntryJson["Severity"] = message->messageSeverity; 3007681b8a1SOliver Brewka logEntryJson["Created"] = std::move(timestamp); 3017681b8a1SOliver Brewka return LogParseError::success; 3027681b8a1SOliver Brewka } 3037681b8a1SOliver Brewka 304*dba9d675SOliver Brewka inline void handleSystemsAndManagersLogServiceEventLogLogEntryCollection( 3057681b8a1SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3063af76e15SOliver Brewka query_param::Query& delegatedQuery, LogServiceParent parent) 3077681b8a1SOliver Brewka { 3087681b8a1SOliver Brewka size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 3097681b8a1SOliver Brewka size_t skip = delegatedQuery.skip.value_or(0); 3107681b8a1SOliver Brewka 3113af76e15SOliver Brewka const std::string parentStr = logServiceParentToString(parent); 3123af76e15SOliver Brewka const std::string_view childId = getChildIdFromParent(parent); 3133af76e15SOliver Brewka const std::string logEntryDescriptor = getLogEntryDescriptor(parent); 3143af76e15SOliver Brewka 3153af76e15SOliver Brewka if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty()) 3163af76e15SOliver Brewka { 3173af76e15SOliver Brewka messages::internalError(asyncResp->res); 3183af76e15SOliver Brewka return; 3193af76e15SOliver Brewka } 3203af76e15SOliver Brewka 3217681b8a1SOliver Brewka // Collections don't include the static data added by SubRoute 3227681b8a1SOliver Brewka // because it has a duplicate entry for members 3237681b8a1SOliver Brewka asyncResp->res.jsonValue["@odata.type"] = 3247681b8a1SOliver Brewka "#LogEntryCollection.LogEntryCollection"; 3253af76e15SOliver Brewka asyncResp->res.jsonValue["@odata.id"] = std::format( 3263af76e15SOliver Brewka "/redfish/v1/{}/{}/LogServices/EventLog/Entries", parentStr, childId); 3273af76e15SOliver Brewka asyncResp->res.jsonValue["Name"] = 3283af76e15SOliver Brewka std::format("{} Event Log Entries", logEntryDescriptor); 3297681b8a1SOliver Brewka asyncResp->res.jsonValue["Description"] = 3303af76e15SOliver Brewka std::format("Collection of {} Event Log Entries", logEntryDescriptor); 3317681b8a1SOliver Brewka 3327681b8a1SOliver Brewka nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 3337681b8a1SOliver Brewka logEntryArray = nlohmann::json::array(); 3347681b8a1SOliver Brewka // Go through the log files and create a unique ID for each 3357681b8a1SOliver Brewka // entry 3367681b8a1SOliver Brewka std::vector<std::filesystem::path> redfishLogFiles; 3377681b8a1SOliver Brewka getRedfishLogFiles(redfishLogFiles); 3387681b8a1SOliver Brewka uint64_t entryCount = 0; 3397681b8a1SOliver Brewka std::string logEntry; 3407681b8a1SOliver Brewka 3417681b8a1SOliver Brewka // Oldest logs are in the last file, so start there and loop 3427681b8a1SOliver Brewka // backwards 3437681b8a1SOliver Brewka for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 3447681b8a1SOliver Brewka { 3457681b8a1SOliver Brewka std::ifstream logStream(*it); 3467681b8a1SOliver Brewka if (!logStream.is_open()) 3477681b8a1SOliver Brewka { 3487681b8a1SOliver Brewka continue; 3497681b8a1SOliver Brewka } 3507681b8a1SOliver Brewka 3517681b8a1SOliver Brewka // Reset the unique ID on the first entry 3527681b8a1SOliver Brewka bool firstEntry = true; 3537681b8a1SOliver Brewka while (std::getline(logStream, logEntry)) 3547681b8a1SOliver Brewka { 3557681b8a1SOliver Brewka std::string idStr; 3567681b8a1SOliver Brewka if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 3577681b8a1SOliver Brewka { 3587681b8a1SOliver Brewka continue; 3597681b8a1SOliver Brewka } 3607681b8a1SOliver Brewka firstEntry = false; 3617681b8a1SOliver Brewka 3627681b8a1SOliver Brewka nlohmann::json::object_t bmcLogEntry; 3637681b8a1SOliver Brewka LogParseError status = 3643af76e15SOliver Brewka fillEventLogEntryJson(idStr, logEntry, bmcLogEntry, parentStr, 3653af76e15SOliver Brewka childId, logEntryDescriptor); 3667681b8a1SOliver Brewka if (status == LogParseError::messageIdNotInRegistry) 3677681b8a1SOliver Brewka { 3687681b8a1SOliver Brewka continue; 3697681b8a1SOliver Brewka } 3707681b8a1SOliver Brewka if (status != LogParseError::success) 3717681b8a1SOliver Brewka { 3727681b8a1SOliver Brewka messages::internalError(asyncResp->res); 3737681b8a1SOliver Brewka return; 3747681b8a1SOliver Brewka } 3757681b8a1SOliver Brewka 3767681b8a1SOliver Brewka entryCount++; 3777681b8a1SOliver Brewka // Handle paging using skip (number of entries to skip from the 3787681b8a1SOliver Brewka // start) and top (number of entries to display) 3797681b8a1SOliver Brewka if (entryCount <= skip || entryCount > skip + top) 3807681b8a1SOliver Brewka { 3817681b8a1SOliver Brewka continue; 3827681b8a1SOliver Brewka } 3837681b8a1SOliver Brewka 3847681b8a1SOliver Brewka logEntryArray.emplace_back(std::move(bmcLogEntry)); 3857681b8a1SOliver Brewka } 3867681b8a1SOliver Brewka } 3877681b8a1SOliver Brewka asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 3887681b8a1SOliver Brewka if (skip + top < entryCount) 3897681b8a1SOliver Brewka { 3907681b8a1SOliver Brewka asyncResp->res.jsonValue["Members@odata.nextLink"] = 3917681b8a1SOliver Brewka boost::urls::format( 3923af76e15SOliver Brewka "/redfish/v1/{}/{}/LogServices/EventLog/Entries?$skip={}", 3933af76e15SOliver Brewka parentStr, childId, std::to_string(skip + top)); 3947681b8a1SOliver Brewka } 3957681b8a1SOliver Brewka } 3967681b8a1SOliver Brewka 397*dba9d675SOliver Brewka inline void handleSystemsAndManagersLogServiceEventLogEntriesGet( 3987681b8a1SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3993af76e15SOliver Brewka const std::string& param, LogServiceParent parent) 4007681b8a1SOliver Brewka { 4017681b8a1SOliver Brewka const std::string& targetID = param; 4027681b8a1SOliver Brewka 4033af76e15SOliver Brewka const std::string parentStr = logServiceParentToString(parent); 4043af76e15SOliver Brewka const std::string_view childId = getChildIdFromParent(parent); 4053af76e15SOliver Brewka const std::string logEntryDescriptor = getLogEntryDescriptor(parent); 4063af76e15SOliver Brewka 4073af76e15SOliver Brewka if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty()) 4083af76e15SOliver Brewka { 4093af76e15SOliver Brewka messages::internalError(asyncResp->res); 4103af76e15SOliver Brewka return; 4113af76e15SOliver Brewka } 4123af76e15SOliver Brewka 4137681b8a1SOliver Brewka // Go through the log files and check the unique ID for each 4147681b8a1SOliver Brewka // entry to find the target entry 4157681b8a1SOliver Brewka std::vector<std::filesystem::path> redfishLogFiles; 4167681b8a1SOliver Brewka getRedfishLogFiles(redfishLogFiles); 4177681b8a1SOliver Brewka std::string logEntry; 4187681b8a1SOliver Brewka 4197681b8a1SOliver Brewka // Oldest logs are in the last file, so start there and loop 4207681b8a1SOliver Brewka // backwards 4217681b8a1SOliver Brewka for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 4227681b8a1SOliver Brewka { 4237681b8a1SOliver Brewka std::ifstream logStream(*it); 4247681b8a1SOliver Brewka if (!logStream.is_open()) 4257681b8a1SOliver Brewka { 4267681b8a1SOliver Brewka continue; 4277681b8a1SOliver Brewka } 4287681b8a1SOliver Brewka 4297681b8a1SOliver Brewka // Reset the unique ID on the first entry 4307681b8a1SOliver Brewka bool firstEntry = true; 4317681b8a1SOliver Brewka while (std::getline(logStream, logEntry)) 4327681b8a1SOliver Brewka { 4337681b8a1SOliver Brewka std::string idStr; 4347681b8a1SOliver Brewka if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 4357681b8a1SOliver Brewka { 4367681b8a1SOliver Brewka continue; 4377681b8a1SOliver Brewka } 4387681b8a1SOliver Brewka firstEntry = false; 4397681b8a1SOliver Brewka 4407681b8a1SOliver Brewka if (idStr == targetID) 4417681b8a1SOliver Brewka { 4427681b8a1SOliver Brewka nlohmann::json::object_t bmcLogEntry; 4433af76e15SOliver Brewka LogParseError status = fillEventLogEntryJson( 4443af76e15SOliver Brewka idStr, logEntry, bmcLogEntry, parentStr, childId, 4453af76e15SOliver Brewka logEntryDescriptor); 4467681b8a1SOliver Brewka if (status != LogParseError::success) 4477681b8a1SOliver Brewka { 4487681b8a1SOliver Brewka messages::internalError(asyncResp->res); 4497681b8a1SOliver Brewka return; 4507681b8a1SOliver Brewka } 4517681b8a1SOliver Brewka asyncResp->res.jsonValue.update(bmcLogEntry); 4527681b8a1SOliver Brewka return; 4537681b8a1SOliver Brewka } 4547681b8a1SOliver Brewka } 4557681b8a1SOliver Brewka } 4567681b8a1SOliver Brewka // Requested ID was not found 4577681b8a1SOliver Brewka messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); 4587681b8a1SOliver Brewka } 4597681b8a1SOliver Brewka 460*dba9d675SOliver Brewka inline void handleSystemsAndManagersLogServicesEventLogActionsClearPost( 4617681b8a1SOliver Brewka const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 4627681b8a1SOliver Brewka { 4637681b8a1SOliver Brewka // Clear the EventLog by deleting the log files 4647681b8a1SOliver Brewka std::vector<std::filesystem::path> redfishLogFiles; 4657681b8a1SOliver Brewka if (getRedfishLogFiles(redfishLogFiles)) 4667681b8a1SOliver Brewka { 4677681b8a1SOliver Brewka for (const std::filesystem::path& file : redfishLogFiles) 4687681b8a1SOliver Brewka { 4697681b8a1SOliver Brewka std::error_code ec; 4707681b8a1SOliver Brewka std::filesystem::remove(file, ec); 4717681b8a1SOliver Brewka } 4727681b8a1SOliver Brewka } 4737681b8a1SOliver Brewka 4747681b8a1SOliver Brewka // Reload rsyslog so it knows to start new log files 4757681b8a1SOliver Brewka dbus::utility::async_method_call( 4767681b8a1SOliver Brewka asyncResp, 4777681b8a1SOliver Brewka [asyncResp](const boost::system::error_code& ec) { 4787681b8a1SOliver Brewka if (ec) 4797681b8a1SOliver Brewka { 4807681b8a1SOliver Brewka BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec); 4817681b8a1SOliver Brewka messages::internalError(asyncResp->res); 4827681b8a1SOliver Brewka return; 4837681b8a1SOliver Brewka } 4847681b8a1SOliver Brewka 4857681b8a1SOliver Brewka messages::success(asyncResp->res); 4867681b8a1SOliver Brewka }, 4877681b8a1SOliver Brewka "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 4887681b8a1SOliver Brewka "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", 4897681b8a1SOliver Brewka "replace"); 4907681b8a1SOliver Brewka } 4917681b8a1SOliver Brewka } // namespace eventlog_utils 4927681b8a1SOliver Brewka } // namespace redfish 493